162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: evxfgpe - External Interfaces for General Purpose Events (GPEs)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define EXPORT_ACPI_INTERFACES
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <acpi/acpi.h>
1362306a36Sopenharmony_ci#include "accommon.h"
1462306a36Sopenharmony_ci#include "acevents.h"
1562306a36Sopenharmony_ci#include "acnamesp.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define _COMPONENT          ACPI_EVENTS
1862306a36Sopenharmony_ciACPI_MODULE_NAME("evxfgpe")
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#if (!ACPI_REDUCED_HARDWARE)	/* Entire module */
2162306a36Sopenharmony_ci/*******************************************************************************
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * FUNCTION:    acpi_update_all_gpes
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * PARAMETERS:  None
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * RETURN:      Status
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * DESCRIPTION: Complete GPE initialization and enable all GPEs that have
3062306a36Sopenharmony_ci *              associated _Lxx or _Exx methods and are not pointed to by any
3162306a36Sopenharmony_ci *              device _PRW methods (this indicates that these GPEs are
3262306a36Sopenharmony_ci *              generally intended for system or device wakeup. Such GPEs
3362306a36Sopenharmony_ci *              have to be enabled directly when the devices whose _PRW
3462306a36Sopenharmony_ci *              methods point to them are set up for wakeup signaling.)
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * NOTE: Should be called after any GPEs are added to the system. Primarily,
3762306a36Sopenharmony_ci * after the system _PRW methods have been run, but also after a GPE Block
3862306a36Sopenharmony_ci * Device has been added or if any new GPE methods have been added via a
3962306a36Sopenharmony_ci * dynamic table load.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci ******************************************************************************/
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciacpi_status acpi_update_all_gpes(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	acpi_status status;
4662306a36Sopenharmony_ci	u8 is_polling_needed = FALSE;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_update_all_gpes);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
5162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
5262306a36Sopenharmony_ci		return_ACPI_STATUS(status);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (acpi_gbl_all_gpes_initialized) {
5662306a36Sopenharmony_ci		goto unlock_and_exit;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	status = acpi_ev_walk_gpe_list(acpi_ev_initialize_gpe_block,
6062306a36Sopenharmony_ci				       &is_polling_needed);
6162306a36Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
6262306a36Sopenharmony_ci		acpi_gbl_all_gpes_initialized = TRUE;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciunlock_and_exit:
6662306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (is_polling_needed && acpi_gbl_all_gpes_initialized) {
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		/* Poll GPEs to handle already triggered events */
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		acpi_ev_gpe_detect(acpi_gbl_gpe_xrupt_list_head);
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci	return_ACPI_STATUS(status);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_update_all_gpes)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*******************************************************************************
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * FUNCTION:    acpi_enable_gpe
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
8462306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * RETURN:      Status
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * DESCRIPTION: Add a reference to a GPE. On the first reference, the GPE is
8962306a36Sopenharmony_ci *              hardware-enabled.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci ******************************************************************************/
9262306a36Sopenharmony_ciacpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	acpi_status status = AE_BAD_PARAMETER;
9562306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
9662306a36Sopenharmony_ci	acpi_cpu_flags flags;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_enable_gpe);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/*
10362306a36Sopenharmony_ci	 * Ensure that we have a valid GPE number and that there is some way
10462306a36Sopenharmony_ci	 * of handling the GPE (handler or a GPE method). In other words, we
10562306a36Sopenharmony_ci	 * won't allow a valid GPE to be enabled if there is no way to handle it.
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
10862306a36Sopenharmony_ci	if (gpe_event_info) {
10962306a36Sopenharmony_ci		if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) !=
11062306a36Sopenharmony_ci		    ACPI_GPE_DISPATCH_NONE) {
11162306a36Sopenharmony_ci			status = acpi_ev_add_gpe_reference(gpe_event_info, TRUE);
11262306a36Sopenharmony_ci			if (ACPI_SUCCESS(status) &&
11362306a36Sopenharmony_ci			    ACPI_GPE_IS_POLLING_NEEDED(gpe_event_info)) {
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci				/* Poll edge-triggered GPEs to handle existing events */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci				acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
11862306a36Sopenharmony_ci				(void)acpi_ev_detect_gpe(gpe_device,
11962306a36Sopenharmony_ci							 gpe_event_info,
12062306a36Sopenharmony_ci							 gpe_number);
12162306a36Sopenharmony_ci				flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
12262306a36Sopenharmony_ci			}
12362306a36Sopenharmony_ci		} else {
12462306a36Sopenharmony_ci			status = AE_NO_HANDLER;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
12962306a36Sopenharmony_ci	return_ACPI_STATUS(status);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_enable_gpe)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*******************************************************************************
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * FUNCTION:    acpi_disable_gpe
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * PARAMETERS:  gpe_device      - Parent GPE Device. NULL for GPE0/GPE1
13862306a36Sopenharmony_ci *              gpe_number      - GPE level within the GPE block
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * RETURN:      Status
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * DESCRIPTION: Remove a reference to a GPE. When the last reference is
14362306a36Sopenharmony_ci *              removed, only then is the GPE disabled (for runtime GPEs), or
14462306a36Sopenharmony_ci *              the GPE mask bit disabled (for wake GPEs)
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci ******************************************************************************/
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciacpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	acpi_status status = AE_BAD_PARAMETER;
15162306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
15262306a36Sopenharmony_ci	acpi_cpu_flags flags;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_disable_gpe);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
16162306a36Sopenharmony_ci	if (gpe_event_info) {
16262306a36Sopenharmony_ci		status = acpi_ev_remove_gpe_reference(gpe_event_info) ;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
16662306a36Sopenharmony_ci	return_ACPI_STATUS(status);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_disable_gpe)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/*******************************************************************************
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * FUNCTION:    acpi_set_gpe
17462306a36Sopenharmony_ci *
17562306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
17662306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
17762306a36Sopenharmony_ci *              action              - ACPI_GPE_ENABLE or ACPI_GPE_DISABLE
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * RETURN:      Status
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * DESCRIPTION: Enable or disable an individual GPE. This function bypasses
18262306a36Sopenharmony_ci *              the reference count mechanism used in the acpi_enable_gpe(),
18362306a36Sopenharmony_ci *              acpi_disable_gpe() interfaces.
18462306a36Sopenharmony_ci *              This API is typically used by the GPE raw handler mode driver
18562306a36Sopenharmony_ci *              to switch between the polling mode and the interrupt mode after
18662306a36Sopenharmony_ci *              the driver has enabled the GPE.
18762306a36Sopenharmony_ci *              The APIs should be invoked in this order:
18862306a36Sopenharmony_ci *               acpi_enable_gpe()            <- Ensure the reference count > 0
18962306a36Sopenharmony_ci *               acpi_set_gpe(ACPI_GPE_DISABLE) <- Enter polling mode
19062306a36Sopenharmony_ci *               acpi_set_gpe(ACPI_GPE_ENABLE) <- Leave polling mode
19162306a36Sopenharmony_ci *               acpi_disable_gpe()           <- Decrease the reference count
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * Note: If a GPE is shared by 2 silicon components, then both the drivers
19462306a36Sopenharmony_ci *       should support GPE polling mode or disabling the GPE for long period
19562306a36Sopenharmony_ci *       for one driver may break the other. So use it with care since all
19662306a36Sopenharmony_ci *       firmware _Lxx/_Exx handlers currently rely on the GPE interrupt mode.
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci ******************************************************************************/
19962306a36Sopenharmony_ciacpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
20262306a36Sopenharmony_ci	acpi_status status;
20362306a36Sopenharmony_ci	acpi_cpu_flags flags;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_set_gpe);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
21262306a36Sopenharmony_ci	if (!gpe_event_info) {
21362306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
21462306a36Sopenharmony_ci		goto unlock_and_exit;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Perform the action */
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	switch (action) {
22062306a36Sopenharmony_ci	case ACPI_GPE_ENABLE:
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
22362306a36Sopenharmony_ci		gpe_event_info->disable_for_dispatch = FALSE;
22462306a36Sopenharmony_ci		break;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	case ACPI_GPE_DISABLE:
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
22962306a36Sopenharmony_ci		gpe_event_info->disable_for_dispatch = TRUE;
23062306a36Sopenharmony_ci		break;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	default:
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciunlock_and_exit:
23962306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
24062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_set_gpe)
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/*******************************************************************************
24662306a36Sopenharmony_ci *
24762306a36Sopenharmony_ci * FUNCTION:    acpi_mask_gpe
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
25062306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
25162306a36Sopenharmony_ci *              is_masked           - Whether the GPE is masked or not
25262306a36Sopenharmony_ci *
25362306a36Sopenharmony_ci * RETURN:      Status
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci * DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to
25662306a36Sopenharmony_ci *              prevent a GPE flooding.
25762306a36Sopenharmony_ci *
25862306a36Sopenharmony_ci ******************************************************************************/
25962306a36Sopenharmony_ciacpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
26262306a36Sopenharmony_ci	acpi_status status;
26362306a36Sopenharmony_ci	acpi_cpu_flags flags;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_mask_gpe);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
27262306a36Sopenharmony_ci	if (!gpe_event_info) {
27362306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
27462306a36Sopenharmony_ci		goto unlock_and_exit;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	status = acpi_ev_mask_gpe(gpe_event_info, is_masked);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciunlock_and_exit:
28062306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
28162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_mask_gpe)
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*******************************************************************************
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * FUNCTION:    acpi_mark_gpe_for_wake
28962306a36Sopenharmony_ci *
29062306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
29162306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * RETURN:      Status
29462306a36Sopenharmony_ci *
29562306a36Sopenharmony_ci * DESCRIPTION: Mark a GPE as having the ability to wake the system. Simply
29662306a36Sopenharmony_ci *              sets the ACPI_GPE_CAN_WAKE flag.
29762306a36Sopenharmony_ci *
29862306a36Sopenharmony_ci * Some potential callers of acpi_setup_gpe_for_wake may know in advance that
29962306a36Sopenharmony_ci * there won't be any notify handlers installed for device wake notifications
30062306a36Sopenharmony_ci * from the given GPE (one example is a button GPE in Linux). For these cases,
30162306a36Sopenharmony_ci * acpi_mark_gpe_for_wake should be used instead of acpi_setup_gpe_for_wake.
30262306a36Sopenharmony_ci * This will set the ACPI_GPE_CAN_WAKE flag for the GPE without trying to
30362306a36Sopenharmony_ci * setup implicit wake notification for it (since there's no handler method).
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci ******************************************************************************/
30662306a36Sopenharmony_ciacpi_status acpi_mark_gpe_for_wake(acpi_handle gpe_device, u32 gpe_number)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
30962306a36Sopenharmony_ci	acpi_status status = AE_BAD_PARAMETER;
31062306a36Sopenharmony_ci	acpi_cpu_flags flags;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_mark_gpe_for_wake);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
31962306a36Sopenharmony_ci	if (gpe_event_info) {
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci		/* Mark the GPE as a possible wake event */
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
32462306a36Sopenharmony_ci		status = AE_OK;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
32862306a36Sopenharmony_ci	return_ACPI_STATUS(status);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_mark_gpe_for_wake)
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/*******************************************************************************
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * FUNCTION:    acpi_setup_gpe_for_wake
33662306a36Sopenharmony_ci *
33762306a36Sopenharmony_ci * PARAMETERS:  wake_device         - Device associated with the GPE (via _PRW)
33862306a36Sopenharmony_ci *              gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
33962306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
34062306a36Sopenharmony_ci *
34162306a36Sopenharmony_ci * RETURN:      Status
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * DESCRIPTION: Mark a GPE as having the ability to wake the system. This
34462306a36Sopenharmony_ci *              interface is intended to be used as the host executes the
34562306a36Sopenharmony_ci *              _PRW methods (Power Resources for Wake) in the system tables.
34662306a36Sopenharmony_ci *              Each _PRW appears under a Device Object (The wake_device), and
34762306a36Sopenharmony_ci *              contains the info for the wake GPE associated with the
34862306a36Sopenharmony_ci *              wake_device.
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci ******************************************************************************/
35162306a36Sopenharmony_ciacpi_status
35262306a36Sopenharmony_ciacpi_setup_gpe_for_wake(acpi_handle wake_device,
35362306a36Sopenharmony_ci			acpi_handle gpe_device, u32 gpe_number)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	acpi_status status;
35662306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
35762306a36Sopenharmony_ci	struct acpi_namespace_node *device_node;
35862306a36Sopenharmony_ci	struct acpi_gpe_notify_info *notify;
35962306a36Sopenharmony_ci	struct acpi_gpe_notify_info *new_notify;
36062306a36Sopenharmony_ci	acpi_cpu_flags flags;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Parameter Validation */
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!wake_device) {
36762306a36Sopenharmony_ci		/*
36862306a36Sopenharmony_ci		 * By forcing wake_device to be valid, we automatically enable the
36962306a36Sopenharmony_ci		 * implicit notify feature on all hosts.
37062306a36Sopenharmony_ci		 */
37162306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Handle root object case */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (wake_device == ACPI_ROOT_OBJECT) {
37762306a36Sopenharmony_ci		device_node = acpi_gbl_root_node;
37862306a36Sopenharmony_ci	} else {
37962306a36Sopenharmony_ci		device_node =
38062306a36Sopenharmony_ci		    ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* Validate wake_device is of type Device */
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (device_node->type != ACPI_TYPE_DEVICE) {
38662306a36Sopenharmony_ci		return_ACPI_STATUS (AE_BAD_PARAMETER);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Allocate a new notify object up front, in case it is needed.
39162306a36Sopenharmony_ci	 * Memory allocation while holding a spinlock is a big no-no
39262306a36Sopenharmony_ci	 * on some hosts.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info));
39562306a36Sopenharmony_ci	if (!new_notify) {
39662306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NO_MEMORY);
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
40462306a36Sopenharmony_ci	if (!gpe_event_info) {
40562306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
40662306a36Sopenharmony_ci		goto unlock_and_exit;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*
41062306a36Sopenharmony_ci	 * If there is no method or handler for this GPE, then the
41162306a36Sopenharmony_ci	 * wake_device will be notified whenever this GPE fires. This is
41262306a36Sopenharmony_ci	 * known as an "implicit notify". Note: The GPE is assumed to be
41362306a36Sopenharmony_ci	 * level-triggered (for windows compatibility).
41462306a36Sopenharmony_ci	 */
41562306a36Sopenharmony_ci	if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
41662306a36Sopenharmony_ci	    ACPI_GPE_DISPATCH_NONE) {
41762306a36Sopenharmony_ci		/*
41862306a36Sopenharmony_ci		 * This is the first device for implicit notify on this GPE.
41962306a36Sopenharmony_ci		 * Just set the flags here, and enter the NOTIFY block below.
42062306a36Sopenharmony_ci		 */
42162306a36Sopenharmony_ci		gpe_event_info->flags =
42262306a36Sopenharmony_ci		    (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
42362306a36Sopenharmony_ci	} else if (gpe_event_info->flags & ACPI_GPE_AUTO_ENABLED) {
42462306a36Sopenharmony_ci		/*
42562306a36Sopenharmony_ci		 * A reference to this GPE has been added during the GPE block
42662306a36Sopenharmony_ci		 * initialization, so drop it now to prevent the GPE from being
42762306a36Sopenharmony_ci		 * permanently enabled and clear its ACPI_GPE_AUTO_ENABLED flag.
42862306a36Sopenharmony_ci		 */
42962306a36Sopenharmony_ci		(void)acpi_ev_remove_gpe_reference(gpe_event_info);
43062306a36Sopenharmony_ci		gpe_event_info->flags &= ~ACPI_GPE_AUTO_ENABLED;
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 * If we already have an implicit notify on this GPE, add
43562306a36Sopenharmony_ci	 * this device to the notify list.
43662306a36Sopenharmony_ci	 */
43762306a36Sopenharmony_ci	if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
43862306a36Sopenharmony_ci	    ACPI_GPE_DISPATCH_NOTIFY) {
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		/* Ensure that the device is not already in the list */
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		notify = gpe_event_info->dispatch.notify_list;
44362306a36Sopenharmony_ci		while (notify) {
44462306a36Sopenharmony_ci			if (notify->device_node == device_node) {
44562306a36Sopenharmony_ci				status = AE_ALREADY_EXISTS;
44662306a36Sopenharmony_ci				goto unlock_and_exit;
44762306a36Sopenharmony_ci			}
44862306a36Sopenharmony_ci			notify = notify->next;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		/* Add this device to the notify list for this GPE */
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		new_notify->device_node = device_node;
45462306a36Sopenharmony_ci		new_notify->next = gpe_event_info->dispatch.notify_list;
45562306a36Sopenharmony_ci		gpe_event_info->dispatch.notify_list = new_notify;
45662306a36Sopenharmony_ci		new_notify = NULL;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* Mark the GPE as a possible wake event */
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
46262306a36Sopenharmony_ci	status = AE_OK;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ciunlock_and_exit:
46562306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* Delete the notify object if it was not used above */
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (new_notify) {
47062306a36Sopenharmony_ci		ACPI_FREE(new_notify);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	return_ACPI_STATUS(status);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/*******************************************************************************
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * FUNCTION:    acpi_set_gpe_wake_mask
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * PARAMETERS:  gpe_device      - Parent GPE Device. NULL for GPE0/GPE1
48162306a36Sopenharmony_ci *              gpe_number      - GPE level within the GPE block
48262306a36Sopenharmony_ci *              action              - Enable or Disable
48362306a36Sopenharmony_ci *
48462306a36Sopenharmony_ci * RETURN:      Status
48562306a36Sopenharmony_ci *
48662306a36Sopenharmony_ci * DESCRIPTION: Set or clear the GPE's wakeup enable mask bit. The GPE must
48762306a36Sopenharmony_ci *              already be marked as a WAKE GPE.
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci ******************************************************************************/
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciacpi_status
49262306a36Sopenharmony_ciacpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	acpi_status status = AE_OK;
49562306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
49662306a36Sopenharmony_ci	struct acpi_gpe_register_info *gpe_register_info;
49762306a36Sopenharmony_ci	acpi_cpu_flags flags;
49862306a36Sopenharmony_ci	u32 register_bit;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_set_gpe_wake_mask);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/*
50562306a36Sopenharmony_ci	 * Ensure that we have a valid GPE number and that this GPE is in
50662306a36Sopenharmony_ci	 * fact a wake GPE
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
50962306a36Sopenharmony_ci	if (!gpe_event_info) {
51062306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
51162306a36Sopenharmony_ci		goto unlock_and_exit;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (!(gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) {
51562306a36Sopenharmony_ci		status = AE_TYPE;
51662306a36Sopenharmony_ci		goto unlock_and_exit;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	gpe_register_info = gpe_event_info->register_info;
52062306a36Sopenharmony_ci	if (!gpe_register_info) {
52162306a36Sopenharmony_ci		status = AE_NOT_EXIST;
52262306a36Sopenharmony_ci		goto unlock_and_exit;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Perform the action */
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	switch (action) {
53062306a36Sopenharmony_ci	case ACPI_GPE_ENABLE:
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		ACPI_SET_BIT(gpe_register_info->enable_for_wake,
53362306a36Sopenharmony_ci			     (u8)register_bit);
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	case ACPI_GPE_DISABLE:
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake,
53962306a36Sopenharmony_ci			       (u8)register_bit);
54062306a36Sopenharmony_ci		break;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	default:
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "%u, Invalid action", action));
54562306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
54662306a36Sopenharmony_ci		break;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ciunlock_and_exit:
55062306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
55162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_set_gpe_wake_mask)
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*******************************************************************************
55762306a36Sopenharmony_ci *
55862306a36Sopenharmony_ci * FUNCTION:    acpi_clear_gpe
55962306a36Sopenharmony_ci *
56062306a36Sopenharmony_ci * PARAMETERS:  gpe_device      - Parent GPE Device. NULL for GPE0/GPE1
56162306a36Sopenharmony_ci *              gpe_number      - GPE level within the GPE block
56262306a36Sopenharmony_ci *
56362306a36Sopenharmony_ci * RETURN:      Status
56462306a36Sopenharmony_ci *
56562306a36Sopenharmony_ci * DESCRIPTION: Clear an ACPI event (general purpose)
56662306a36Sopenharmony_ci *
56762306a36Sopenharmony_ci ******************************************************************************/
56862306a36Sopenharmony_ciacpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	acpi_status status = AE_OK;
57162306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
57262306a36Sopenharmony_ci	acpi_cpu_flags flags;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_clear_gpe);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
58162306a36Sopenharmony_ci	if (!gpe_event_info) {
58262306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
58362306a36Sopenharmony_ci		goto unlock_and_exit;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	status = acpi_hw_clear_gpe(gpe_event_info);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci      unlock_and_exit:
58962306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
59062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_clear_gpe)
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci/*******************************************************************************
59662306a36Sopenharmony_ci *
59762306a36Sopenharmony_ci * FUNCTION:    acpi_get_gpe_status
59862306a36Sopenharmony_ci *
59962306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
60062306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
60162306a36Sopenharmony_ci *              event_status        - Where the current status of the event
60262306a36Sopenharmony_ci *                                    will be returned
60362306a36Sopenharmony_ci *
60462306a36Sopenharmony_ci * RETURN:      Status
60562306a36Sopenharmony_ci *
60662306a36Sopenharmony_ci * DESCRIPTION: Get the current status of a GPE (signalled/not_signalled)
60762306a36Sopenharmony_ci *
60862306a36Sopenharmony_ci ******************************************************************************/
60962306a36Sopenharmony_ciacpi_status
61062306a36Sopenharmony_ciacpi_get_gpe_status(acpi_handle gpe_device,
61162306a36Sopenharmony_ci		    u32 gpe_number, acpi_event_status *event_status)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	acpi_status status = AE_OK;
61462306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
61562306a36Sopenharmony_ci	acpi_cpu_flags flags;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_get_gpe_status);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
62462306a36Sopenharmony_ci	if (!gpe_event_info) {
62562306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
62662306a36Sopenharmony_ci		goto unlock_and_exit;
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Obtain status on the requested GPE number */
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	status = acpi_hw_get_gpe_status(gpe_event_info, event_status);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ciunlock_and_exit:
63462306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
63562306a36Sopenharmony_ci	return_ACPI_STATUS(status);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_get_gpe_status)
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci/*******************************************************************************
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * FUNCTION:    acpi_gispatch_gpe
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
64562306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
64662306a36Sopenharmony_ci *
64762306a36Sopenharmony_ci * RETURN:      INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
64862306a36Sopenharmony_ci *
64962306a36Sopenharmony_ci * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
65062306a36Sopenharmony_ci *              (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
65162306a36Sopenharmony_ci *
65262306a36Sopenharmony_ci ******************************************************************************/
65362306a36Sopenharmony_ciu32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_dispatch_gpe);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_dispatch_gpe)
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/*******************************************************************************
66362306a36Sopenharmony_ci *
66462306a36Sopenharmony_ci * FUNCTION:    acpi_finish_gpe
66562306a36Sopenharmony_ci *
66662306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Namespace node for the GPE Block
66762306a36Sopenharmony_ci *                                    (NULL for FADT defined GPEs)
66862306a36Sopenharmony_ci *              gpe_number          - GPE level within the GPE block
66962306a36Sopenharmony_ci *
67062306a36Sopenharmony_ci * RETURN:      Status
67162306a36Sopenharmony_ci *
67262306a36Sopenharmony_ci * DESCRIPTION: Clear and conditionally re-enable a GPE. This completes the GPE
67362306a36Sopenharmony_ci *              processing. Intended for use by asynchronous host-installed
67462306a36Sopenharmony_ci *              GPE handlers. The GPE is only re-enabled if the enable_for_run bit
67562306a36Sopenharmony_ci *              is set in the GPE info.
67662306a36Sopenharmony_ci *
67762306a36Sopenharmony_ci ******************************************************************************/
67862306a36Sopenharmony_ciacpi_status acpi_finish_gpe(acpi_handle gpe_device, u32 gpe_number)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct acpi_gpe_event_info *gpe_event_info;
68162306a36Sopenharmony_ci	acpi_status status;
68262306a36Sopenharmony_ci	acpi_cpu_flags flags;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_finish_gpe);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* Ensure that we have a valid GPE number */
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
69162306a36Sopenharmony_ci	if (!gpe_event_info) {
69262306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
69362306a36Sopenharmony_ci		goto unlock_and_exit;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	status = acpi_ev_finish_gpe(gpe_event_info);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciunlock_and_exit:
69962306a36Sopenharmony_ci	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
70062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_finish_gpe)
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci/******************************************************************************
70662306a36Sopenharmony_ci *
70762306a36Sopenharmony_ci * FUNCTION:    acpi_disable_all_gpes
70862306a36Sopenharmony_ci *
70962306a36Sopenharmony_ci * PARAMETERS:  None
71062306a36Sopenharmony_ci *
71162306a36Sopenharmony_ci * RETURN:      Status
71262306a36Sopenharmony_ci *
71362306a36Sopenharmony_ci * DESCRIPTION: Disable and clear all GPEs in all GPE blocks
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci ******************************************************************************/
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ciacpi_status acpi_disable_all_gpes(void)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	acpi_status status;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_disable_all_gpes);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
72462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
72562306a36Sopenharmony_ci		return_ACPI_STATUS(status);
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	status = acpi_hw_disable_all_gpes();
72962306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return_ACPI_STATUS(status);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_disable_all_gpes)
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/******************************************************************************
73762306a36Sopenharmony_ci *
73862306a36Sopenharmony_ci * FUNCTION:    acpi_enable_all_runtime_gpes
73962306a36Sopenharmony_ci *
74062306a36Sopenharmony_ci * PARAMETERS:  None
74162306a36Sopenharmony_ci *
74262306a36Sopenharmony_ci * RETURN:      Status
74362306a36Sopenharmony_ci *
74462306a36Sopenharmony_ci * DESCRIPTION: Enable all "runtime" GPEs, in all GPE blocks
74562306a36Sopenharmony_ci *
74662306a36Sopenharmony_ci ******************************************************************************/
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ciacpi_status acpi_enable_all_runtime_gpes(void)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	acpi_status status;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_enable_all_runtime_gpes);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
75562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
75662306a36Sopenharmony_ci		return_ACPI_STATUS(status);
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	status = acpi_hw_enable_all_runtime_gpes();
76062306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return_ACPI_STATUS(status);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes)
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci/******************************************************************************
76862306a36Sopenharmony_ci *
76962306a36Sopenharmony_ci * FUNCTION:    acpi_enable_all_wakeup_gpes
77062306a36Sopenharmony_ci *
77162306a36Sopenharmony_ci * PARAMETERS:  None
77262306a36Sopenharmony_ci *
77362306a36Sopenharmony_ci * RETURN:      Status
77462306a36Sopenharmony_ci *
77562306a36Sopenharmony_ci * DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
77662306a36Sopenharmony_ci *              all GPE blocks.
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci ******************************************************************************/
77962306a36Sopenharmony_ciacpi_status acpi_enable_all_wakeup_gpes(void)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	acpi_status status;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
78662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
78762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	status = acpi_hw_enable_all_wakeup_gpes();
79162306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	return_ACPI_STATUS(status);
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci/******************************************************************************
79962306a36Sopenharmony_ci *
80062306a36Sopenharmony_ci * FUNCTION:    acpi_any_gpe_status_set
80162306a36Sopenharmony_ci *
80262306a36Sopenharmony_ci * PARAMETERS:  gpe_skip_number      - Number of the GPE to skip
80362306a36Sopenharmony_ci *
80462306a36Sopenharmony_ci * RETURN:      Whether or not the status bit is set for any GPE
80562306a36Sopenharmony_ci *
80662306a36Sopenharmony_ci * DESCRIPTION: Check the status bits of all enabled GPEs, except for the one
80762306a36Sopenharmony_ci *              represented by the "skip" argument, and return TRUE if any of
80862306a36Sopenharmony_ci *              them is set or FALSE otherwise.
80962306a36Sopenharmony_ci *
81062306a36Sopenharmony_ci ******************************************************************************/
81162306a36Sopenharmony_ciu32 acpi_any_gpe_status_set(u32 gpe_skip_number)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	acpi_status status;
81462306a36Sopenharmony_ci	acpi_handle gpe_device;
81562306a36Sopenharmony_ci	u8 ret;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_any_gpe_status_set);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
82062306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
82162306a36Sopenharmony_ci		return (FALSE);
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	status = acpi_get_gpe_device(gpe_skip_number, &gpe_device);
82562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
82662306a36Sopenharmony_ci		gpe_device = NULL;
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	ret = acpi_hw_check_all_gpes(gpe_device, gpe_skip_number);
83062306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return (ret);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_any_gpe_status_set)
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci/*******************************************************************************
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * FUNCTION:    acpi_install_gpe_block
84062306a36Sopenharmony_ci *
84162306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Handle to the parent GPE Block Device
84262306a36Sopenharmony_ci *              gpe_block_address   - Address and space_ID
84362306a36Sopenharmony_ci *              register_count      - Number of GPE register pairs in the block
84462306a36Sopenharmony_ci *              interrupt_number    - H/W interrupt for the block
84562306a36Sopenharmony_ci *
84662306a36Sopenharmony_ci * RETURN:      Status
84762306a36Sopenharmony_ci *
84862306a36Sopenharmony_ci * DESCRIPTION: Create and Install a block of GPE registers. The GPEs are not
84962306a36Sopenharmony_ci *              enabled here.
85062306a36Sopenharmony_ci *
85162306a36Sopenharmony_ci ******************************************************************************/
85262306a36Sopenharmony_ciacpi_status
85362306a36Sopenharmony_ciacpi_install_gpe_block(acpi_handle gpe_device,
85462306a36Sopenharmony_ci		       struct acpi_generic_address *gpe_block_address,
85562306a36Sopenharmony_ci		       u32 register_count, u32 interrupt_number)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	acpi_status status;
85862306a36Sopenharmony_ci	union acpi_operand_object *obj_desc;
85962306a36Sopenharmony_ci	struct acpi_namespace_node *node;
86062306a36Sopenharmony_ci	struct acpi_gpe_block_info *gpe_block;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_install_gpe_block);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if ((!gpe_device) || (!gpe_block_address) || (!register_count)) {
86562306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
86962306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
87062306a36Sopenharmony_ci		return_ACPI_STATUS(status);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	node = acpi_ns_validate_handle(gpe_device);
87462306a36Sopenharmony_ci	if (!node) {
87562306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
87662306a36Sopenharmony_ci		goto unlock_and_exit;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/* Validate the parent device */
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (node->type != ACPI_TYPE_DEVICE) {
88262306a36Sopenharmony_ci		status = AE_TYPE;
88362306a36Sopenharmony_ci		goto unlock_and_exit;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (node->object) {
88762306a36Sopenharmony_ci		status = AE_ALREADY_EXISTS;
88862306a36Sopenharmony_ci		goto unlock_and_exit;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/*
89262306a36Sopenharmony_ci	 * For user-installed GPE Block Devices, the gpe_block_base_number
89362306a36Sopenharmony_ci	 * is always zero
89462306a36Sopenharmony_ci	 */
89562306a36Sopenharmony_ci	status = acpi_ev_create_gpe_block(node, gpe_block_address->address,
89662306a36Sopenharmony_ci					  gpe_block_address->space_id,
89762306a36Sopenharmony_ci					  register_count, 0, interrupt_number,
89862306a36Sopenharmony_ci					  &gpe_block);
89962306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
90062306a36Sopenharmony_ci		goto unlock_and_exit;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* Install block in the device_object attached to the node */
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	obj_desc = acpi_ns_get_attached_object(node);
90662306a36Sopenharmony_ci	if (!obj_desc) {
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		/*
90962306a36Sopenharmony_ci		 * No object, create a new one (Device nodes do not always have
91062306a36Sopenharmony_ci		 * an attached object)
91162306a36Sopenharmony_ci		 */
91262306a36Sopenharmony_ci		obj_desc = acpi_ut_create_internal_object(ACPI_TYPE_DEVICE);
91362306a36Sopenharmony_ci		if (!obj_desc) {
91462306a36Sopenharmony_ci			status = AE_NO_MEMORY;
91562306a36Sopenharmony_ci			goto unlock_and_exit;
91662306a36Sopenharmony_ci		}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		status =
91962306a36Sopenharmony_ci		    acpi_ns_attach_object(node, obj_desc, ACPI_TYPE_DEVICE);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		/* Remove local reference to the object */
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci		acpi_ut_remove_reference(obj_desc);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
92662306a36Sopenharmony_ci			goto unlock_and_exit;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* Now install the GPE block in the device_object */
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	obj_desc->device.gpe_block = gpe_block;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ciunlock_and_exit:
93562306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
93662306a36Sopenharmony_ci	return_ACPI_STATUS(status);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_install_gpe_block)
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci/*******************************************************************************
94262306a36Sopenharmony_ci *
94362306a36Sopenharmony_ci * FUNCTION:    acpi_remove_gpe_block
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci * PARAMETERS:  gpe_device          - Handle to the parent GPE Block Device
94662306a36Sopenharmony_ci *
94762306a36Sopenharmony_ci * RETURN:      Status
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * DESCRIPTION: Remove a previously installed block of GPE registers
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci ******************************************************************************/
95262306a36Sopenharmony_ciacpi_status acpi_remove_gpe_block(acpi_handle gpe_device)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	union acpi_operand_object *obj_desc;
95562306a36Sopenharmony_ci	acpi_status status;
95662306a36Sopenharmony_ci	struct acpi_namespace_node *node;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_remove_gpe_block);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (!gpe_device) {
96162306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
96562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
96662306a36Sopenharmony_ci		return_ACPI_STATUS(status);
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	node = acpi_ns_validate_handle(gpe_device);
97062306a36Sopenharmony_ci	if (!node) {
97162306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
97262306a36Sopenharmony_ci		goto unlock_and_exit;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/* Validate the parent device */
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (node->type != ACPI_TYPE_DEVICE) {
97862306a36Sopenharmony_ci		status = AE_TYPE;
97962306a36Sopenharmony_ci		goto unlock_and_exit;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Get the device_object attached to the node */
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	obj_desc = acpi_ns_get_attached_object(node);
98562306a36Sopenharmony_ci	if (!obj_desc || !obj_desc->device.gpe_block) {
98662306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NULL_OBJECT);
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* Delete the GPE block (but not the device_object) */
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	status = acpi_ev_delete_gpe_block(obj_desc->device.gpe_block);
99262306a36Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
99362306a36Sopenharmony_ci		obj_desc->device.gpe_block = NULL;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ciunlock_and_exit:
99762306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
99862306a36Sopenharmony_ci	return_ACPI_STATUS(status);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_remove_gpe_block)
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/*******************************************************************************
100462306a36Sopenharmony_ci *
100562306a36Sopenharmony_ci * FUNCTION:    acpi_get_gpe_device
100662306a36Sopenharmony_ci *
100762306a36Sopenharmony_ci * PARAMETERS:  index               - System GPE index (0-current_gpe_count)
100862306a36Sopenharmony_ci *              gpe_device          - Where the parent GPE Device is returned
100962306a36Sopenharmony_ci *
101062306a36Sopenharmony_ci * RETURN:      Status
101162306a36Sopenharmony_ci *
101262306a36Sopenharmony_ci * DESCRIPTION: Obtain the GPE device associated with the input index. A NULL
101362306a36Sopenharmony_ci *              gpe device indicates that the gpe number is contained in one of
101462306a36Sopenharmony_ci *              the FADT-defined gpe blocks. Otherwise, the GPE block device.
101562306a36Sopenharmony_ci *
101662306a36Sopenharmony_ci ******************************************************************************/
101762306a36Sopenharmony_ciacpi_status acpi_get_gpe_device(u32 index, acpi_handle *gpe_device)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct acpi_gpe_device_info info;
102062306a36Sopenharmony_ci	acpi_status status;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_get_gpe_device);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (!gpe_device) {
102562306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (index >= acpi_current_gpe_count) {
102962306a36Sopenharmony_ci		return_ACPI_STATUS(AE_NOT_EXIST);
103062306a36Sopenharmony_ci	}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Setup and walk the GPE list */
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	info.index = index;
103562306a36Sopenharmony_ci	info.status = AE_NOT_EXIST;
103662306a36Sopenharmony_ci	info.gpe_device = NULL;
103762306a36Sopenharmony_ci	info.next_block_base_index = 0;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	status = acpi_ev_walk_gpe_list(acpi_ev_get_gpe_device, &info);
104062306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
104162306a36Sopenharmony_ci		return_ACPI_STATUS(status);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	*gpe_device = ACPI_CAST_PTR(acpi_handle, info.gpe_device);
104562306a36Sopenharmony_ci	return_ACPI_STATUS(info.status);
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_get_gpe_device)
104962306a36Sopenharmony_ci#endif				/* !ACPI_REDUCED_HARDWARE */
1050