diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2010-06-08 04:49:08 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2010-06-12 00:44:37 -0400 |
commit | fd247447c1d94a79d5cfc647430784306b3a8323 (patch) | |
tree | d3a58bb5e50068f4d9245f0788e7f14ff56ac711 /drivers/acpi | |
parent | e4e9a735991c80fb0fc1bd4a13a93681c3c17ce0 (diff) |
ACPI / ACPICA: Fix low-level GPE manipulation code
ACPICA uses acpi_ev_enable_gpe() for enabling GPEs at the low level,
which is incorrect, because this function only enables the GPE if the
corresponding bit in its enable register's enable_for_run mask is set.
This causes acpi_set_gpe() to work incorrectly if used for enabling
GPEs that were not previously enabled with acpi_enable_gpe(). As a
result, among other things, wakeup-only GPEs are never enabled by
acpi_enable_wakeup_device(), so the devices that use them are unable
to wake up the system.
To fix this issue remove acpi_ev_enable_gpe() and its counterpart
acpi_ev_disable_gpe() and replace acpi_hw_low_disable_gpe() with
acpi_hw_low_set_gpe() that will be used instead to manipulate GPE
enable bits at the low level. Make the users of acpi_ev_enable_gpe()
and acpi_ev_disable_gpe() call acpi_hw_low_set_gpe() instead and
make sure that GPE enable masks are only updated by acpi_enable_gpe()
and acpi_disable_gpe() when GPE reference counters change from 0
to 1 and from 1 to 0, respectively.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/acpica/acevents.h | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/achware.h | 3 | ||||
-rw-r--r-- | drivers/acpi/acpica/evgpe.c | 108 | ||||
-rw-r--r-- | drivers/acpi/acpica/evxfevnt.c | 59 | ||||
-rw-r--r-- | drivers/acpi/acpica/hwgpe.c | 26 |
5 files changed, 78 insertions, 122 deletions
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 5e094a28cf54..138bbb521930 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h | |||
@@ -78,10 +78,6 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node, | |||
78 | acpi_status | 78 | acpi_status |
79 | acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info); | 79 | acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info); |
80 | 80 | ||
81 | acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); | ||
82 | |||
83 | acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); | ||
84 | |||
85 | struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, | 81 | struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device, |
86 | u32 gpe_number); | 82 | u32 gpe_number); |
87 | 83 | ||
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index c46277d179f0..32391588e163 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h | |||
@@ -93,7 +93,8 @@ acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width); | |||
93 | u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, | 93 | u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, |
94 | struct acpi_gpe_register_info *gpe_register_info); | 94 | struct acpi_gpe_register_info *gpe_register_info); |
95 | 95 | ||
96 | acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info); | 96 | acpi_status |
97 | acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action); | ||
97 | 98 | ||
98 | acpi_status | 99 | acpi_status |
99 | acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info); | 100 | acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info); |
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 57eeb3bde41e..66cd03835d6e 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c | |||
@@ -99,106 +99,6 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info) | |||
99 | return_ACPI_STATUS(AE_OK); | 99 | return_ACPI_STATUS(AE_OK); |
100 | } | 100 | } |
101 | 101 | ||
102 | /******************************************************************************* | ||
103 | * | ||
104 | * FUNCTION: acpi_ev_enable_gpe | ||
105 | * | ||
106 | * PARAMETERS: gpe_event_info - GPE to enable | ||
107 | * | ||
108 | * RETURN: Status | ||
109 | * | ||
110 | * DESCRIPTION: Hardware-enable a GPE. Always enables the GPE, regardless | ||
111 | * of type or number of references. | ||
112 | * | ||
113 | * Note: The GPE lock should be already acquired when this function is called. | ||
114 | * | ||
115 | ******************************************************************************/ | ||
116 | |||
117 | acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | ||
118 | { | ||
119 | acpi_status status; | ||
120 | |||
121 | |||
122 | ACPI_FUNCTION_TRACE(ev_enable_gpe); | ||
123 | |||
124 | |||
125 | /* | ||
126 | * We will only allow a GPE to be enabled if it has either an | ||
127 | * associated method (_Lxx/_Exx) or a handler. Otherwise, the | ||
128 | * GPE will be immediately disabled by acpi_ev_gpe_dispatch the | ||
129 | * first time it fires. | ||
130 | */ | ||
131 | if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { | ||
132 | return_ACPI_STATUS(AE_NO_HANDLER); | ||
133 | } | ||
134 | |||
135 | /* Ensure the HW enable masks are current */ | ||
136 | |||
137 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); | ||
138 | if (ACPI_FAILURE(status)) { | ||
139 | return_ACPI_STATUS(status); | ||
140 | } | ||
141 | |||
142 | /* Clear the GPE (of stale events) */ | ||
143 | |||
144 | status = acpi_hw_clear_gpe(gpe_event_info); | ||
145 | if (ACPI_FAILURE(status)) { | ||
146 | return_ACPI_STATUS(status); | ||
147 | } | ||
148 | |||
149 | /* Enable the requested GPE */ | ||
150 | |||
151 | status = acpi_hw_write_gpe_enable_reg(gpe_event_info); | ||
152 | return_ACPI_STATUS(status); | ||
153 | } | ||
154 | |||
155 | /******************************************************************************* | ||
156 | * | ||
157 | * FUNCTION: acpi_ev_disable_gpe | ||
158 | * | ||
159 | * PARAMETERS: gpe_event_info - GPE to disable | ||
160 | * | ||
161 | * RETURN: Status | ||
162 | * | ||
163 | * DESCRIPTION: Hardware-disable a GPE. Always disables the requested GPE, | ||
164 | * regardless of the type or number of references. | ||
165 | * | ||
166 | * Note: The GPE lock should be already acquired when this function is called. | ||
167 | * | ||
168 | ******************************************************************************/ | ||
169 | |||
170 | acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) | ||
171 | { | ||
172 | acpi_status status; | ||
173 | |||
174 | ACPI_FUNCTION_TRACE(ev_disable_gpe); | ||
175 | |||
176 | |||
177 | /* | ||
178 | * Note: Always disable the GPE, even if we think that that it is already | ||
179 | * disabled. It is possible that the AML or some other code has enabled | ||
180 | * the GPE behind our back. | ||
181 | */ | ||
182 | |||
183 | /* Ensure the HW enable masks are current */ | ||
184 | |||
185 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); | ||
186 | if (ACPI_FAILURE(status)) { | ||
187 | return_ACPI_STATUS(status); | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Always H/W disable this GPE, even if we don't know the GPE type. | ||
192 | * Simply clear the enable bit for this particular GPE, but do not | ||
193 | * write out the current GPE enable mask since this may inadvertently | ||
194 | * enable GPEs too early. An example is a rogue GPE that has arrived | ||
195 | * during ACPICA initialization - possibly because AML or other code | ||
196 | * has enabled the GPE. | ||
197 | */ | ||
198 | status = acpi_hw_low_disable_gpe(gpe_event_info); | ||
199 | return_ACPI_STATUS(status); | ||
200 | } | ||
201 | |||
202 | 102 | ||
203 | /******************************************************************************* | 103 | /******************************************************************************* |
204 | * | 104 | * |
@@ -450,10 +350,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) | |||
450 | return_VOID; | 350 | return_VOID; |
451 | } | 351 | } |
452 | 352 | ||
453 | /* Update the GPE register masks for return to enabled state */ | ||
454 | |||
455 | (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); | ||
456 | |||
457 | /* | 353 | /* |
458 | * Take a snapshot of the GPE info for this level - we copy the info to | 354 | * Take a snapshot of the GPE info for this level - we copy the info to |
459 | * prevent a race condition with remove_handler/remove_block. | 355 | * prevent a race condition with remove_handler/remove_block. |
@@ -606,7 +502,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) | |||
606 | * Disable the GPE, so it doesn't keep firing before the method has a | 502 | * Disable the GPE, so it doesn't keep firing before the method has a |
607 | * chance to run (it runs asynchronously with interrupts enabled). | 503 | * chance to run (it runs asynchronously with interrupts enabled). |
608 | */ | 504 | */ |
609 | status = acpi_ev_disable_gpe(gpe_event_info); | 505 | status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); |
610 | if (ACPI_FAILURE(status)) { | 506 | if (ACPI_FAILURE(status)) { |
611 | ACPI_EXCEPTION((AE_INFO, status, | 507 | ACPI_EXCEPTION((AE_INFO, status, |
612 | "Unable to disable GPE[0x%2X]", | 508 | "Unable to disable GPE[0x%2X]", |
@@ -643,7 +539,7 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) | |||
643 | * Disable the GPE. The GPE will remain disabled a handler | 539 | * Disable the GPE. The GPE will remain disabled a handler |
644 | * is installed or ACPICA is restarted. | 540 | * is installed or ACPICA is restarted. |
645 | */ | 541 | */ |
646 | status = acpi_ev_disable_gpe(gpe_event_info); | 542 | status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); |
647 | if (ACPI_FAILURE(status)) { | 543 | if (ACPI_FAILURE(status)) { |
648 | ACPI_EXCEPTION((AE_INFO, status, | 544 | ACPI_EXCEPTION((AE_INFO, status, |
649 | "Unable to disable GPE[0x%2X]", | 545 | "Unable to disable GPE[0x%2X]", |
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 7c7bbb4d402c..e3d9f5c8e53d 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c | |||
@@ -201,6 +201,44 @@ ACPI_EXPORT_SYMBOL(acpi_enable_event) | |||
201 | 201 | ||
202 | /******************************************************************************* | 202 | /******************************************************************************* |
203 | * | 203 | * |
204 | * FUNCTION: acpi_clear_and_enable_gpe | ||
205 | * | ||
206 | * PARAMETERS: gpe_event_info - GPE to enable | ||
207 | * | ||
208 | * RETURN: Status | ||
209 | * | ||
210 | * DESCRIPTION: Clear the given GPE from stale events and enable it. | ||
211 | * | ||
212 | ******************************************************************************/ | ||
213 | static acpi_status | ||
214 | acpi_clear_and_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) | ||
215 | { | ||
216 | acpi_status status; | ||
217 | |||
218 | /* | ||
219 | * We will only allow a GPE to be enabled if it has either an | ||
220 | * associated method (_Lxx/_Exx) or a handler. Otherwise, the | ||
221 | * GPE will be immediately disabled by acpi_ev_gpe_dispatch the | ||
222 | * first time it fires. | ||
223 | */ | ||
224 | if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) { | ||
225 | return_ACPI_STATUS(AE_NO_HANDLER); | ||
226 | } | ||
227 | |||
228 | /* Clear the GPE (of stale events) */ | ||
229 | status = acpi_hw_clear_gpe(gpe_event_info); | ||
230 | if (ACPI_FAILURE(status)) { | ||
231 | return_ACPI_STATUS(status); | ||
232 | } | ||
233 | |||
234 | /* Enable the requested GPE */ | ||
235 | status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); | ||
236 | |||
237 | return_ACPI_STATUS(status); | ||
238 | } | ||
239 | |||
240 | /******************************************************************************* | ||
241 | * | ||
204 | * FUNCTION: acpi_set_gpe | 242 | * FUNCTION: acpi_set_gpe |
205 | * | 243 | * |
206 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 | 244 | * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 |
@@ -240,11 +278,11 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) | |||
240 | 278 | ||
241 | switch (action) { | 279 | switch (action) { |
242 | case ACPI_GPE_ENABLE: | 280 | case ACPI_GPE_ENABLE: |
243 | status = acpi_ev_enable_gpe(gpe_event_info); | 281 | status = acpi_clear_and_enable_gpe(gpe_event_info); |
244 | break; | 282 | break; |
245 | 283 | ||
246 | case ACPI_GPE_DISABLE: | 284 | case ACPI_GPE_DISABLE: |
247 | status = acpi_ev_disable_gpe(gpe_event_info); | 285 | status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); |
248 | break; | 286 | break; |
249 | 287 | ||
250 | default: | 288 | default: |
@@ -307,7 +345,11 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type) | |||
307 | 345 | ||
308 | gpe_event_info->runtime_count++; | 346 | gpe_event_info->runtime_count++; |
309 | if (gpe_event_info->runtime_count == 1) { | 347 | if (gpe_event_info->runtime_count == 1) { |
310 | status = acpi_ev_enable_gpe(gpe_event_info); | 348 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); |
349 | if (ACPI_SUCCESS(status)) { | ||
350 | status = acpi_clear_and_enable_gpe(gpe_event_info); | ||
351 | } | ||
352 | |||
311 | if (ACPI_FAILURE(status)) { | 353 | if (ACPI_FAILURE(status)) { |
312 | gpe_event_info->runtime_count--; | 354 | gpe_event_info->runtime_count--; |
313 | goto unlock_and_exit; | 355 | goto unlock_and_exit; |
@@ -334,7 +376,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type) | |||
334 | */ | 376 | */ |
335 | gpe_event_info->wakeup_count++; | 377 | gpe_event_info->wakeup_count++; |
336 | if (gpe_event_info->wakeup_count == 1) { | 378 | if (gpe_event_info->wakeup_count == 1) { |
337 | (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); | 379 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); |
338 | } | 380 | } |
339 | } | 381 | } |
340 | 382 | ||
@@ -394,7 +436,12 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type | |||
394 | 436 | ||
395 | gpe_event_info->runtime_count--; | 437 | gpe_event_info->runtime_count--; |
396 | if (!gpe_event_info->runtime_count) { | 438 | if (!gpe_event_info->runtime_count) { |
397 | status = acpi_ev_disable_gpe(gpe_event_info); | 439 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); |
440 | if (ACPI_SUCCESS(status)) { | ||
441 | status = acpi_hw_low_set_gpe(gpe_event_info, | ||
442 | ACPI_GPE_DISABLE); | ||
443 | } | ||
444 | |||
398 | if (ACPI_FAILURE(status)) { | 445 | if (ACPI_FAILURE(status)) { |
399 | gpe_event_info->runtime_count++; | 446 | gpe_event_info->runtime_count++; |
400 | goto unlock_and_exit; | 447 | goto unlock_and_exit; |
@@ -415,7 +462,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type | |||
415 | 462 | ||
416 | gpe_event_info->wakeup_count--; | 463 | gpe_event_info->wakeup_count--; |
417 | if (!gpe_event_info->wakeup_count) { | 464 | if (!gpe_event_info->wakeup_count) { |
418 | (void)acpi_ev_update_gpe_enable_masks(gpe_event_info); | 465 | status = acpi_ev_update_gpe_enable_masks(gpe_event_info); |
419 | } | 466 | } |
420 | } | 467 | } |
421 | 468 | ||
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index d989b8e786cc..40388e23e103 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c | |||
@@ -78,23 +78,27 @@ u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info, | |||
78 | 78 | ||
79 | /****************************************************************************** | 79 | /****************************************************************************** |
80 | * | 80 | * |
81 | * FUNCTION: acpi_hw_low_disable_gpe | 81 | * FUNCTION: acpi_hw_low_set_gpe |
82 | * | 82 | * |
83 | * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled | 83 | * PARAMETERS: gpe_event_info - Info block for the GPE to be disabled |
84 | * action - Enable or disable | ||
84 | * | 85 | * |
85 | * RETURN: Status | 86 | * RETURN: Status |
86 | * | 87 | * |
87 | * DESCRIPTION: Disable a single GPE in the enable register. | 88 | * DESCRIPTION: Enable or disable a single GPE in its enable register. |
88 | * | 89 | * |
89 | ******************************************************************************/ | 90 | ******************************************************************************/ |
90 | 91 | ||
91 | acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) | 92 | acpi_status |
93 | acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action) | ||
92 | { | 94 | { |
93 | struct acpi_gpe_register_info *gpe_register_info; | 95 | struct acpi_gpe_register_info *gpe_register_info; |
94 | acpi_status status; | 96 | acpi_status status; |
95 | u32 enable_mask; | 97 | u32 enable_mask; |
96 | u32 register_bit; | 98 | u32 register_bit; |
97 | 99 | ||
100 | ACPI_FUNCTION_ENTRY(); | ||
101 | |||
98 | /* Get the info block for the entire GPE register */ | 102 | /* Get the info block for the entire GPE register */ |
99 | 103 | ||
100 | gpe_register_info = gpe_event_info->register_info; | 104 | gpe_register_info = gpe_event_info->register_info; |
@@ -109,11 +113,23 @@ acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) | |||
109 | return (status); | 113 | return (status); |
110 | } | 114 | } |
111 | 115 | ||
112 | /* Clear just the bit that corresponds to this GPE */ | 116 | /* Set ot clear just the bit that corresponds to this GPE */ |
113 | 117 | ||
114 | register_bit = acpi_hw_gpe_register_bit(gpe_event_info, | 118 | register_bit = acpi_hw_gpe_register_bit(gpe_event_info, |
115 | gpe_register_info); | 119 | gpe_register_info); |
116 | ACPI_CLEAR_BIT(enable_mask, register_bit); | 120 | switch (action) { |
121 | case ACPI_GPE_ENABLE: | ||
122 | ACPI_SET_BIT(enable_mask, register_bit); | ||
123 | break; | ||
124 | |||
125 | case ACPI_GPE_DISABLE: | ||
126 | ACPI_CLEAR_BIT(enable_mask, register_bit); | ||
127 | break; | ||
128 | |||
129 | default: | ||
130 | ACPI_ERROR((AE_INFO, "Invalid action\n")); | ||
131 | return (AE_BAD_PARAMETER); | ||
132 | } | ||
117 | 133 | ||
118 | /* Write the updated enable mask */ | 134 | /* Write the updated enable mask */ |
119 | 135 | ||