diff options
author | Bob Moore <robert.moore@intel.com> | 2012-06-28 22:04:17 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-07-14 11:17:29 -0400 |
commit | 5816b3430c4b5f31d9c35af1da7be721c9518137 (patch) | |
tree | 6d5877c622f05f65cab4040771a6654a5fbf47b3 | |
parent | e40d5940396aa4002feeab8323224b5c7f84246c (diff) |
ACPICA: Add support for implicit notify on multiple devices
Adds basic support to allow multiple devices to be implicitly
notified.
This change is partially derived from original commit 981858b("ACPI /
ACPICA: Implicit notify for multiple devices") by Rafael.
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jung-uk Kim <jkim@freebsd.org>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | drivers/acpi/acpica/aclocal.h | 11 | ||||
-rw-r--r-- | drivers/acpi/acpica/evgpe.c | 22 | ||||
-rw-r--r-- | drivers/acpi/acpica/evgpeutil.c | 20 | ||||
-rw-r--r-- | drivers/acpi/acpica/evxfgpe.c | 106 |
4 files changed, 109 insertions, 50 deletions
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index af7330f4a52d..6b225e810f3a 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h | |||
@@ -404,6 +404,13 @@ struct acpi_gpe_handler_info { | |||
404 | u8 originally_enabled; /* True if GPE was originally enabled */ | 404 | u8 originally_enabled; /* True if GPE was originally enabled */ |
405 | }; | 405 | }; |
406 | 406 | ||
407 | /* Notify info for implicit notify, multiple device objects */ | ||
408 | |||
409 | struct acpi_gpe_notify_info { | ||
410 | struct acpi_namespace_node *device_node; /* Device to be notified */ | ||
411 | struct acpi_gpe_notify_info *next; | ||
412 | }; | ||
413 | |||
407 | struct acpi_gpe_notify_object { | 414 | struct acpi_gpe_notify_object { |
408 | struct acpi_namespace_node *node; | 415 | struct acpi_namespace_node *node; |
409 | struct acpi_gpe_notify_object *next; | 416 | struct acpi_gpe_notify_object *next; |
@@ -412,7 +419,7 @@ struct acpi_gpe_notify_object { | |||
412 | union acpi_gpe_dispatch_info { | 419 | union acpi_gpe_dispatch_info { |
413 | struct acpi_namespace_node *method_node; /* Method node for this GPE level */ | 420 | struct acpi_namespace_node *method_node; /* Method node for this GPE level */ |
414 | struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ | 421 | struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ |
415 | struct acpi_gpe_notify_object device; /* List of _PRW devices for implicit notify */ | 422 | struct acpi_gpe_notify_info *notify_list; /* List of _PRW devices for implicit notifies */ |
416 | }; | 423 | }; |
417 | 424 | ||
418 | /* | 425 | /* |
@@ -420,7 +427,7 @@ union acpi_gpe_dispatch_info { | |||
420 | * NOTE: Important to keep this struct as small as possible. | 427 | * NOTE: Important to keep this struct as small as possible. |
421 | */ | 428 | */ |
422 | struct acpi_gpe_event_info { | 429 | struct acpi_gpe_event_info { |
423 | union acpi_gpe_dispatch_info dispatch; /* Either Method or Handler */ | 430 | union acpi_gpe_dispatch_info dispatch; /* Either Method, Handler, or notify_list */ |
424 | struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ | 431 | struct acpi_gpe_register_info *register_info; /* Backpointer to register info */ |
425 | u8 flags; /* Misc info about this GPE */ | 432 | u8 flags; /* Misc info about this GPE */ |
426 | u8 gpe_number; /* This GPE */ | 433 | u8 gpe_number; /* This GPE */ |
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 8ba0e5f17091..afbd5cb391f6 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c | |||
@@ -466,7 +466,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) | |||
466 | acpi_status status; | 466 | acpi_status status; |
467 | struct acpi_gpe_event_info *local_gpe_event_info; | 467 | struct acpi_gpe_event_info *local_gpe_event_info; |
468 | struct acpi_evaluate_info *info; | 468 | struct acpi_evaluate_info *info; |
469 | struct acpi_gpe_notify_object *notify_object; | 469 | struct acpi_gpe_notify_info *notify; |
470 | 470 | ||
471 | ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); | 471 | ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); |
472 | 472 | ||
@@ -517,17 +517,17 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) | |||
517 | * completes. The notify handlers are NOT invoked synchronously | 517 | * completes. The notify handlers are NOT invoked synchronously |
518 | * from this thread -- because handlers may in turn run other | 518 | * from this thread -- because handlers may in turn run other |
519 | * control methods. | 519 | * control methods. |
520 | * | ||
521 | * June 2012: Expand implicit notify mechanism to support | ||
522 | * notifies on multiple device objects. | ||
520 | */ | 523 | */ |
521 | status = acpi_ev_queue_notify_request( | 524 | notify = local_gpe_event_info->dispatch.notify_list; |
522 | local_gpe_event_info->dispatch.device.node, | 525 | while (ACPI_SUCCESS(status) && notify) { |
523 | ACPI_NOTIFY_DEVICE_WAKE); | 526 | status = |
524 | 527 | acpi_ev_queue_notify_request(notify->device_node, | |
525 | notify_object = local_gpe_event_info->dispatch.device.next; | 528 | ACPI_NOTIFY_DEVICE_WAKE); |
526 | while (ACPI_SUCCESS(status) && notify_object) { | 529 | |
527 | status = acpi_ev_queue_notify_request( | 530 | notify = notify->next; |
528 | notify_object->node, | ||
529 | ACPI_NOTIFY_DEVICE_WAKE); | ||
530 | notify_object = notify_object->next; | ||
531 | } | 531 | } |
532 | 532 | ||
533 | break; | 533 | break; |
diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 3c43796b8361..0c33c62bd9eb 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c | |||
@@ -347,6 +347,8 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | |||
347 | void *context) | 347 | void *context) |
348 | { | 348 | { |
349 | struct acpi_gpe_event_info *gpe_event_info; | 349 | struct acpi_gpe_event_info *gpe_event_info; |
350 | struct acpi_gpe_notify_info *notify; | ||
351 | struct acpi_gpe_notify_info *next; | ||
350 | u32 i; | 352 | u32 i; |
351 | u32 j; | 353 | u32 j; |
352 | 354 | ||
@@ -365,10 +367,28 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | |||
365 | 367 | ||
366 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == | 368 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == |
367 | ACPI_GPE_DISPATCH_HANDLER) { | 369 | ACPI_GPE_DISPATCH_HANDLER) { |
370 | |||
371 | /* Delete an installed handler block */ | ||
372 | |||
368 | ACPI_FREE(gpe_event_info->dispatch.handler); | 373 | ACPI_FREE(gpe_event_info->dispatch.handler); |
369 | gpe_event_info->dispatch.handler = NULL; | 374 | gpe_event_info->dispatch.handler = NULL; |
370 | gpe_event_info->flags &= | 375 | gpe_event_info->flags &= |
371 | ~ACPI_GPE_DISPATCH_MASK; | 376 | ~ACPI_GPE_DISPATCH_MASK; |
377 | } else if ((gpe_event_info-> | ||
378 | flags & ACPI_GPE_DISPATCH_MASK) == | ||
379 | ACPI_GPE_DISPATCH_NOTIFY) { | ||
380 | |||
381 | /* Delete the implicit notification device list */ | ||
382 | |||
383 | notify = gpe_event_info->dispatch.notify_list; | ||
384 | while (notify) { | ||
385 | next = notify->next; | ||
386 | ACPI_FREE(notify); | ||
387 | notify = next; | ||
388 | } | ||
389 | gpe_event_info->dispatch.notify_list = NULL; | ||
390 | gpe_event_info->flags &= | ||
391 | ~ACPI_GPE_DISPATCH_MASK; | ||
372 | } | 392 | } |
373 | } | 393 | } |
374 | } | 394 | } |
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 86f9b343ebd4..2ce44099eb84 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c | |||
@@ -197,12 +197,12 @@ acpi_status | |||
197 | acpi_setup_gpe_for_wake(acpi_handle wake_device, | 197 | acpi_setup_gpe_for_wake(acpi_handle wake_device, |
198 | acpi_handle gpe_device, u32 gpe_number) | 198 | acpi_handle gpe_device, u32 gpe_number) |
199 | { | 199 | { |
200 | acpi_status status = AE_BAD_PARAMETER; | 200 | acpi_status status; |
201 | struct acpi_gpe_event_info *gpe_event_info; | 201 | struct acpi_gpe_event_info *gpe_event_info; |
202 | struct acpi_namespace_node *device_node; | 202 | struct acpi_namespace_node *device_node; |
203 | struct acpi_gpe_notify_object *notify_object; | 203 | struct acpi_gpe_notify_info *notify; |
204 | struct acpi_gpe_notify_info *new_notify; | ||
204 | acpi_cpu_flags flags; | 205 | acpi_cpu_flags flags; |
205 | u8 gpe_dispatch_mask; | ||
206 | 206 | ||
207 | ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); | 207 | ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); |
208 | 208 | ||
@@ -216,63 +216,95 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, | |||
216 | return_ACPI_STATUS(AE_BAD_PARAMETER); | 216 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
217 | } | 217 | } |
218 | 218 | ||
219 | /* Handle root object case */ | ||
220 | |||
221 | if (wake_device == ACPI_ROOT_OBJECT) { | ||
222 | device_node = acpi_gbl_root_node; | ||
223 | } else { | ||
224 | device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); | ||
225 | } | ||
226 | |||
227 | /* Validate WakeDevice is of type Device */ | ||
228 | |||
229 | if (device_node->type != ACPI_TYPE_DEVICE) { | ||
230 | return_ACPI_STATUS (AE_BAD_PARAMETER); | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Allocate a new notify object up front, in case it is needed. | ||
235 | * Memory allocation while holding a spinlock is a big no-no | ||
236 | * on some hosts. | ||
237 | */ | ||
238 | new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info)); | ||
239 | if (!new_notify) { | ||
240 | return_ACPI_STATUS(AE_NO_MEMORY); | ||
241 | } | ||
242 | |||
219 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); | 243 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); |
220 | 244 | ||
221 | /* Ensure that we have a valid GPE number */ | 245 | /* Ensure that we have a valid GPE number */ |
222 | 246 | ||
223 | gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); | 247 | gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); |
224 | if (!gpe_event_info) { | 248 | if (!gpe_event_info) { |
249 | status = AE_BAD_PARAMETER; | ||
225 | goto unlock_and_exit; | 250 | goto unlock_and_exit; |
226 | } | 251 | } |
227 | 252 | ||
228 | if (wake_device == ACPI_ROOT_OBJECT) { | ||
229 | goto out; | ||
230 | } | ||
231 | |||
232 | /* | 253 | /* |
233 | * If there is no method or handler for this GPE, then the | 254 | * If there is no method or handler for this GPE, then the |
234 | * wake_device will be notified whenever this GPE fires (aka | 255 | * wake_device will be notified whenever this GPE fires. This is |
235 | * "implicit notify") Note: The GPE is assumed to be | 256 | * known as an "implicit notify". Note: The GPE is assumed to be |
236 | * level-triggered (for windows compatibility). | 257 | * level-triggered (for windows compatibility). |
237 | */ | 258 | */ |
238 | gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK; | 259 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == |
239 | if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE | 260 | ACPI_GPE_DISPATCH_NONE) { |
240 | && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) { | 261 | /* |
241 | goto out; | 262 | * This is the first device for implicit notify on this GPE. |
242 | } | 263 | * Just set the flags here, and enter the NOTIFY block below. |
243 | 264 | */ | |
244 | /* Validate wake_device is of type Device */ | 265 | gpe_event_info->flags = |
245 | 266 | (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED); | |
246 | device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); | ||
247 | if (device_node->type != ACPI_TYPE_DEVICE) { | ||
248 | goto unlock_and_exit; | ||
249 | } | 267 | } |
250 | 268 | ||
251 | if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) { | 269 | /* |
252 | gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY | | 270 | * If we already have an implicit notify on this GPE, add |
253 | ACPI_GPE_LEVEL_TRIGGERED); | 271 | * this device to the notify list. |
254 | gpe_event_info->dispatch.device.node = device_node; | 272 | */ |
255 | gpe_event_info->dispatch.device.next = NULL; | 273 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == |
256 | } else { | 274 | ACPI_GPE_DISPATCH_NOTIFY) { |
257 | /* There are multiple devices to notify implicitly. */ | 275 | |
258 | 276 | /* Ensure that the device is not already in the list */ | |
259 | notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object)); | 277 | |
260 | if (!notify_object) { | 278 | notify = gpe_event_info->dispatch.notify_list; |
261 | status = AE_NO_MEMORY; | 279 | while (notify) { |
262 | goto unlock_and_exit; | 280 | if (notify->device_node == device_node) { |
281 | status = AE_ALREADY_EXISTS; | ||
282 | goto unlock_and_exit; | ||
283 | } | ||
284 | notify = notify->next; | ||
263 | } | 285 | } |
264 | 286 | ||
265 | notify_object->node = device_node; | 287 | /* Add this device to the notify list for this GPE */ |
266 | notify_object->next = gpe_event_info->dispatch.device.next; | 288 | |
267 | gpe_event_info->dispatch.device.next = notify_object; | 289 | new_notify->device_node = device_node; |
290 | new_notify->next = gpe_event_info->dispatch.notify_list; | ||
291 | gpe_event_info->dispatch.notify_list = new_notify; | ||
292 | new_notify = NULL; | ||
268 | } | 293 | } |
269 | 294 | ||
270 | out: | 295 | /* Mark the GPE as a possible wake event */ |
296 | |||
271 | gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; | 297 | gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; |
272 | status = AE_OK; | 298 | status = AE_OK; |
273 | 299 | ||
274 | unlock_and_exit: | 300 | unlock_and_exit: |
275 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | 301 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); |
302 | |||
303 | /* Delete the notify object if it was not used above */ | ||
304 | |||
305 | if (new_notify) { | ||
306 | ACPI_FREE(new_notify); | ||
307 | } | ||
276 | return_ACPI_STATUS(status); | 308 | return_ACPI_STATUS(status); |
277 | } | 309 | } |
278 | ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) | 310 | ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake) |