ACPICA: Add support for host-installed SCI handlers.
authorLv Zheng <lv.zheng@intel.com>
Mon, 23 Sep 2013 01:52:05 +0000 (09:52 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 23 Sep 2013 23:46:24 +0000 (01:46 +0200)
This change adds support to allow hosts to install System Control
Interrupt handlers. Certain ACPI functionality requires the host
to handle raw SCIs. For example, the "SCI Doorbell" that is defined
for memory power state support requires the host device driver to
handle SCIs to examine if the doorbell has been activated. Multiple
SCI handlers can be installed to allow for future expansion.
Debugger support is included.
Lv Zheng, Bob Moore. ACPICA BZ 1032.

Bug summary:
It is reported when the PCC (Platform Communication Channel, via
MPST table, defined in ACPI specification 5.0) subchannel responds
to the host, it issues an SCI and the host must probe the subchannel
for channel status.

Buglink: http://bugs.acpica.org/show_bug.cgi?id=1032
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Reviewed-by: Len Brown <len.brown@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/acpica/acdebug.h
drivers/acpi/acpica/acevents.h
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/evgpeutil.c
drivers/acpi/acpica/evmisc.c
drivers/acpi/acpica/evsci.c
drivers/acpi/acpica/evxface.c
drivers/acpi/acpica/utglobal.c
include/acpi/acpixf.h
include/acpi/actypes.h

index 9feba08c29fe08b772e976fdefc7531300eb4d2d..30c2d691138678fd9ed646feaf6d710aad07f032 100644 (file)
@@ -113,11 +113,12 @@ void acpi_db_display_handlers(void);
 ACPI_HW_DEPENDENT_RETURN_VOID(void
                              acpi_db_generate_gpe(char *gpe_arg,
                                                   char *block_arg))
+ ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_db_generate_sci(void))
 
 /*
  * dbconvert - miscellaneous conversion routines
  */
- acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value);
+acpi_status acpi_db_hex_char_to_value(int hex_char, u8 *return_value);
 
 acpi_status acpi_db_convert_to_package(char *string, union acpi_object *object);
 
index ab0e9771038127c83365fc3b1d72de841612fb99..3ae5fd02ae649f46677b362af9aa4cd39c4e6e3b 100644 (file)
@@ -242,11 +242,11 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
  */
 u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context);
 
-u32 acpi_ev_install_sci_handler(void);
+u32 acpi_ev_sci_dispatch(void);
 
-acpi_status acpi_ev_remove_sci_handler(void);
+u32 acpi_ev_install_sci_handler(void);
 
-u32 acpi_ev_initialize_SCI(u32 program_SCI);
+acpi_status acpi_ev_remove_all_sci_handlers(void);
 
 ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_ev_terminate(void))
 #endif                         /* __ACEVENTS_H__  */
index c9ad2e1f1ee1a16ae105c37203caa2e2a8966780..0fba431f4fcb90fd1bad8ec85b870486a33b70d6 100644 (file)
@@ -269,6 +269,7 @@ ACPI_EXTERN acpi_table_handler acpi_gbl_table_handler;
 ACPI_EXTERN void *acpi_gbl_table_handler_context;
 ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk;
 ACPI_EXTERN acpi_interface_handler acpi_gbl_interface_handler;
+ACPI_EXTERN struct acpi_sci_handler_info *acpi_gbl_sci_handler_list;
 
 /* Owner ID support */
 
