diff options
| -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 | ||
