ACPICA: New: acpi_get_gpe_device interface

This function maps an input GPE index to a GPE block device. Also
Added acpi_current_gpe_count to track the current number of GPEs
that are being managed by the ACPICA core (both FADT-based GPEs
and the GPEs contained in GPE block devices.)

Modify drivers/acpi/system.c to use these 2 new interfaces

Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c
index 7537cda..2872be2 100644
--- a/drivers/acpi/events/evgpeblk.c
+++ b/drivers/acpi/events/evgpeblk.c
@@ -124,6 +124,7 @@
  * FUNCTION:    acpi_ev_walk_gpe_list
  *
  * PARAMETERS:  gpe_walk_callback   - Routine called for each GPE block
+ *              Context             - Value passed to callback
  *
  * RETURN:      Status
  *
@@ -131,7 +132,8 @@
  *
  ******************************************************************************/
 
-acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback)
+acpi_status
+acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context)
 {
 	struct acpi_gpe_block_info *gpe_block;
 	struct acpi_gpe_xrupt_info *gpe_xrupt_info;
@@ -154,8 +156,13 @@
 
 			/* One callback per GPE block */
 
-			status = gpe_walk_callback(gpe_xrupt_info, gpe_block);
+			status =
+			    gpe_walk_callback(gpe_xrupt_info, gpe_block,
+					      context);
 			if (ACPI_FAILURE(status)) {
+				if (status == AE_CTRL_END) {	/* Callback abort */
+					status = AE_OK;
+				}
 				goto unlock_and_exit;
 			}
 
@@ -186,7 +193,8 @@
 
 acpi_status
 acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
-			    struct acpi_gpe_block_info *gpe_block)
+			    struct acpi_gpe_block_info *gpe_block,
+			    void *context)
 {
 	struct acpi_gpe_event_info *gpe_event_info;
 	u32 i;
@@ -690,7 +698,8 @@
 
 	/* Disable all GPEs in this block */
 
-	status = acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block);
+	status =
+	    acpi_hw_disable_gpe_block(gpe_block->xrupt_block, gpe_block, NULL);
 
 	if (!gpe_block->previous && !gpe_block->next) {
 
@@ -717,6 +726,9 @@
 		acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
 	}
 
+	acpi_current_gpe_count -=
+	    gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH;
+
 	/* Free the gpe_block */
 
 	ACPI_FREE(gpe_block->register_info);
@@ -958,6 +970,9 @@
 			  gpe_device->name.ascii, gpe_block->register_count,
 			  interrupt_number));
 
+	/* Update global count of currently available GPEs */
+
+	acpi_current_gpe_count += register_count * ACPI_GPE_REGISTER_WIDTH;
 	return_ACPI_STATUS(AE_OK);
 }
 
@@ -1057,7 +1072,7 @@
 
 	/* Enable all valid runtime GPEs found above */
 
-	status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block);
+	status = acpi_hw_enable_runtime_gpe_block(NULL, gpe_block, NULL);
 	if (ACPI_FAILURE(status)) {
 		ACPI_ERROR((AE_INFO, "Could not enable GPEs in GpeBlock %p",
 			    gpe_block));
diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c
index dbac5b3..d807158 100644
--- a/drivers/acpi/events/evmisc.c
+++ b/drivers/acpi/events/evmisc.c
@@ -588,7 +588,7 @@
 
 		/* Disable all GPEs in all GPE blocks */
 
-		status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block);
+		status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL);
 
 		/* Remove SCI handler */
 
@@ -606,7 +606,7 @@
 
 	/* Deallocate all handler objects installed within GPE info structs */
 
-	status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers);
+	status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL);
 
 	/* Return to original mode if necessary */
 
diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c
index 669b8ca..aa4dec8 100644
--- a/drivers/acpi/events/evxfevnt.c
+++ b/drivers/acpi/events/evxfevnt.c
@@ -49,6 +49,11 @@
 #define _COMPONENT          ACPI_EVENTS
 ACPI_MODULE_NAME("evxfevnt")
 
+/* Local prototypes */
+acpi_status
+acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+		       struct acpi_gpe_block_info *gpe_block, void *context);
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_enable
@@ -60,6 +65,7 @@
  * DESCRIPTION: Transfers the system into ACPI mode.
  *
  ******************************************************************************/
+
 acpi_status acpi_enable(void)
 {
 	acpi_status status = AE_OK;
@@ -717,3 +723,90 @@
 }
 
 ACPI_EXPORT_SYMBOL(acpi_remove_gpe_block)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_get_gpe_device
+ *
+ * PARAMETERS:  Index               - System GPE index (0-current_gpe_count)
+ *              gpe_device          - Where the parent GPE Device is returned
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL
+ *              gpe device indicates that the gpe number is contained in one of
+ *              the FADT-defined gpe blocks. Otherwise, the GPE block device.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_get_gpe_device(u32 index, acpi_handle *gpe_device)
+{
+	struct acpi_gpe_device_info info;
+	acpi_status status;
+
+	ACPI_FUNCTION_TRACE(acpi_get_gpe_device);
+
+	if (!gpe_device) {
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
+	}
+
+	if (index >= acpi_current_gpe_count) {
+		return_ACPI_STATUS(AE_NOT_EXIST);
+	}
+
+	/* Setup and walk the GPE list */
+
+	info.index = index;
+	info.status = AE_NOT_EXIST;
+	info.gpe_device = NULL;
+	info.next_block_base_index = 0;
+
+	status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info);
+	if (ACPI_FAILURE(status)) {
+		return_ACPI_STATUS(status);
+	}
+
+	*gpe_device = info.gpe_device;
+	return_ACPI_STATUS(info.status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_get_gpe_device
+ *
+ * PARAMETERS:  GPE_WALK_CALLBACK
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE
+ *              block device. NULL if the GPE is one of the FADT-defined GPEs.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
+		       struct acpi_gpe_block_info *gpe_block, void *context)
+{
+	struct acpi_gpe_device_info *info = context;
+
+	/* Increment Index by the number of GPEs in this block */
+
+	info->next_block_base_index +=
+	    (gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH);
+
+	if (info->index < info->next_block_base_index) {
+		/*
+		 * The GPE index is within this block, get the node. Leave the node
+		 * NULL for the FADT-defined GPEs
+		 */
+		if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) {
+			info->gpe_device = gpe_block->node;
+		}
+
+		info->status = AE_OK;
+		return (AE_CTRL_END);
+	}
+
+	return (AE_OK);
+}