index 0ed00669cd217a0754087ed61bc89465e31804f3..be9e30ee60488b7de09a61ea1dd198fe84ebf43b 100644 (file)
@@ -398,6 +398,14 @@ struct acpi_simple_repair_info {
  *
  ****************************************************************************/
 
+/* Dispatch info for each host-installed SCI handler */
+
+struct acpi_sci_handler_info {
+       struct acpi_sci_handler_info *next;
+       acpi_sci_handler address;       /* Address of handler */
+       void *context;          /* Context to be passed to handler */
+};
+
 /* Dispatch info for each GPE -- either a method or handler, cannot be both */
 
 struct acpi_gpe_handler_info {
index b24dbb80fab8f681dc3089cd8a29c700f5cb57c7..d52339090b604ba1569f599a03a45e6712e54ee9 100644 (file)
@@ -196,7 +196,7 @@ acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
  *
  * FUNCTION:    acpi_ev_get_gpe_xrupt_block
  *
- * PARAMETERS:  interrupt_number     - Interrupt for a GPE block
+ * PARAMETERS:  interrupt_number            - Interrupt for a GPE block
  *
  * RETURN:      A GPE interrupt block
  *
index 1b111ef74903771f8f9a8f690aef85b752f65c61..a5687540e9a66e5ba20bfeb9772ad5351b20bc61 100644 (file)
@@ -264,13 +264,6 @@ void acpi_ev_terminate(void)
 
                status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL);
 
-               /* Remove SCI handler */
-
-               status = acpi_ev_remove_sci_handler();
-               if (ACPI_FAILURE(status)) {
-                       ACPI_ERROR((AE_INFO, "Could not remove SCI handler"));
-               }
-
                status = acpi_ev_remove_global_lock_handler();
                if (ACPI_FAILURE(status)) {
                        ACPI_ERROR((AE_INFO,
@@ -280,6 +273,13 @@ void acpi_ev_terminate(void)
                acpi_gbl_events_initialized = FALSE;
        }
 
+       /* Remove SCI handlers */
+
+       status = acpi_ev_remove_all_sci_handlers();
+       if (ACPI_FAILURE(status)) {
+               ACPI_ERROR((AE_INFO, "Could not remove SCI handler"));
+       }
+
        /* Deallocate all handler objects installed within GPE info structs */
 
        status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL);
index b905acf7aacdc1d90f9189b9660df28eb6777f08..b2f0fb2f57b44f86998d8121ac16b0f27b2bdcc2 100644 (file)
@@ -52,6 +52,52 @@ ACPI_MODULE_NAME("evsci")
 /* Local prototypes */
 static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context);
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_sci_dispatch
+ *
+ * PARAMETERS:  None
+ *
+ * RETURN:      Status code indicates whether interrupt was handled.
+ *
+ * DESCRIPTION: Dispatch the SCI to all host-installed SCI handlers.
+ *
+ ******************************************************************************/
+
+u32 acpi_ev_sci_dispatch(void)
+{
+       struct acpi_sci_handler_info *sci_handler;
+       acpi_cpu_flags flags;
+       u32 int_status = ACPI_INTERRUPT_NOT_HANDLED;
+
+       ACPI_FUNCTION_NAME(ev_sci_dispatch);
+
+       /* Are there any host-installed SCI handlers? */
+
+       if (!acpi_gbl_sci_handler_list) {
+               return (int_status);
+       }
+
+       flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+       /* Invoke all host-installed SCI handlers */
+
+       sci_handler = acpi_gbl_sci_handler_list;
+       while (sci_handler) {
+
+               /* Invoke the installed handler (at interrupt level) */
+
+               int_status |= sci_handler->address((u32)acpi_gbl_FADT.
+                                                  sci_interrupt,
+                                                  sci_handler->context);
+
+               sci_handler = sci_handler->next;
+       }
+
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+       return (int_status);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ev_sci_xrupt_handler
@@ -89,6 +135,10 @@ static u32 ACPI_SYSTEM_XFACE acpi_ev_sci_xrupt_handler(void *context)
         */
        interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list);
 
+       /* Invoke all host-installed SCI handlers */
+
+       interrupt_handled |= acpi_ev_sci_dispatch();
+
        return_UINT32(interrupt_handled);
 }
 
@@ -112,14 +162,13 @@ u32 ACPI_SYSTEM_XFACE acpi_ev_gpe_xrupt_handler(void *context)
        ACPI_FUNCTION_TRACE(ev_gpe_xrupt_handler);
 
        /*
-        * We are guaranteed by the ACPI CA initialization/shutdown code that
+        * We are guaranteed by the ACPICA initialization/shutdown code that
         * if this interrupt handler is installed, ACPI is enabled.
         */
 
        /* GPEs: Check for and dispatch any GPEs that have occurred */
 
        interrupt_handled |= acpi_ev_gpe_detect(gpe_xrupt_list);
