162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
562306a36Sopenharmony_ci *                         Address Spaces.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *****************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define EXPORT_ACPI_INTERFACES
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <acpi/acpi.h>
1462306a36Sopenharmony_ci#include "accommon.h"
1562306a36Sopenharmony_ci#include "acnamesp.h"
1662306a36Sopenharmony_ci#include "acevents.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define _COMPONENT          ACPI_EVENTS
1962306a36Sopenharmony_ciACPI_MODULE_NAME("evxfregn")
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*******************************************************************************
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * FUNCTION:    acpi_install_address_space_handler_internal
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * PARAMETERS:  device          - Handle for the device
2662306a36Sopenharmony_ci *              space_id        - The address space ID
2762306a36Sopenharmony_ci *              handler         - Address of the handler
2862306a36Sopenharmony_ci *              setup           - Address of the setup function
2962306a36Sopenharmony_ci *              context         - Value passed to the handler on each access
3062306a36Sopenharmony_ci *              Run_reg         - Run _REG methods for this address space?
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * RETURN:      Status
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * DESCRIPTION: Install a handler for all op_regions of a given space_id.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * NOTE: This function should only be called after acpi_enable_subsystem has
3762306a36Sopenharmony_ci * been called. This is because any _REG methods associated with the Space ID
3862306a36Sopenharmony_ci * are executed here, and these methods can only be safely executed after
3962306a36Sopenharmony_ci * the default handlers have been installed and the hardware has been
4062306a36Sopenharmony_ci * initialized (via acpi_enable_subsystem.)
4162306a36Sopenharmony_ci * To avoid this problem pass FALSE for Run_Reg and later on call
4262306a36Sopenharmony_ci * acpi_execute_reg_methods() to execute _REG.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci ******************************************************************************/
4562306a36Sopenharmony_cistatic acpi_status
4662306a36Sopenharmony_ciacpi_install_address_space_handler_internal(acpi_handle device,
4762306a36Sopenharmony_ci					    acpi_adr_space_type space_id,
4862306a36Sopenharmony_ci					    acpi_adr_space_handler handler,
4962306a36Sopenharmony_ci					    acpi_adr_space_setup setup,
5062306a36Sopenharmony_ci					    void *context, u8 run_reg)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct acpi_namespace_node *node;
5362306a36Sopenharmony_ci	acpi_status status;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_install_address_space_handler);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* Parameter validation */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (!device) {
6062306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
6462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
6562306a36Sopenharmony_ci		return_ACPI_STATUS(status);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* Convert and validate the device handle */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	node = acpi_ns_validate_handle(device);
7162306a36Sopenharmony_ci	if (!node) {
7262306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
7362306a36Sopenharmony_ci		goto unlock_and_exit;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Install the handler for all Regions for this Space ID */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	status =
7962306a36Sopenharmony_ci	    acpi_ev_install_space_handler(node, space_id, handler, setup,
8062306a36Sopenharmony_ci					  context);
8162306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
8262306a36Sopenharmony_ci		goto unlock_and_exit;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Run all _REG methods for this address space */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (run_reg) {
8862306a36Sopenharmony_ci		acpi_ev_execute_reg_methods(node, space_id, ACPI_REG_CONNECT);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciunlock_and_exit:
9262306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
9362306a36Sopenharmony_ci	return_ACPI_STATUS(status);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciacpi_status
9762306a36Sopenharmony_ciacpi_install_address_space_handler(acpi_handle device,
9862306a36Sopenharmony_ci				   acpi_adr_space_type space_id,
9962306a36Sopenharmony_ci				   acpi_adr_space_handler handler,
10062306a36Sopenharmony_ci				   acpi_adr_space_setup setup, void *context)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	return acpi_install_address_space_handler_internal(device, space_id,
10362306a36Sopenharmony_ci							   handler, setup,
10462306a36Sopenharmony_ci							   context, TRUE);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_install_address_space_handler)
10862306a36Sopenharmony_ciacpi_status
10962306a36Sopenharmony_ciacpi_install_address_space_handler_no_reg(acpi_handle device,
11062306a36Sopenharmony_ci					  acpi_adr_space_type space_id,
11162306a36Sopenharmony_ci					  acpi_adr_space_handler handler,
11262306a36Sopenharmony_ci					  acpi_adr_space_setup setup,
11362306a36Sopenharmony_ci					  void *context)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	return acpi_install_address_space_handler_internal(device, space_id,
11662306a36Sopenharmony_ci							   handler, setup,
11762306a36Sopenharmony_ci							   context, FALSE);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_install_address_space_handler_no_reg)
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/*******************************************************************************
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * FUNCTION:    acpi_remove_address_space_handler
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * PARAMETERS:  device          - Handle for the device
12762306a36Sopenharmony_ci *              space_id        - The address space ID
12862306a36Sopenharmony_ci *              handler         - Address of the handler
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * RETURN:      Status
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * DESCRIPTION: Remove a previously installed handler.
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci ******************************************************************************/
13562306a36Sopenharmony_ciacpi_status
13662306a36Sopenharmony_ciacpi_remove_address_space_handler(acpi_handle device,
13762306a36Sopenharmony_ci				  acpi_adr_space_type space_id,
13862306a36Sopenharmony_ci				  acpi_adr_space_handler handler)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	union acpi_operand_object *obj_desc;
14162306a36Sopenharmony_ci	union acpi_operand_object *handler_obj;
14262306a36Sopenharmony_ci	union acpi_operand_object *region_obj;
14362306a36Sopenharmony_ci	union acpi_operand_object **last_obj_ptr;
14462306a36Sopenharmony_ci	struct acpi_namespace_node *node;
14562306a36Sopenharmony_ci	acpi_status status;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_remove_address_space_handler);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Parameter validation */
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!device) {
15262306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
15662306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
15762306a36Sopenharmony_ci		return_ACPI_STATUS(status);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Convert and validate the device handle */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	node = acpi_ns_validate_handle(device);
16362306a36Sopenharmony_ci	if (!node ||
16462306a36Sopenharmony_ci	    ((node->type != ACPI_TYPE_DEVICE) &&
16562306a36Sopenharmony_ci	     (node->type != ACPI_TYPE_PROCESSOR) &&
16662306a36Sopenharmony_ci	     (node->type != ACPI_TYPE_THERMAL) &&
16762306a36Sopenharmony_ci	     (node != acpi_gbl_root_node))) {
16862306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
16962306a36Sopenharmony_ci		goto unlock_and_exit;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Make sure the internal object exists */
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	obj_desc = acpi_ns_get_attached_object(node);
17562306a36Sopenharmony_ci	if (!obj_desc) {
17662306a36Sopenharmony_ci		status = AE_NOT_EXIST;
17762306a36Sopenharmony_ci		goto unlock_and_exit;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Find the address handler the user requested */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	handler_obj = obj_desc->common_notify.handler;
18362306a36Sopenharmony_ci	last_obj_ptr = &obj_desc->common_notify.handler;
18462306a36Sopenharmony_ci	while (handler_obj) {
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		/* We have a handler, see if user requested this one */
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		if (handler_obj->address_space.space_id == space_id) {
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci			/* Handler must be the same as the installed handler */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			if (handler_obj->address_space.handler != handler) {
19362306a36Sopenharmony_ci				status = AE_BAD_PARAMETER;
19462306a36Sopenharmony_ci				goto unlock_and_exit;
19562306a36Sopenharmony_ci			}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci			/* Matched space_id, first dereference this in the Regions */
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
20062306a36Sopenharmony_ci					  "Removing address handler %p(%p) for region %s "
20162306a36Sopenharmony_ci					  "on Device %p(%p)\n",
20262306a36Sopenharmony_ci					  handler_obj, handler,
20362306a36Sopenharmony_ci					  acpi_ut_get_region_name(space_id),
20462306a36Sopenharmony_ci					  node, obj_desc));
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci			region_obj = handler_obj->address_space.region_list;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci			/* Walk the handler's region list */
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			while (region_obj) {
21162306a36Sopenharmony_ci				/*
21262306a36Sopenharmony_ci				 * First disassociate the handler from the region.
21362306a36Sopenharmony_ci				 *
21462306a36Sopenharmony_ci				 * NOTE: this doesn't mean that the region goes away
21562306a36Sopenharmony_ci				 * The region is just inaccessible as indicated to
21662306a36Sopenharmony_ci				 * the _REG method
21762306a36Sopenharmony_ci				 */
21862306a36Sopenharmony_ci				acpi_ev_detach_region(region_obj, TRUE);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci				/*
22162306a36Sopenharmony_ci				 * Walk the list: Just grab the head because the
22262306a36Sopenharmony_ci				 * detach_region removed the previous head.
22362306a36Sopenharmony_ci				 */
22462306a36Sopenharmony_ci				region_obj =
22562306a36Sopenharmony_ci				    handler_obj->address_space.region_list;
22662306a36Sopenharmony_ci			}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			/* Remove this Handler object from the list */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci			*last_obj_ptr = handler_obj->address_space.next;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci			/* Now we can delete the handler object */
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci			acpi_os_release_mutex(handler_obj->address_space.
23562306a36Sopenharmony_ci					      context_mutex);
23662306a36Sopenharmony_ci			acpi_ut_remove_reference(handler_obj);
23762306a36Sopenharmony_ci			goto unlock_and_exit;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		/* Walk the linked list of handlers */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		last_obj_ptr = &handler_obj->address_space.next;
24362306a36Sopenharmony_ci		handler_obj = handler_obj->address_space.next;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* The handler does not exist */
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_OPREGION,
24962306a36Sopenharmony_ci			  "Unable to remove address handler %p for %s(%X), DevNode %p, obj %p\n",
25062306a36Sopenharmony_ci			  handler, acpi_ut_get_region_name(space_id), space_id,
25162306a36Sopenharmony_ci			  node, obj_desc));
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	status = AE_NOT_EXIST;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ciunlock_and_exit:
25662306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
25762306a36Sopenharmony_ci	return_ACPI_STATUS(status);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_remove_address_space_handler)
26162306a36Sopenharmony_ci/*******************************************************************************
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * FUNCTION:    acpi_execute_reg_methods
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * PARAMETERS:  device          - Handle for the device
26662306a36Sopenharmony_ci *              space_id        - The address space ID
26762306a36Sopenharmony_ci *
26862306a36Sopenharmony_ci * RETURN:      Status
26962306a36Sopenharmony_ci *
27062306a36Sopenharmony_ci * DESCRIPTION: Execute _REG for all op_regions of a given space_id.
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci ******************************************************************************/
27362306a36Sopenharmony_ciacpi_status
27462306a36Sopenharmony_ciacpi_execute_reg_methods(acpi_handle device, acpi_adr_space_type space_id)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct acpi_namespace_node *node;
27762306a36Sopenharmony_ci	acpi_status status;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(acpi_execute_reg_methods);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Parameter validation */
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (!device) {
28462306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BAD_PARAMETER);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
28862306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
28962306a36Sopenharmony_ci		return_ACPI_STATUS(status);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Convert and validate the device handle */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	node = acpi_ns_validate_handle(device);
29562306a36Sopenharmony_ci	if (node) {
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		/* Run all _REG methods for this address space */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		acpi_ev_execute_reg_methods(node, space_id, ACPI_REG_CONNECT);
30062306a36Sopenharmony_ci	} else {
30162306a36Sopenharmony_ci		status = AE_BAD_PARAMETER;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
30562306a36Sopenharmony_ci	return_ACPI_STATUS(status);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciACPI_EXPORT_SYMBOL(acpi_execute_reg_methods)
309