Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd247447 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Len Brown
Browse files

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: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent e4e9a735
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -78,10 +78,6 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
acpi_status
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info);

acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);

acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);

struct acpi_gpe_event_info *acpi_ev_get_gpe_event_info(acpi_handle gpe_device,
						       u32 gpe_number);

+2 −1
Original line number Diff line number Diff line
@@ -93,7 +93,8 @@ acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width);
u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info,
			     struct acpi_gpe_register_info *gpe_register_info);

acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info);
acpi_status
acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action);

acpi_status
acpi_hw_write_gpe_enable_reg(struct acpi_gpe_event_info *gpe_event_info);
+2 −106
Original line number Diff line number Diff line
@@ -99,106 +99,6 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info)
	return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_enable_gpe
 *
 * PARAMETERS:  gpe_event_info          - GPE to enable
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Hardware-enable a GPE. Always enables the GPE, regardless
 *              of type or number of references.
 *
 * Note: The GPE lock should be already acquired when this function is called.
 *
 ******************************************************************************/

acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
{
	acpi_status status;


	ACPI_FUNCTION_TRACE(ev_enable_gpe);


	/*
	 * We will only allow a GPE to be enabled if it has either an
	 * associated method (_Lxx/_Exx) or a handler. Otherwise, the
	 * GPE will be immediately disabled by acpi_ev_gpe_dispatch the
	 * first time it fires.
	 */
	if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) {
		return_ACPI_STATUS(AE_NO_HANDLER);
	}

	/* Ensure the HW enable masks are current */

	status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	/* Clear the GPE (of stale events) */

	status = acpi_hw_clear_gpe(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	/* Enable the requested GPE */

	status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
	return_ACPI_STATUS(status);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_disable_gpe
 *
 * PARAMETERS:  gpe_event_info          - GPE to disable
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Hardware-disable a GPE. Always disables the requested GPE,
 *              regardless of the type or number of references.
 *
 * Note: The GPE lock should be already acquired when this function is called.
 *
 ******************************************************************************/

acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
{
	acpi_status status;

	ACPI_FUNCTION_TRACE(ev_disable_gpe);


	/*
	 * Note: Always disable the GPE, even if we think that that it is already
	 * disabled. It is possible that the AML or some other code has enabled
	 * the GPE behind our back.
	 */

	/* Ensure the HW enable masks are current */

	status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	/*
	 * Always H/W disable this GPE, even if we don't know the GPE type.
	 * Simply clear the enable bit for this particular GPE, but do not
	 * write out the current GPE enable mask since this may inadvertently
	 * enable GPEs too early. An example is a rogue GPE that has arrived
	 * during ACPICA initialization - possibly because AML or other code
	 * has enabled the GPE.
	 */
	status = acpi_hw_low_disable_gpe(gpe_event_info);
	return_ACPI_STATUS(status);
}


/*******************************************************************************
 *
@@ -450,10 +350,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context)
		return_VOID;
	}

	/* Update the GPE register masks for return to enabled state */

	(void)acpi_ev_update_gpe_enable_masks(gpe_event_info);

	/*
	 * Take a snapshot of the GPE info for this level - we copy the info to
	 * 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)
		 * Disable the GPE, so it doesn't keep firing before the method has a
		 * chance to run (it runs asynchronously with interrupts enabled).
		 */
		status = acpi_ev_disable_gpe(gpe_event_info);
		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
		if (ACPI_FAILURE(status)) {
			ACPI_EXCEPTION((AE_INFO, status,
					"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)
		 * Disable the GPE. The GPE will remain disabled a handler
		 * is installed or ACPICA is restarted.
		 */
		status = acpi_ev_disable_gpe(gpe_event_info);
		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
		if (ACPI_FAILURE(status)) {
			ACPI_EXCEPTION((AE_INFO, status,
					"Unable to disable GPE[0x%2X]",
+53 −6
Original line number Diff line number Diff line
@@ -199,6 +199,44 @@ acpi_status acpi_enable_event(u32 event, u32 flags)

ACPI_EXPORT_SYMBOL(acpi_enable_event)

/*******************************************************************************
 *
 * FUNCTION:    acpi_clear_and_enable_gpe
 *
 * PARAMETERS:  gpe_event_info  - GPE to enable
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Clear the given GPE from stale events and enable it.
 *
 ******************************************************************************/
static acpi_status
acpi_clear_and_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
{
	acpi_status status;

	/*
	 * We will only allow a GPE to be enabled if it has either an
	 * associated method (_Lxx/_Exx) or a handler. Otherwise, the
	 * GPE will be immediately disabled by acpi_ev_gpe_dispatch the
	 * first time it fires.
	 */
	if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK)) {
		return_ACPI_STATUS(AE_NO_HANDLER);
	}

	/* Clear the GPE (of stale events) */
	status = acpi_hw_clear_gpe(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		return_ACPI_STATUS(status);
	}

	/* Enable the requested GPE */
	status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);

	return_ACPI_STATUS(status);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_set_gpe
@@ -240,11 +278,11 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)

	switch (action) {
	case ACPI_GPE_ENABLE:
		status = acpi_ev_enable_gpe(gpe_event_info);
		status = acpi_clear_and_enable_gpe(gpe_event_info);
		break;

	case ACPI_GPE_DISABLE:
		status = acpi_ev_disable_gpe(gpe_event_info);
		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
		break;

	default:
@@ -307,7 +345,11 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type)

		gpe_event_info->runtime_count++;
		if (gpe_event_info->runtime_count == 1) {
			status = acpi_ev_enable_gpe(gpe_event_info);
			status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
			if (ACPI_SUCCESS(status)) {
				status = acpi_clear_and_enable_gpe(gpe_event_info);
			}

			if (ACPI_FAILURE(status)) {
				gpe_event_info->runtime_count--;
				goto unlock_and_exit;
@@ -334,7 +376,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type)
		 */
		gpe_event_info->wakeup_count++;
		if (gpe_event_info->wakeup_count == 1) {
			(void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
			status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
		}
	}

@@ -394,7 +436,12 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type

		gpe_event_info->runtime_count--;
		if (!gpe_event_info->runtime_count) {
			status = acpi_ev_disable_gpe(gpe_event_info);
			status = acpi_ev_update_gpe_enable_masks(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++;
				goto unlock_and_exit;
@@ -415,7 +462,7 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u8 gpe_type

		gpe_event_info->wakeup_count--;
		if (!gpe_event_info->wakeup_count) {
			(void)acpi_ev_update_gpe_enable_masks(gpe_event_info);
			status = acpi_ev_update_gpe_enable_masks(gpe_event_info);
		}
	}

+21 −5
Original line number Diff line number Diff line
@@ -78,23 +78,27 @@ u32 acpi_hw_gpe_register_bit(struct acpi_gpe_event_info *gpe_event_info,

/******************************************************************************
 *
 * FUNCTION:	acpi_hw_low_disable_gpe
 * FUNCTION:	acpi_hw_low_set_gpe
 *
 * PARAMETERS:	gpe_event_info	    - Info block for the GPE to be disabled
 *		action		    - Enable or disable
 *
 * RETURN:	Status
 *
 * DESCRIPTION: Disable a single GPE in the enable register.
 * DESCRIPTION: Enable or disable a single GPE in its enable register.
 *
 ******************************************************************************/

acpi_status acpi_hw_low_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)
acpi_status
acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 action)
{
	struct acpi_gpe_register_info *gpe_register_info;
	acpi_status status;
	u32 enable_mask;
	u32 register_bit;

	ACPI_FUNCTION_ENTRY();

	/* Get the info block for the entire GPE register */

	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)
		return (status);
	}

	/* Clear just the bit that corresponds to this GPE */
	/* Set ot clear just the bit that corresponds to this GPE */

	register_bit = acpi_hw_gpe_register_bit(gpe_event_info,
						gpe_register_info);
	switch (action) {
	case ACPI_GPE_ENABLE:
		ACPI_SET_BIT(enable_mask, register_bit);
		break;

	case ACPI_GPE_DISABLE:
		ACPI_CLEAR_BIT(enable_mask, register_bit);
		break;

	default:
		ACPI_ERROR((AE_INFO, "Invalid action\n"));
		return (AE_BAD_PARAMETER);
	}

	/* Write the updated enable mask */

Loading