-
        return_UINT32(interrupt_handled);
 }
 
@@ -150,15 +199,15 @@ u32 acpi_ev_install_sci_handler(void)
 
 /******************************************************************************
  *
- * FUNCTION:    acpi_ev_remove_sci_handler
+ * FUNCTION:    acpi_ev_remove_all_sci_handlers
  *
  * PARAMETERS:  none
  *
- * RETURN:      E_OK if handler uninstalled OK, E_ERROR if handler was not
+ * RETURN:      AE_OK if handler uninstalled, AE_ERROR if handler was not
  *              installed to begin with
  *
  * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be
- *              taken.
+ *              taken. Remove all host-installed SCI handlers.
  *
  * Note:  It doesn't seem important to disable all events or set the event
  *        enable registers to their original values. The OS should disable
@@ -167,11 +216,13 @@ u32 acpi_ev_install_sci_handler(void)
  *
  ******************************************************************************/
 
-acpi_status acpi_ev_remove_sci_handler(void)
+acpi_status acpi_ev_remove_all_sci_handlers(void)
 {
+       struct acpi_sci_handler_info *sci_handler;
+       acpi_cpu_flags flags;
        acpi_status status;
 
-       ACPI_FUNCTION_TRACE(ev_remove_sci_handler);
+       ACPI_FUNCTION_TRACE(ev_remove_all_sci_handlers);
 
        /* Just let the OS remove the handler and disable the level */
 
@@ -179,6 +230,21 @@ acpi_status acpi_ev_remove_sci_handler(void)
            acpi_os_remove_interrupt_handler((u32) acpi_gbl_FADT.sci_interrupt,
                                             acpi_ev_sci_xrupt_handler);
 
+       if (!acpi_gbl_sci_handler_list) {
+               return (status);
+       }
+
+       flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+       /* Free all host-installed SCI handlers */
+
+       while (acpi_gbl_sci_handler_list) {
+               sci_handler = acpi_gbl_sci_handler_list;
+               acpi_gbl_sci_handler_list = sci_handler->next;
+               ACPI_FREE(sci_handler);
+       }
+
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
        return_ACPI_STATUS(status);
 }
 
