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

Commit 9630bdd9 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Jesse Barnes
Browse files

ACPI: Use GPE reference counting to support shared GPEs



ACPI GPEs may map to multiple devices.  The current GPE interface
only provides a mechanism for enabling and disabling GPEs, making
it difficult to change the state of GPEs at runtime without extensive
cooperation between devices.

Add an API to allow devices to indicate whether or not they want
their device's GPE to be enabled for both runtime and wakeup events.

Remove the old GPE type handling entirely, which gets rid of various
quirks, like the implicit disabling with GPE type setting. This
requires a small amount of rework in order to ensure that non-wake
GPEs are enabled by default to preserve existing behaviour.

Based on patches from Matthew Garrett <mjg@redhat.com>.

Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent c39fae14
Loading
Loading
Loading
Loading
+1 −5
Original line number Diff line number Diff line
@@ -76,8 +76,7 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node *node,
 * evgpe - GPE handling and dispatch
 */
acpi_status
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
				u8 type);
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,
@@ -121,9 +120,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info,

u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list);

acpi_status
acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type);

acpi_status
acpi_ev_check_for_wake_only_gpe(struct acpi_gpe_event_info *gpe_event_info);

+2 −0
Original line number Diff line number Diff line
@@ -426,6 +426,8 @@ struct acpi_gpe_event_info {
	struct acpi_gpe_register_info *register_info;	/* Backpointer to register info */
	u8 flags;		/* Misc info about this GPE */
	u8 gpe_number;		/* This GPE */
	u8 runtime_count;
	u8 wakeup_count;
};

/* Information about a GPE register pair, one per each status/enable pair in an array */
+16 −137
Original line number Diff line number Diff line
@@ -52,56 +52,11 @@ ACPI_MODULE_NAME("evgpe")
/* Local prototypes */
static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context);

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_set_gpe_type
 *
 * PARAMETERS:  gpe_event_info          - GPE to set
 *              Type                    - New type
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run)
 *
 ******************************************************************************/

