diff options
Diffstat (limited to 'drivers/acpi/ec.c')
| -rw-r--r-- | drivers/acpi/ec.c | 126 |
1 files changed, 80 insertions, 46 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fd1801bdee66..d6471bb6852f 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 */ |
| @@ -265,20 +259,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
| 265 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); | 259 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); |
| 266 | spin_unlock_irqrestore(&ec->curr_lock, tmp); | 260 | spin_unlock_irqrestore(&ec->curr_lock, tmp); |
| 267 | ret = ec_poll(ec); | 261 | ret = ec_poll(ec); |
| 268 | pr_debug(PREFIX "transaction end\n"); | ||
| 269 | spin_lock_irqsave(&ec->curr_lock, tmp); | 262 | spin_lock_irqsave(&ec->curr_lock, tmp); |
| 270 | ec->curr = NULL; | 263 | ec->curr = NULL; |
| 271 | spin_unlock_irqrestore(&ec->curr_lock, tmp); | 264 | 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; | 265 | return ret; |
| 283 | } | 266 | } |
| 284 | 267 | ||
| @@ -321,7 +304,26 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
| 321 | status = -ETIME; | 304 | status = -ETIME; |
| 322 | goto end; | 305 | goto end; |
| 323 | } | 306 | } |
| 307 | pr_debug(PREFIX "transaction start\n"); | ||
| 308 | /* disable GPE during transaction if storm is detected */ | ||
| 309 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
| 310 | acpi_disable_gpe(NULL, ec->gpe); | ||
| 311 | } | ||
| 312 | |||
| 324 | status = acpi_ec_transaction_unlocked(ec, t); | 313 | status = acpi_ec_transaction_unlocked(ec, t); |
| 314 | |||
| 315 | /* check if we received SCI during transaction */ | ||
| 316 | ec_check_sci_sync(ec, acpi_ec_read_status(ec)); | ||
| 317 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | ||
| 318 | msleep(1); | ||
| 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 | } | ||
| 326 | pr_debug(PREFIX "transaction end\n"); | ||
| 325 | end: | 327 | end: |
| 326 | if (ec->global_lock) | 328 | if (ec->global_lock) |
| 327 | acpi_release_global_lock(glk); | 329 | acpi_release_global_lock(glk); |
| @@ -443,7 +445,7 @@ int ec_transaction(u8 command, | |||
| 443 | 445 | ||
| 444 | EXPORT_SYMBOL(ec_transaction); | 446 | EXPORT_SYMBOL(ec_transaction); |
| 445 | 447 | ||
| 446 | static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | 448 | static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data) |
| 447 | { | 449 | { |
| 448 | int result; | 450 | int result; |
| 449 | u8 d; | 451 | u8 d; |
| @@ -452,20 +454,16 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
| 452 | .wlen = 0, .rlen = 1}; | 454 | .wlen = 0, .rlen = 1}; |
| 453 | if (!ec || !data) | 455 | if (!ec || !data) |
| 454 | return -EINVAL; | 456 | return -EINVAL; |
| 455 | |||
| 456 | /* | 457 | /* |
| 457 | * Query the EC to find out which _Qxx method we need to evaluate. | 458 | * 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 | 459 | * Note that successful completion of the query causes the ACPI_EC_SCI |
| 459 | * bit to be cleared (and thus clearing the interrupt source). | 460 | * bit to be cleared (and thus clearing the interrupt source). |
| 460 | */ | 461 | */ |
| 461 | 462 | result = acpi_ec_transaction_unlocked(ec, &t); | |
| 462 | result = acpi_ec_transaction(ec, &t); | ||
| 463 | if (result) | 463 | if (result) |
| 464 | return result; | 464 | return result; |
| 465 | |||
| 466 | if (!d) | 465 | if (!d) |
| 467 | return -ENODATA; | 466 | return -ENODATA; |
| 468 | |||
| 469 | *data = d; | 467 | *data = d; |
| 470 | return 0; | 468 | return 0; |
| 471 | } | 469 | } |
| @@ -509,43 +507,79 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) | |||
| 509 | 507 | ||
| 510 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); | 508 | EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); |
| 511 | 509 | ||
| 512 | static void acpi_ec_gpe_query(void *ec_cxt) | 510 | static void acpi_ec_run(void *cxt) |
| 513 | { | 511 | { |
| 514 | struct acpi_ec *ec = ec_cxt; | 512 | struct acpi_ec_query_handler *handler = cxt; |
| 515 | u8 value = 0; | 513 | if (!handler) |
| 516 | struct acpi_ec_query_handler *handler, copy; | ||
| 517 | |||
| 518 | if (!ec || acpi_ec_query(ec, &value)) | ||
| 519 | return; | 514 | return; |
| 520 | mutex_lock(&ec->lock); | 515 | pr_debug(PREFIX "start query execution\n"); |
| 516 | if (handler->func) | ||
| 517 | handler->func(handler->data); | ||
| 518 | else if (handler->handle) | ||
| 519 | acpi_evaluate_object(handler->handle, NULL, NULL, NULL); | ||
| 520 | pr_debug(PREFIX "stop query execution\n"); | ||
| 521 | kfree(handler); | ||
| 522 | } | ||
| 523 | |||
| 524 | static int acpi_ec_sync_query(struct acpi_ec *ec) | ||
| 525 | { | ||
| 526 | u8 value = 0; | ||
| 527 | int status; | ||
| 528 | struct acpi_ec_query_handler *handler, *copy; | ||
| 529 | if ((status = acpi_ec_query_unlocked(ec, &value))) | ||
| 530 | return status; | ||
| 521 | list_for_each_entry(handler, &ec->list, node) { | 531 | list_for_each_entry(handler, &ec->list, node) { |
| 522 | if (value == handler->query_bit) { | 532 | if (value == handler->query_bit) { |
| 523 | /* have custom handler for this bit */ | 533 | /* have custom handler for this bit */ |
| 524 | memcpy(©, handler, sizeof(copy)); | 534 | copy = kmalloc(sizeof(*handler), GFP_KERNEL); |
| 525 | mutex_unlock(&ec->lock); | 535 | if (!copy) |
| 526 | if (copy.func) { | 536 | return -ENOMEM; |
| 527 | copy.func(copy.data); | 537 | memcpy(copy, handler, sizeof(*copy)); |
| 528 | } else if (copy.handle) { | 538 | pr_debug(PREFIX "push query execution (0x%2x) on queue\n", value); |
| 529 | acpi_evaluate_object(copy.handle, NULL, NULL, NULL); | 539 | return acpi_os_execute((copy->func) ? |
| 530 | } | 540 | OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, |
| 531 | return; | 541 | acpi_ec_run, copy); |
| 532 | } | 542 | } |
| 533 | } | 543 | } |
| 544 | return 0; | ||
| 545 | } | ||
| 546 | |||
| 547 | static void acpi_ec_gpe_query(void *ec_cxt) | ||
| 548 | { | ||
| 549 | struct acpi_ec *ec = ec_cxt; | ||
| 550 | if (!ec) | ||
| 551 | return; | ||
| 552 | mutex_lock(&ec->lock); | ||
| 553 | acpi_ec_sync_query(ec); | ||
| 534 | mutex_unlock(&ec->lock); | 554 | mutex_unlock(&ec->lock); |
| 535 | } | 555 | } |
| 536 | 556 | ||
| 557 | static void acpi_ec_gpe_query(void *ec_cxt); | ||
| 558 | |||
| 559 | static int ec_check_sci(struct acpi_ec *ec, u8 state) | ||
| 560 | { | ||
| 561 | if (state & ACPI_EC_FLAG_SCI) { | ||
| 562 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { | ||
| 563 | pr_debug(PREFIX "push gpe query to the queue\n"); | ||
| 564 | return acpi_os_execute(OSL_NOTIFY_HANDLER, | ||
| 565 | acpi_ec_gpe_query, ec); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | return 0; | ||
| 569 | } | ||
| 570 | |||
| 537 | static u32 acpi_ec_gpe_handler(void *data) | 571 | static u32 acpi_ec_gpe_handler(void *data) |
| 538 | { | 572 | { |
| 539 | struct acpi_ec *ec = data; | 573 | struct acpi_ec *ec = data; |
| 540 | u8 status; | ||
| 541 | 574 | ||
| 542 | pr_debug(PREFIX "~~~> interrupt\n"); | 575 | pr_debug(PREFIX "~~~> interrupt\n"); |
| 543 | status = acpi_ec_read_status(ec); | ||
| 544 | 576 | ||
| 545 | advance_transaction(ec, status); | 577 | advance_transaction(ec, acpi_ec_read_status(ec)); |
| 546 | if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) | 578 | if (ec_transaction_done(ec) && |
| 579 | (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { | ||
| 547 | wake_up(&ec->wait); | 580 | wake_up(&ec->wait); |
| 548 | ec_check_sci(ec, status); | 581 | ec_check_sci(ec, acpi_ec_read_status(ec)); |
| 582 | } | ||
| 549 | return ACPI_INTERRUPT_HANDLED; | 583 | return ACPI_INTERRUPT_HANDLED; |
| 550 | } | 584 | } |
| 551 | 585 | ||
