diff options
author | Alexey Starikovskiy <astarikovskiy@suse.de> | 2009-12-24 03:34:16 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-12-24 14:52:38 -0500 |
commit | a62e8f1978f49e52f87a711ff6711b323d4b12ff (patch) | |
tree | 7d88562acb89e12cff4c2fce18a83495428ed0a3 /drivers/acpi/ec.c | |
parent | fcb11235d3910c39afece52f6e106a9ca565d34b (diff) |
ACPI: EC: Accelerate query execution
Split EC query handling into acknowledge and execution phase.
This allows much smaller pending query lattency and lowers chances
of EC going "wild" and losing events.
Reference: http://bugzilla.kernel.org/show_bug.cgi?id=14858
Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r-- | drivers/acpi/ec.c | 122 |
1 files changed, 77 insertions, 45 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fd1801bdee66..9cc38857c33b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -201,14 +201,13 @@ unlock: | |||
201 | spin_unlock_irqrestore(&ec->curr_lock, flags); | 201 | spin_unlock_irqrestore(&ec->curr_lock, flags); |
202 | } | 202 | } |
203 | 203 | ||
204 | static void acpi_ec_gpe_query(void *ec_cxt); | 204 | static int acpi_ec_sync_query(struct acpi_ec *ec); |
205 | 205 | ||
206 | static int ec_check_sci(struct acpi_ec *ec, u8 state) | 206 | static int ec_check_sci_sync(struct acpi_ec *ec, u8 state) |
207 | { | 207 | { |
208 | if (state & ACPI_EC_FLAG_SCI) { | 208 | if (state & ACPI_EC_FLAG_SCI) { |
209 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) | 209 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) |
210 | return acpi_os_execute(OSL_EC_BURST_HANDLER, | 210 | return acpi_ec_sync_query(ec); |
211 | acpi_ec_gpe_query, ec); | ||
212 | } | 211 | } |
213 | return 0; | 212 | return 0; |
214 | } | 213 | } |
@@ -249,11 +248,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
249 | { | 248 | { |
250 | unsigned long tmp; | 249 | unsigned long tmp; |
251 | int ret = 0; | 250 | int ret = 0; |
252 | pr_debug(PREFIX "transaction start\n"); | ||
253 | /* disable GPE during transaction if storm is detected */ | ||
254 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
255 | acpi_disable_gpe(NULL, ec->gpe); | ||
256 | } | ||
257 | if (EC_FLAGS_MSI) | 251 | if (EC_FLAGS_MSI) |
258 | udelay(ACPI_EC_MSI_UDELAY); | 252 | udelay(ACPI_EC_MSI_UDELAY); |
259 | /* start transaction */ | 253 | /* start transaction */ |
@@ -269,16 +263,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
269 | spin_lock_irqsave(&ec->curr_lock, tmp); | 263 | spin_lock_irqsave(&ec->curr_lock, tmp); |
270 | ec->curr = NULL; | 264 | ec->curr = NULL; |
271 | spin_unlock_irqrestore(&ec->curr_lock, tmp); | 265 | spin_unlock_irqrestore(&ec->curr_lock, tmp); |
272 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
273 | /* check if we received SCI during transaction */ | ||
274 | ec_check_sci(ec, acpi_ec_read_status(ec)); | ||
275 | /* it is safe to enable GPE outside of transaction */ | ||
276 | acpi_enable_gpe(NULL, ec->gpe); | ||
277 | } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { | ||
278 | pr_info(PREFIX "GPE storm detected, " | ||
279 | "transactions will use polling mode\n"); | ||
280 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); | ||
281 | } | ||
282 | return ret; | 266 | return ret; |
283 | } | 267 | } |
284 | 268 | ||
@@ -321,7 +305,24 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
321 | status = -ETIME; | 305 | status = -ETIME; |
322 | goto end; | 306 | goto end; |
323 | } | 307 | } |
308 | pr_debug(PREFIX "transaction start\n"); | ||
309 | /* disable GPE during transaction if storm is detected */ | ||
310 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
311 | acpi_disable_gpe(NULL, ec->gpe); | ||
312 | } | ||
313 | |||
324 | status = acpi_ec_transaction_unlocked(ec, t); | 314 | status = acpi_ec_transaction_unlocked(ec, t); |
315 | |||
316 | /* check if we received SCI during transaction */ | ||
317 | ec_check_sci_sync(ec, acpi_ec_read_status(ec)); | ||
318 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
319 | /* it is safe to enable GPE outside of transaction */ | ||
320 | acpi_enable_gpe(NULL, ec->gpe); | ||
321 | } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { | ||
322 | pr_info(PREFIX "GPE storm detected, " | ||
323 | "transactions will use polling mode\n"); | ||
324 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); | ||
325 | } | ||
325 | end: | 326 | end: |
326 | if (ec->global_lock) | 327 | if (ec->global_lock) |
327 | acpi_release_global_lock(glk); | 328 | acpi_release_global_lock(glk); |
@@ -443,7 +444,7 @@ int ec_transaction(u8 command, | |||
443 | 444 | ||
444 | EXPORT_SYMBOL(ec_transaction); | 445 | EXPORT_SYMBOL(ec_transaction); |
445 | 446 | ||
446 | static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | 447 | static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data) |
447 | { | 448 | { |
448 | int result; | 449 | int result; |
449 | u8 d; | 450 | u8 d; |
@@ -452,20 +453,16 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
452 | .wlen = 0, .rlen = 1}; | 453 | .wlen = 0, .rlen = 1}; |
453 | if (!ec || !data) | 454 | if (!ec || !data) |
454 | return -EINVAL; | 455 | return -EINVAL; |
455 | |||
456 | /* | 456 | /* |
457 | * Query the EC to find out which _Qxx method we need to evaluate. | 457 | * Query the EC to find out which _Qxx method we need to evaluate. |
458 | * Note that successful completion of the query causes the ACPI_EC_SCI | 458 | * Note that successful completion of the query causes the ACPI_EC_SCI |
459 | * bit to be cleared (and thus clearing the interrupt source). | 459 | * bit to be cleared (and thus clearing the interrupt source). |
460 | */ | 460 | */ |
461 | 461 | result = acpi_ec_transaction_unlocked(ec, &t); | |
462 | result = acpi_ec_transaction(ec, &t); | ||
463 | if (result) | 462 | if (result) |
464 | return result; | 463 | return result; |
465 | |||
466 | if (!d) | 464 | if (!d) |
467 | return -ENODATA; | 465 | return -ENODATA; |
468 | |||
469 | *data = d; | 466 | *data = d; |
470 | return 0; | 467 | return 0; |
471 | } | 468 | } |
@@ -509,43 +506,78 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | |||
509 | 506 | ||
510 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); | 507 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); |
511 | 508 | ||
512 | static void acpi_ec_gpe_query(void *ec_cxt) | 509 | static void acpi_ec_run(void *cxt) |
513 | { | 510 | { |
514 | struct acpi_ec *ec = ec_cxt; | 511 | struct acpi_ec_query_handler *handler = cxt; |
515 | u8 value = 0; | 512 | if (!handler) |
516 | struct acpi_ec_query_handler *handler, copy; | ||
517 | |||
518 | if (!ec || acpi_ec_query(ec, &value)) | ||
519 | return; | 513 | return; |
520 | mutex_lock(&ec->lock); | 514 | pr_debug(PREFIX "start query execution\n"); |
515 | if (handler->func) | ||
516 | handler->func(handler->data); | ||
517 | else if (handler->handle) | ||
518 | acpi_evaluate_object(handler->handle, NULL, NULL, NULL); | ||
519 | pr_debug(PREFIX "stop query execution\n"); | ||
520 | kfree(handler); | ||
521 | } | ||
522 | |||
523 | static int acpi_ec_sync_query(struct acpi_ec *ec) | ||
524 | { | ||
525 | u8 value = 0; | ||
526 | int status; | ||
527 | struct acpi_ec_query_handler *handler, *copy; | ||
528 | if ((status = acpi_ec_query_unlocked(ec, &value))) | ||
529 | return status; | ||
521 | list_for_each_entry(handler, &ec->list, node) { | 530 | list_for_each_entry(handler, &ec->list, node) { |
522 | if (value == handler->query_bit) { | 531 | if (value == handler->query_bit) { |
523 | /* have custom handler for this bit */ | 532 | /* have custom handler for this bit */ |
524 | memcpy(©, handler, sizeof(copy)); | 533 | copy = kmalloc(sizeof(*handler), GFP_KERNEL); |
525 | mutex_unlock(&ec->lock); | 534 | if (!copy) |
526 | if (copy.func) { | 535 | return -ENOMEM; |
527 | copy.func(copy.data); | 536 | memcpy(copy, handler, sizeof(*copy)); |
528 | } else if (copy.handle) { | 537 | pr_debug(PREFIX "push query execution (0x%2x) on queue\n", value); |
529 | acpi_evaluate_object(copy.handle, NULL, NULL, NULL); | 538 | return acpi_os_execute(OSL_GPE_HANDLER, |
530 | } | 539 | acpi_ec_run, copy); |
531 | return; | ||
532 | } | 540 | } |
533 | } | 541 | } |
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static void acpi_ec_gpe_query(void *ec_cxt) | ||
546 | { | ||
547 | struct acpi_ec *ec = ec_cxt; | ||
548 | if (!ec) | ||
549 | return; | ||
550 | mutex_lock(&ec->lock); | ||
551 | acpi_ec_sync_query(ec); | ||
534 | mutex_unlock(&ec->lock); | 552 | mutex_unlock(&ec->lock); |
535 | } | 553 | } |
536 | 554 | ||
555 | static void acpi_ec_gpe_query(void *ec_cxt); | ||
556 | |||
557 | static int ec_check_sci(struct acpi_ec *ec, u8 state) | ||
558 | { | ||
559 | if (state & ACPI_EC_FLAG_SCI) { | ||
560 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { | ||
561 | pr_debug(PREFIX "push gpe query to the queue\n"); | ||
562 | return acpi_os_execute(OSL_NOTIFY_HANDLER, | ||
563 | acpi_ec_gpe_query, ec); | ||
564 | } | ||
565 | } | ||
566 | return 0; | ||
567 | } | ||
568 | |||
537 | static u32 acpi_ec_gpe_handler(void *data) | 569 | static u32 acpi_ec_gpe_handler(void *data) |
538 | { | 570 | { |
539 | struct acpi_ec *ec = data; | 571 | struct acpi_ec *ec = data; |
540 | u8 status; | ||
541 | 572 | ||
542 | pr_debug(PREFIX "~~~> interrupt\n"); | 573 | pr_debug(PREFIX "~~~> interrupt\n"); |
543 | status = acpi_ec_read_status(ec); | ||
544 | 574 | ||
545 | advance_transaction(ec, status); | 575 | advance_transaction(ec, acpi_ec_read_status(ec)); |
546 | if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) | 576 | if (ec_transaction_done(ec) && |
577 | (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { | ||
547 | wake_up(&ec->wait); | 578 | wake_up(&ec->wait); |
548 | ec_check_sci(ec, status); | 579 | ec_check_sci(ec, acpi_ec_read_status(ec)); |
580 | } | ||
549 | return ACPI_INTERRUPT_HANDLED; | 581 | return ACPI_INTERRUPT_HANDLED; |
550 | } | 582 | } |
551 | 583 | ||