acpi_status
acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
{
	acpi_status status;

	ACPI_FUNCTION_TRACE(ev_set_gpe_type);

	/* Validate type and update register enable masks */

	switch (type) {
	case ACPI_GPE_TYPE_WAKE:
	case ACPI_GPE_TYPE_RUNTIME:
	case ACPI_GPE_TYPE_WAKE_RUN:
		break;

	default:
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	/* Disable the GPE if currently enabled */

	status = acpi_ev_disable_gpe(gpe_event_info);

	/* Clear the type bits and insert the new Type */

	gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK;
	gpe_event_info->flags |= type;
	return_ACPI_STATUS(status);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_update_gpe_enable_masks
 *
 * PARAMETERS:  gpe_event_info          - GPE to update
 *              Type                    - What to do: ACPI_GPE_DISABLE or
 *                                        ACPI_GPE_ENABLE
 *
 * RETURN:      Status
 *
@@ -110,8 +65,7 @@ acpi_ev_set_gpe_type(struct acpi_gpe_event_info *gpe_event_info, u8 type)
 ******************************************************************************/

acpi_status
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
				u8 type)
acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info)
{
	struct acpi_gpe_register_info *gpe_register_info;
	u8 register_bit;
@@ -127,37 +81,14 @@ acpi_ev_update_gpe_enable_masks(struct acpi_gpe_event_info *gpe_event_info,
	    (1 <<
	     (gpe_event_info->gpe_number - gpe_register_info->base_gpe_number));

	/* 1) Disable case. Simply clear all enable bits */

	if (type == ACPI_GPE_DISABLE) {
		ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
			       register_bit);
	ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, register_bit);
	ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
		return_ACPI_STATUS(AE_OK);
	}

	/* 2) Enable case. Set/Clear the appropriate enable bits */

	switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
	case ACPI_GPE_TYPE_WAKE:
		ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
		ACPI_CLEAR_BIT(gpe_register_info->enable_for_run, register_bit);
		break;

	case ACPI_GPE_TYPE_RUNTIME:
		ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
			       register_bit);
	if (gpe_event_info->runtime_count)
		ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
		break;

	case ACPI_GPE_TYPE_WAKE_RUN:
	if (gpe_event_info->wakeup_count)
		ACPI_SET_BIT(gpe_register_info->enable_for_wake, register_bit);
		ACPI_SET_BIT(gpe_register_info->enable_for_run, register_bit);
		break;

	default:
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	return_ACPI_STATUS(AE_OK);
}
@@ -186,48 +117,21 @@ acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info,

	/* Make sure HW enable masks are updated */

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

	/* Mark wake-enabled or HW enable, or both */

	switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
	case ACPI_GPE_TYPE_WAKE:

		ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
		break;

	case ACPI_GPE_TYPE_WAKE_RUN:

		ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);

		/*lint -fallthrough */

	case ACPI_GPE_TYPE_RUNTIME:

		ACPI_SET_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);

		if (write_to_hardware) {

	if (gpe_event_info->runtime_count && write_to_hardware) {
		/* Clear the GPE (of stale events), then enable it */

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

		/* Enable the requested runtime GPE */

		status = acpi_hw_write_gpe_enable_reg(gpe_event_info);
	}
		break;

	default:
		return_ACPI_STATUS(AE_BAD_PARAMETER);
	}

	return_ACPI_STATUS(AE_OK);
}
@@ -252,34 +156,9 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info)

	/* Make sure HW enable masks are updated */

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

	/* Clear the appropriate enabled flags for this GPE */

	switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) {
	case ACPI_GPE_TYPE_WAKE:
		ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);
		break;

	case ACPI_GPE_TYPE_WAKE_RUN:
		ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED);

		/* fallthrough */

	case ACPI_GPE_TYPE_RUNTIME:

		/* Disable the requested runtime GPE */

		ACPI_CLEAR_BIT(gpe_event_info->flags, ACPI_GPE_RUN_ENABLED);
		break;

	default:
		break;
	}

	/*
	 * Even if we don't know the GPE type, make sure that we always
+33 −54
Original line number Diff line number Diff line
@@ -258,7 +258,6 @@ acpi_ev_save_method_info(acpi_handle obj_handle,
	u32 gpe_number;
	char name[ACPI_NAME_SIZE + 1];
	u8 type;
	acpi_status status;

	ACPI_FUNCTION_TRACE(ev_save_method_info);

@@ -325,26 +324,20 @@ acpi_ev_save_method_info(acpi_handle obj_handle,

	/*
	 * Now we can add this information to the gpe_event_info block for use
	 * during dispatch of this GPE. Default type is RUNTIME, although this may
	 * change when the _PRW methods are executed later.
	 * during dispatch of this GPE.
	 */
	gpe_event_info =
	    &gpe_block->event_info[gpe_number - gpe_block->block_base_number];

	gpe_event_info->flags = (u8)
	    (type | ACPI_GPE_DISPATCH_METHOD | ACPI_GPE_TYPE_RUNTIME);
	gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD);

	gpe_event_info->dispatch.method_node =
	    (struct acpi_namespace_node *)obj_handle;

	/* Update enable mask, but don't enable the HW GPE as of yet */

	status = acpi_ev_enable_gpe(gpe_event_info, FALSE);

	ACPI_DEBUG_PRINT((ACPI_DB_LOAD,
			  "Registered GPE method %s as GPE number 0x%.2X\n",
			  name, gpe_number));
	return_ACPI_STATUS(status);
	return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
@@ -454,20 +447,7 @@ acpi_ev_match_prw_and_gpe(acpi_handle obj_handle,
							gpe_block->
							block_base_number];

		/* Mark GPE for WAKE-ONLY but WAKE_DISABLED */

		gpe_event_info->flags &=
		    ~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED);

		status =
		    acpi_ev_set_gpe_type(gpe_event_info, ACPI_GPE_TYPE_WAKE);
		if (ACPI_FAILURE(status)) {
			goto cleanup;
		}

		status =
		    acpi_ev_update_gpe_enable_masks(gpe_event_info,
						    ACPI_GPE_DISABLE);
		gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
	}

      cleanup:
@@ -989,7 +969,6 @@ acpi_status
acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
			     struct acpi_gpe_block_info *gpe_block)
{
	acpi_status status;
	struct acpi_gpe_event_info *gpe_event_info;
	struct acpi_gpe_walk_info gpe_info;
	u32 wake_gpe_count;
@@ -1019,7 +998,6 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
		gpe_info.gpe_block = gpe_block;
		gpe_info.gpe_device = gpe_device;

		status =
		acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
					   ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
					   acpi_ev_match_prw_and_gpe, NULL,
@@ -1027,34 +1005,43 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
	}

	/*
	 * Enable all GPEs in this block that have these attributes:
	 * 1) are "runtime" or "run/wake" GPEs, and
	 * 2) have a corresponding _Lxx or _Exx method
	 *
	 * Any other GPEs within this block must be enabled via the
	 * acpi_enable_gpe() external interface.
	 * Enable all GPEs that have a corresponding method and aren't
	 * capable of generating wakeups. Any other GPEs within this block
	 * must be enabled via the acpi_enable_gpe() interface.
	 */
	wake_gpe_count = 0;
	gpe_enabled_count = 0;
	if (gpe_device == acpi_gbl_fadt_gpe_device)
		gpe_device = NULL;

	for (i = 0; i < gpe_block->register_count; i++) {
		for (j = 0; j < 8; j++) {
		for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
			acpi_status status;
			acpi_size gpe_index;
			int gpe_number;

			/* Get the info block for this particular GPE */
			gpe_index = (acpi_size)i * ACPI_GPE_REGISTER_WIDTH + j;
			gpe_event_info = &gpe_block->event_info[gpe_index];

			gpe_event_info = &gpe_block->event_info[((acpi_size) i *
								 ACPI_GPE_REGISTER_WIDTH)
								+ j];

			if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
			     ACPI_GPE_DISPATCH_METHOD) &&
			    (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) {
				gpe_enabled_count++;
			}

			if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) {
			if (gpe_event_info->flags & ACPI_GPE_CAN_WAKE) {
				wake_gpe_count++;
				if (acpi_gbl_leave_wake_gpes_disabled)
					continue;
			}

			if (!(gpe_event_info->flags & ACPI_GPE_DISPATCH_METHOD))
				continue;

			gpe_number = gpe_index + gpe_block->block_base_number;
			status = acpi_enable_gpe(gpe_device, gpe_number,
						ACPI_GPE_TYPE_RUNTIME);
			if (ACPI_FAILURE(status))
				ACPI_ERROR((AE_INFO,
						"Failed to enable GPE %02X\n",
						gpe_number));
			else
				gpe_enabled_count++;
		}
	}

@@ -1062,15 +1049,7 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
			  "Found %u Wake, Enabled %u Runtime GPEs in this block\n",
			  wake_gpe_count, gpe_enabled_count));

	/* Enable all valid runtime GPEs found above */

	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));
	}

	return_ACPI_STATUS(status);
	return_ACPI_STATUS(AE_OK);
}

/*******************************************************************************
+0 −14
Original line number Diff line number Diff line
@@ -617,13 +617,6 @@ acpi_install_gpe_handler(acpi_handle gpe_device,
	handler->context = context;
	handler->method_node = gpe_event_info->dispatch.method_node;

	/* Disable the GPE before installing the handler */

	status = acpi_ev_disable_gpe(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		goto unlock_and_exit;
	}

	/* Install the handler */

	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
@@ -707,13 +700,6 @@ acpi_remove_gpe_handler(acpi_handle gpe_device,
		goto unlock_and_exit;
	}

	/* Disable the GPE before removing the handler */

	status = acpi_ev_disable_gpe(gpe_event_info);
	if (ACPI_FAILURE(status)) {
		goto unlock_and_exit;
	}

	/* Make sure all deferred tasks are completed */

	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
Loading