index ca5fba99c33bc82a302f8eb246ca41224a2cbada..6f56146a6f88a55a7d0ebebaa4be0bbd84126bd0 100644 (file)
@@ -383,6 +383,144 @@ ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
 #endif                         /*  ACPI_FUTURE_USAGE  */
 
 #if (!ACPI_REDUCED_HARDWARE)
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_install_sci_handler
+ *
+ * PARAMETERS:  address             - Address of the handler
+ *              context             - Value passed to the handler on each SCI
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Install a handler for a System Control Interrupt.
+ *
+ ******************************************************************************/
+acpi_status acpi_install_sci_handler(acpi_sci_handler address, void *context)
+{
+       struct acpi_sci_handler_info *new_sci_handler;
+       struct acpi_sci_handler_info *sci_handler;
+       acpi_cpu_flags flags;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_install_sci_handler);
+
+       if (!address) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       /* Allocate and init a handler object */
+
+       new_sci_handler = ACPI_ALLOCATE(sizeof(struct acpi_sci_handler_info));
+       if (!new_sci_handler) {
+               return_ACPI_STATUS(AE_NO_MEMORY);
+       }
+
+       new_sci_handler->address = address;
+       new_sci_handler->context = context;
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               goto exit;
+       }
+
+       /* Lock list during installation */
+
+       flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+       sci_handler = acpi_gbl_sci_handler_list;
+
+       /* Ensure handler does not already exist */
+
+       while (sci_handler) {
+               if (address == sci_handler->address) {
+                       status = AE_ALREADY_EXISTS;
+                       goto unlock_and_exit;
+               }
+
+               sci_handler = sci_handler->next;
+       }
+
+       /* Install the new handler into the global list (at head) */
+
+       new_sci_handler->next = acpi_gbl_sci_handler_list;
+       acpi_gbl_sci_handler_list = new_sci_handler;
+
+      unlock_and_exit:
+
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+
+      exit:
+       if (ACPI_FAILURE(status)) {
+               ACPI_FREE(new_sci_handler);
+       }
+       return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_remove_sci_handler
+ *
+ * PARAMETERS:  address             - Address of the handler
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Remove a handler for a System Control Interrupt.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_remove_sci_handler(acpi_sci_handler address)
+{
+       struct acpi_sci_handler_info *prev_sci_handler;
+       struct acpi_sci_handler_info *next_sci_handler;
+       acpi_cpu_flags flags;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE(acpi_remove_sci_handler);
+
+       if (!address) {
+               return_ACPI_STATUS(AE_BAD_PARAMETER);
+       }
+
+       status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
+       if (ACPI_FAILURE(status)) {
+               return_ACPI_STATUS(status);
+       }
+
+       /* Remove the SCI handler with lock */
+
+       flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+       prev_sci_handler = NULL;
+       next_sci_handler = acpi_gbl_sci_handler_list;
+       while (next_sci_handler) {
+               if (next_sci_handler->address == address) {
+
+                       /* Unlink and free the SCI handler info block */
+
+                       if (prev_sci_handler) {
+                               prev_sci_handler->next = next_sci_handler->next;
+                       } else {
+                               acpi_gbl_sci_handler_list =
+                                   next_sci_handler->next;
+                       }
+
+                       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+                       ACPI_FREE(next_sci_handler);
+                       goto unlock_and_exit;
+               }
+
+               prev_sci_handler = next_sci_handler;
+               next_sci_handler = next_sci_handler->next;
+       }
+
+       acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+       status = AE_NOT_EXIST;
+
+      unlock_and_exit:
+       (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
+       return_ACPI_STATUS(status);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_install_global_event_handler
@@ -398,6 +536,7 @@ ACPI_EXPORT_SYMBOL(acpi_install_exception_handler)
  *              Can be used to update event counters, etc.
  *
  ******************************************************************************/
+
 acpi_status
 acpi_install_global_event_handler(acpi_gbl_event_handler handler, void *context)
 {
index d6f26bf8a0626d0d7497baeaf1ce931d3a76c9ba..046d5b059c07f3ce56b6f35d2c2aac3ea9d59f0d 100644 (file)
@@ -291,7 +291,7 @@ acpi_status acpi_ut_init_globals(void)
 
 #if (!ACPI_REDUCED_HARDWARE)
 
-       /* GPE support */
+       /* GPE/SCI support */
 
        acpi_gbl_all_gpes_initialized = FALSE;
        acpi_gbl_gpe_xrupt_list_head = NULL;
@@ -300,6 +300,7 @@ acpi_status acpi_ut_init_globals(void)
        acpi_current_gpe_count = 0;
 
        acpi_gbl_global_event_handler = NULL;
+       acpi_gbl_sci_handler_list = NULL;
 
 #endif                         /* !ACPI_REDUCED_HARDWARE */
 
index 85bfdbe178052bd2231dce137b1463d174b2bffe..55a4d3ae1477d83d28864048a7a535c01d8ea762 100644 (file)
@@ -280,9 +280,16 @@ acpi_status
 acpi_install_initialization_handler(acpi_init_handler handler, u32 function);
 
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
-                               acpi_install_global_event_handler
-                               (acpi_gbl_event_handler handler, void *context))
-
+                               acpi_install_sci_handler(acpi_sci_handler
+                                                        address,
+                                                        void *context))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_remove_sci_handler(acpi_sci_handler
+                                                        address))
+ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
+                                acpi_install_global_event_handler
+                                (acpi_gbl_event_handler handler,
+                                 void *context))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                 acpi_install_fixed_event_handler(u32
                                                                  acpi_event,
index b748aefce929983cf94d3729db782e62c2843768..850f75027fb6d34529f652e30077cdaaba51f941 100644 (file)
@@ -945,6 +945,9 @@ typedef void
 /*
  * Various handlers and callback procedures
  */
+typedef
+u32 (*acpi_sci_handler) (u32 interrupt_number, void *context);
+
 typedef
 void (*acpi_gbl_event_handler) (u32 event_type,
                               acpi_handle device,