ACPI / ACPICA: Fix reference counting problems with GPE handlers

If a handler is installed for a GPE associated with an AML method and
such that it cannot wake up the system from sleep states, the GPE
remains enabled after the handler has been installed, although it
should be disabled in that case to avoid spurious execution of the
handler.

Fix this issue by making acpi_install_gpe_handler() disable GPEs
that were previously associated with AML methods and cannot wake up
the system from sleep states.

Analogously, make acpi_remove_gpe_handler() enable the GPEs that
are associated with AML methods after their handlers have been
removed and cannot wake up the system from sleep states.  In addition
to that, fix a code ordering issue in acpi_remove_gpe_handler() that
renders the locking ineffective (ACPI_MTX_EVENTS is released
temporarily in the middle of the routine to wait for the completion
of events already in progress).

For this purpose introduce acpi_raw_disable_gpe() and
acpi_raw_enable_gpe() to be called with acpi_gbl_gpe_lock held
and rework acpi_disable_gpe() and acpi_enable_gpe(), respectively, to
use them.  Also rework acpi_gpe_can_wake() to use
acpi_raw_disable_gpe() instead of calling acpi_disable_gpe() after
releasing the lock to avoid the possible theoretical race with
acpi_install_gpe_handler().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: "Moore, Robert" <robert.moore@intel.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 7a6a3e6f..f226eac 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -137,6 +137,79 @@
 
 /*******************************************************************************
  *
+ * FUNCTION:    acpi_raw_enable_gpe
+ *
+ * PARAMETERS:  gpe_event_info  - GPE to enable
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
+ *              hardware-enabled.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_raw_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+	acpi_status status = AE_OK;
+
+	if (gpe_event_info->runtime_count == ACPI_UINT8_MAX) {
+		return_ACPI_STATUS(AE_LIMIT);
+	}
+
+	gpe_event_info->runtime_count++;
+	if (gpe_event_info->runtime_count == 1) {
+		status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
+		if (ACPI_SUCCESS(status)) {
+			status = acpi_ev_enable_gpe(gpe_event_info);
+		}
+
+		if (ACPI_FAILURE(status)) {
+			gpe_event_info->runtime_count--;
+		}
+	}
+
+	return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_raw_disable_gpe
+ *
+ * PARAMETERS:  gpe_event_info  - GPE to disable
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Remove a reference to a GPE. When the last reference is
+ *              removed, the GPE is hardware-disabled.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_raw_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
+{
+	acpi_status status = AE_OK;
+
+	if (!gpe_event_info->runtime_count) {
+		return_ACPI_STATUS(AE_LIMIT);
+	}
+
+	gpe_event_info->runtime_count--;
+	if (!gpe_event_info->runtime_count) {
+		status = acpi_ev_update_gpe_enable_mask(gpe_event_info);
+		if (ACPI_SUCCESS(status)) {
+			status = acpi_hw_low_set_gpe(gpe_event_info,
+						     ACPI_GPE_DISABLE);
+		}
+
+		if (ACPI_FAILURE(status)) {
+			gpe_event_info->runtime_count++;
+		}
+	}
+
+	return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
  * FUNCTION:    acpi_ev_low_get_gpe_info
  *
  * PARAMETERS:  gpe_number          - Raw GPE number