diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/ec.c | 81 |
1 files changed, 73 insertions, 8 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index c385606bbceb..2540870e89f7 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -121,6 +121,7 @@ struct transaction { | |||
121 | }; | 121 | }; |
122 | 122 | ||
123 | static int acpi_ec_query(struct acpi_ec *ec, u8 *data); | 123 | static int acpi_ec_query(struct acpi_ec *ec, u8 *data); |
124 | static void advance_transaction(struct acpi_ec *ec); | ||
124 | 125 | ||
125 | struct acpi_ec *boot_ec, *first_ec; | 126 | struct acpi_ec *boot_ec, *first_ec; |
126 | EXPORT_SYMBOL(first_ec); | 127 | EXPORT_SYMBOL(first_ec); |
@@ -132,7 +133,7 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ | |||
132 | static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ | 133 | static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ |
133 | 134 | ||
134 | /* -------------------------------------------------------------------------- | 135 | /* -------------------------------------------------------------------------- |
135 | * Transaction Management | 136 | * EC Registers |
136 | * -------------------------------------------------------------------------- */ | 137 | * -------------------------------------------------------------------------- */ |
137 | 138 | ||
138 | static inline u8 acpi_ec_read_status(struct acpi_ec *ec) | 139 | static inline u8 acpi_ec_read_status(struct acpi_ec *ec) |
@@ -191,6 +192,64 @@ static const char *acpi_ec_cmd_string(u8 cmd) | |||
191 | #define acpi_ec_cmd_string(cmd) "UNDEF" | 192 | #define acpi_ec_cmd_string(cmd) "UNDEF" |
192 | #endif | 193 | #endif |
193 | 194 | ||
195 | /* -------------------------------------------------------------------------- | ||
196 | * GPE Registers | ||
197 | * -------------------------------------------------------------------------- */ | ||
198 | |||
199 | static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec) | ||
200 | { | ||
201 | acpi_event_status gpe_status = 0; | ||
202 | |||
203 | (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status); | ||
204 | return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false; | ||
205 | } | ||
206 | |||
207 | static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) | ||
208 | { | ||
209 | if (open) | ||
210 | acpi_enable_gpe(NULL, ec->gpe); | ||
211 | else | ||
212 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); | ||
213 | if (acpi_ec_is_gpe_raised(ec)) { | ||
214 | /* | ||
215 | * On some platforms, EN=1 writes cannot trigger GPE. So | ||
216 | * software need to manually trigger a pseudo GPE event on | ||
217 | * EN=1 writes. | ||
218 | */ | ||
219 | pr_debug("***** Polling quirk *****\n"); | ||
220 | advance_transaction(ec); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) | ||
225 | { | ||
226 | if (close) | ||
227 | acpi_disable_gpe(NULL, ec->gpe); | ||
228 | else | ||
229 | acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); | ||
230 | } | ||
231 | |||
232 | static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) | ||
233 | { | ||
234 | /* | ||
235 | * GPE STS is a W1C register, which means: | ||
236 | * 1. Software can clear it without worrying about clearing other | ||
237 | * GPEs' STS bits when the hardware sets them in parallel. | ||
238 | * 2. As long as software can ensure only clearing it when it is | ||
239 | * set, hardware won't set it in parallel. | ||
240 | * So software can clear GPE in any contexts. | ||
241 | * Warning: do not move the check into advance_transaction() as the | ||
242 | * EC commands will be sent without GPE raised. | ||
243 | */ | ||
244 | if (!acpi_ec_is_gpe_raised(ec)) | ||
245 | return; | ||
246 | acpi_clear_gpe(NULL, ec->gpe); | ||
247 | } | ||
248 | |||
249 | /* -------------------------------------------------------------------------- | ||
250 | * Transaction Management | ||
251 | * -------------------------------------------------------------------------- */ | ||
252 | |||
194 | static void acpi_ec_submit_query(struct acpi_ec *ec) | 253 | static void acpi_ec_submit_query(struct acpi_ec *ec) |
195 | { | 254 | { |
196 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { | 255 | if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { |
@@ -227,6 +286,12 @@ static void advance_transaction(struct acpi_ec *ec) | |||
227 | 286 | ||
228 | pr_debug("===== %s (%d) =====\n", | 287 | pr_debug("===== %s (%d) =====\n", |
229 | in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); | 288 | in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); |
289 | /* | ||
290 | * By always clearing STS before handling all indications, we can | ||
291 | * ensure a hardware STS 0->1 change after this clearing can always | ||
292 | * trigger a GPE interrupt. | ||
293 | */ | ||
294 | acpi_ec_clear_gpe(ec); | ||
230 | status = acpi_ec_read_status(ec); | 295 | status = acpi_ec_read_status(ec); |
231 | t = ec->curr; | 296 | t = ec->curr; |
232 | if (!t) | 297 | if (!t) |
@@ -378,7 +443,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
378 | /* disable GPE during transaction if storm is detected */ | 443 | /* disable GPE during transaction if storm is detected */ |
379 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | 444 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { |
380 | /* It has to be disabled, so that it doesn't trigger. */ | 445 | /* It has to be disabled, so that it doesn't trigger. */ |
381 | acpi_disable_gpe(NULL, ec->gpe); | 446 | acpi_ec_disable_gpe(ec, false); |
382 | } | 447 | } |
383 | 448 | ||
384 | status = acpi_ec_transaction_unlocked(ec, t); | 449 | status = acpi_ec_transaction_unlocked(ec, t); |
@@ -386,7 +451,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) | |||
386 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | 451 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { |
387 | msleep(1); | 452 | msleep(1); |
388 | /* It is safe to enable the GPE outside of the transaction. */ | 453 | /* It is safe to enable the GPE outside of the transaction. */ |
389 | acpi_enable_gpe(NULL, ec->gpe); | 454 | acpi_ec_enable_gpe(ec, false); |
390 | } else if (t->irq_count > ec_storm_threshold) { | 455 | } else if (t->irq_count > ec_storm_threshold) { |
391 | pr_info("GPE storm detected(%d GPEs), " | 456 | pr_info("GPE storm detected(%d GPEs), " |
392 | "transactions will use polling mode\n", | 457 | "transactions will use polling mode\n", |
@@ -693,7 +758,7 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, | |||
693 | spin_lock_irqsave(&ec->lock, flags); | 758 | spin_lock_irqsave(&ec->lock, flags); |
694 | advance_transaction(ec); | 759 | advance_transaction(ec); |
695 | spin_unlock_irqrestore(&ec->lock, flags); | 760 | spin_unlock_irqrestore(&ec->lock, flags); |
696 | return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; | 761 | return ACPI_INTERRUPT_HANDLED; |
697 | } | 762 | } |
698 | 763 | ||
699 | /* -------------------------------------------------------------------------- | 764 | /* -------------------------------------------------------------------------- |
@@ -812,13 +877,13 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
812 | 877 | ||
813 | if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) | 878 | if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) |
814 | return 0; | 879 | return 0; |
815 | status = acpi_install_gpe_handler(NULL, ec->gpe, | 880 | status = acpi_install_gpe_raw_handler(NULL, ec->gpe, |
816 | ACPI_GPE_EDGE_TRIGGERED, | 881 | ACPI_GPE_EDGE_TRIGGERED, |
817 | &acpi_ec_gpe_handler, ec); | 882 | &acpi_ec_gpe_handler, ec); |
818 | if (ACPI_FAILURE(status)) | 883 | if (ACPI_FAILURE(status)) |
819 | return -ENODEV; | 884 | return -ENODEV; |
820 | 885 | ||
821 | acpi_enable_gpe(NULL, ec->gpe); | 886 | acpi_ec_enable_gpe(ec, true); |
822 | status = acpi_install_address_space_handler(ec->handle, | 887 | status = acpi_install_address_space_handler(ec->handle, |
823 | ACPI_ADR_SPACE_EC, | 888 | ACPI_ADR_SPACE_EC, |
824 | &acpi_ec_space_handler, | 889 | &acpi_ec_space_handler, |
@@ -833,7 +898,7 @@ static int ec_install_handlers(struct acpi_ec *ec) | |||
833 | pr_err("Fail in evaluating the _REG object" | 898 | pr_err("Fail in evaluating the _REG object" |
834 | " of EC device. Broken bios is suspected.\n"); | 899 | " of EC device. Broken bios is suspected.\n"); |
835 | } else { | 900 | } else { |
836 | acpi_disable_gpe(NULL, ec->gpe); | 901 | acpi_ec_disable_gpe(ec, true); |
837 | acpi_remove_gpe_handler(NULL, ec->gpe, | 902 | acpi_remove_gpe_handler(NULL, ec->gpe, |
838 | &acpi_ec_gpe_handler); | 903 | &acpi_ec_gpe_handler); |
839 | return -ENODEV; | 904 | return -ENODEV; |
@@ -848,7 +913,7 @@ static void ec_remove_handlers(struct acpi_ec *ec) | |||
848 | { | 913 | { |
849 | if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) | 914 | if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) |
850 | return; | 915 | return; |
851 | acpi_disable_gpe(NULL, ec->gpe); | 916 | acpi_ec_disable_gpe(ec, true); |
852 | if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, | 917 | if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, |
853 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) | 918 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) |
854 | pr_err("failed to remove space handler\n"); | 919 | pr_err("failed to remove space handler\n"); |