diff options
| -rw-r--r-- | drivers/acpi/ec.c | 68 | ||||
| -rw-r--r-- | drivers/acpi/internal.h | 1 |
2 files changed, 66 insertions, 3 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a6179b71c573..1fa14634c3f6 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
| @@ -145,6 +145,11 @@ static bool acpi_ec_started(struct acpi_ec *ec) | |||
| 145 | !test_bit(EC_FLAGS_STOPPED, &ec->flags); | 145 | !test_bit(EC_FLAGS_STOPPED, &ec->flags); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | static bool acpi_ec_flushed(struct acpi_ec *ec) | ||
| 149 | { | ||
| 150 | return ec->reference_count == 1; | ||
| 151 | } | ||
| 152 | |||
| 148 | /* -------------------------------------------------------------------------- | 153 | /* -------------------------------------------------------------------------- |
| 149 | * EC Registers | 154 | * EC Registers |
| 150 | * -------------------------------------------------------------------------- */ | 155 | * -------------------------------------------------------------------------- */ |
| @@ -266,6 +271,44 @@ static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) | |||
| 266 | * Transaction Management | 271 | * Transaction Management |
| 267 | * -------------------------------------------------------------------------- */ | 272 | * -------------------------------------------------------------------------- */ |
| 268 | 273 | ||
| 274 | static void acpi_ec_submit_request(struct acpi_ec *ec) | ||
| 275 | { | ||
| 276 | ec->reference_count++; | ||
| 277 | if (ec->reference_count == 1) | ||
| 278 | acpi_ec_enable_gpe(ec, true); | ||
| 279 | } | ||
| 280 | |||
| 281 | static void acpi_ec_complete_request(struct acpi_ec *ec) | ||
| 282 | { | ||
| 283 | bool flushed = false; | ||
| 284 | |||
| 285 | ec->reference_count--; | ||
| 286 | if (ec->reference_count == 0) | ||
| 287 | acpi_ec_disable_gpe(ec, true); | ||
| 288 | flushed = acpi_ec_flushed(ec); | ||
| 289 | if (flushed) | ||
| 290 | wake_up(&ec->wait); | ||
| 291 | } | ||
| 292 | |||
| 293 | /* | ||
| 294 | * acpi_ec_submit_flushable_request() - Increase the reference count unless | ||
| 295 | * the flush operation is not in | ||
| 296 | * progress | ||
| 297 | * @ec: the EC device | ||
| 298 | * | ||
| 299 | * This function must be used before taking a new action that should hold | ||
| 300 | * the reference count. If this function returns false, then the action | ||
| 301 | * must be discarded or it will prevent the flush operation from being | ||
| 302 | * completed. | ||
| 303 | */ | ||
| 304 | static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) | ||
| 305 | { | ||
| 306 | if (!acpi_ec_started(ec)) | ||
| 307 | return false; | ||
| 308 | acpi_ec_submit_request(ec); | ||
| 309 | return true; | ||
| 310 | } | ||
| 311 | |||
| 269 | static void acpi_ec_submit_query(struct acpi_ec *ec) | 312 | static void acpi_ec_submit_query(struct acpi_ec *ec) |
| 270 | { | 313 | { |
| 271 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { | 314 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { |
| @@ -426,7 +469,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
| 426 | udelay(ACPI_EC_MSI_UDELAY); | 469 | udelay(ACPI_EC_MSI_UDELAY); |
| 427 | /* start transaction */ | 470 | /* start transaction */ |
| 428 | spin_lock_irqsave(&ec->lock, tmp); | 471 | spin_lock_irqsave(&ec->lock, tmp); |
| 429 | if (!acpi_ec_started(ec)) { | 472 | /* Enable GPE for command processing (IBF=0/OBF=1) */ |
| 473 | if (!acpi_ec_submit_flushable_request(ec)) { | ||
| 430 | ret = -EINVAL; | 474 | ret = -EINVAL; |
| 431 | goto unlock; | 475 | goto unlock; |
| 432 | } | 476 | } |
| @@ -441,6 +485,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, | |||
| 441 | pr_debug("***** Command(%s) stopped *****\n", | 485 | pr_debug("***** Command(%s) stopped *****\n", |
| 442 | acpi_ec_cmd_string(t->command)); | 486 | acpi_ec_cmd_string(t->command)); |
| 443 | ec->curr = NULL; | 487 | ec->curr = NULL; |
| 488 | /* Disable GPE for command processing (IBF=0/OBF=1) */ | ||
| 489 | acpi_ec_complete_request(ec); | ||
| 444 | unlock: | 490 | unlock: |
| 445 | spin_unlock_irqrestore(&ec->lock, tmp); | 491 | spin_unlock_irqrestore(&ec->lock, tmp); |
| 446 | return ret; | 492 | return ret; |
| @@ -614,13 +660,25 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) | |||
| 614 | spin_lock_irqsave(&ec->lock, flags); | 660 | spin_lock_irqsave(&ec->lock, flags); |
| 615 | if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { | 661 | if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { |
| 616 | pr_debug("+++++ Starting EC +++++\n"); | 662 | pr_debug("+++++ Starting EC +++++\n"); |
| 663 | /* Enable GPE for event processing (SCI_EVT=1) */ | ||
| 617 | if (!resuming) | 664 | if (!resuming) |
| 618 | acpi_ec_enable_gpe(ec, true); | 665 | acpi_ec_submit_request(ec); |
| 619 | pr_info("+++++ EC started +++++\n"); | 666 | pr_info("+++++ EC started +++++\n"); |
| 620 | } | 667 | } |
| 621 | spin_unlock_irqrestore(&ec->lock, flags); | 668 | spin_unlock_irqrestore(&ec->lock, flags); |
| 622 | } | 669 | } |
| 623 | 670 | ||
| 671 | static bool acpi_ec_stopped(struct acpi_ec *ec) | ||
| 672 | { | ||
| 673 | unsigned long flags; | ||
| 674 | bool flushed; | ||
| 675 | |||
| 676 | spin_lock_irqsave(&ec->lock, flags); | ||
| 677 | flushed = acpi_ec_flushed(ec); | ||
| 678 | spin_unlock_irqrestore(&ec->lock, flags); | ||
| 679 | return flushed; | ||
| 680 | } | ||
| 681 | |||
| 624 | static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) | 682 | static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) |
| 625 | { | 683 | { |
| 626 | unsigned long flags; | 684 | unsigned long flags; |
| @@ -629,8 +687,12 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) | |||
| 629 | if (acpi_ec_started(ec)) { | 687 | if (acpi_ec_started(ec)) { |
| 630 | pr_debug("+++++ Stopping EC +++++\n"); | 688 | pr_debug("+++++ Stopping EC +++++\n"); |
| 631 | set_bit(EC_FLAGS_STOPPED, &ec->flags); | 689 | set_bit(EC_FLAGS_STOPPED, &ec->flags); |
| 690 | spin_unlock_irqrestore(&ec->lock, flags); | ||
| 691 | wait_event(ec->wait, acpi_ec_stopped(ec)); | ||
| 692 | spin_lock_irqsave(&ec->lock, flags); | ||
| 693 | /* Disable GPE for event processing (SCI_EVT=1) */ | ||
| 632 | if (!suspending) | 694 | if (!suspending) |
| 633 | acpi_ec_disable_gpe(ec, true); | 695 | acpi_ec_complete_request(ec); |
| 634 | clear_bit(EC_FLAGS_STARTED, &ec->flags); | 696 | clear_bit(EC_FLAGS_STARTED, &ec->flags); |
| 635 | clear_bit(EC_FLAGS_STOPPED, &ec->flags); | 697 | clear_bit(EC_FLAGS_STOPPED, &ec->flags); |
| 636 | pr_info("+++++ EC stopped +++++\n"); | 698 | pr_info("+++++ EC stopped +++++\n"); |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index dc420787ffcd..7dc69d82f658 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
| @@ -122,6 +122,7 @@ struct acpi_ec { | |||
| 122 | unsigned long data_addr; | 122 | unsigned long data_addr; |
| 123 | unsigned long global_lock; | 123 | unsigned long global_lock; |
| 124 | unsigned long flags; | 124 | unsigned long flags; |
| 125 | unsigned long reference_count; | ||
| 125 | struct mutex mutex; | 126 | struct mutex mutex; |
| 126 | wait_queue_head_t wait; | 127 | wait_queue_head_t wait; |
| 127 | struct list_head list; | 128 | struct list_head list; |
