Commit e2066ca1 authored by Bob Moore's avatar Bob Moore Committed by Len Brown
Browse files

ACPICA: Execute an orphan _REG method under the EC device

This change will force the execution of a _REG method underneath
the EC device even if there is no corresponding operation region
of type EmbeddedControl. Fixes a problem seen on some machines
and apparently is compatible with Windows behavior.

http://www.acpica.org/bugzilla/show_bug.cgi?id=875



Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 07aa99e9
Loading
Loading
Loading
Loading
+119 −2
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ static u8
acpi_ev_has_default_handler(struct acpi_namespace_node *node,
			    acpi_adr_space_type space_id);

static void acpi_ev_orphan_ec_reg_method(void);

static acpi_status
acpi_ev_reg_run(acpi_handle obj_handle,
		u32 level, void *context, void **return_value);
@@ -561,7 +563,9 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj,

			/* Now stop region accesses by executing the _REG method */

			status = acpi_ev_execute_reg_method(region_obj, 0);
			status =
			    acpi_ev_execute_reg_method(region_obj,
						       ACPI_REG_DISCONNECT);
			if (ACPI_FAILURE(status)) {
				ACPI_EXCEPTION((AE_INFO, status,
						"from region _REG, [%s]",
@@ -1062,6 +1066,12 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
					ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run,
					NULL, &space_id, NULL);

	/* Special case for EC: handle "orphan" _REG methods with no region */

	if (space_id == ACPI_ADR_SPACE_EC) {
		acpi_ev_orphan_ec_reg_method();
	}

	return_ACPI_STATUS(status);
}

@@ -1120,6 +1130,113 @@ acpi_ev_reg_run(acpi_handle obj_handle,
		return (AE_OK);
	}

	status = acpi_ev_execute_reg_method(obj_desc, 1);
	status = acpi_ev_execute_reg_method(obj_desc, ACPI_REG_CONNECT);
	return (status);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ev_orphan_ec_reg_method
 *
 * PARAMETERS:  None
 *
 * RETURN:      None
 *
 * DESCRIPTION: Execute an "orphan" _REG method that appears under the EC
 *              device. This is a _REG method that has no corresponding region
 *              within the EC device scope. The orphan _REG method appears to
 *              have been enabled by the description of the ECDT in the ACPI
 *              specification: "The availability of the region space can be
 *              detected by providing a _REG method object underneath the
 *              Embedded Controller device."
 *
 *              To quickly access the EC device, we use the EC_ID that appears
 *              within the ECDT. Otherwise, we would need to perform a time-
 *              consuming namespace walk, executing _HID methods to find the
 *              EC device.
 *
 ******************************************************************************/

static void acpi_ev_orphan_ec_reg_method(void)
{
	struct acpi_table_ecdt *table;
	acpi_status status;
	struct acpi_object_list args;
	union acpi_object objects[2];
	struct acpi_namespace_node *ec_device_node;
	struct acpi_namespace_node *reg_method;
	struct acpi_namespace_node *next_node;

	ACPI_FUNCTION_TRACE(ev_orphan_ec_reg_method);

	/* Get the ECDT (if present in system) */

	status = acpi_get_table(ACPI_SIG_ECDT, 0,
				ACPI_CAST_INDIRECT_PTR(struct acpi_table_header,
						       &table));
	if (ACPI_FAILURE(status)) {
		return_VOID;
	}

	/* We need a valid EC_ID string */

	if (!(*table->id)) {
		return_VOID;
	}

	/* Namespace is currently locked, must release */

	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);

	/* Get a handle to the EC device referenced in the ECDT */

	status = acpi_get_handle(NULL,
				 ACPI_CAST_PTR(char, table->id),
				 ACPI_CAST_PTR(acpi_handle, &ec_device_node));
	if (ACPI_FAILURE(status)) {
		goto exit;
	}

	/* Get a handle to a _REG method immediately under the EC device */

	status = acpi_get_handle(ec_device_node,
				 METHOD_NAME__REG, ACPI_CAST_PTR(acpi_handle,
								 &reg_method));
	if (ACPI_FAILURE(status)) {
		goto exit;
	}

	/*
	 * Execute the _REG method only if there is no Operation Region in
	 * this scope with the Embedded Controller space ID. Otherwise, it
	 * will already have been executed. Note, this allows for Regions
	 * with other space IDs to be present; but the code below will then
	 * execute the _REG method with the EC space ID argument.
	 */
	next_node = acpi_ns_get_next_node(ec_device_node, NULL);
	while (next_node) {
		if ((next_node->type == ACPI_TYPE_REGION) &&
		    (next_node->object) &&
		    (next_node->object->region.space_id == ACPI_ADR_SPACE_EC)) {
			goto exit;	/* Do not execute _REG */
		}
		next_node = acpi_ns_get_next_node(ec_device_node, next_node);
	}

	/* Evaluate the _REG(EC,Connect) method */

	args.count = 2;
	args.pointer = objects;
	objects[0].type = ACPI_TYPE_INTEGER;
	objects[0].integer.value = ACPI_ADR_SPACE_EC;
	objects[1].type = ACPI_TYPE_INTEGER;
	objects[1].integer.value = ACPI_REG_CONNECT;

	status = acpi_evaluate_object(reg_method, NULL, &args, NULL);

      exit:
	/* We ignore all errors from above, don't care */

	status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
	return_VOID;
}
+1 −1
Original line number Diff line number Diff line
@@ -637,7 +637,7 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,

					status =
					    acpi_ev_execute_reg_method
					    (region_obj, 1);
					    (region_obj, ACPI_REG_CONNECT);

					if (acpi_ns_locked) {
						status =
+7 −6
Original line number Diff line number Diff line
@@ -130,20 +130,21 @@ acpi_install_address_space_handler(acpi_handle device,
	case ACPI_ADR_SPACE_PCI_CONFIG:
	case ACPI_ADR_SPACE_DATA_TABLE:

		if (acpi_gbl_reg_methods_executed) {
		if (!acpi_gbl_reg_methods_executed) {

			/* Run all _REG methods for this address space */

			status = acpi_ev_execute_reg_methods(node, space_id);
			/* We will defer execution of the _REG methods for this space */
			goto unlock_and_exit;
		}
		break;

	default:

		status = acpi_ev_execute_reg_methods(node, space_id);
		break;
	}

	/* Run all _REG methods for this address space */

	status = acpi_ev_execute_reg_methods(node, space_id);

      unlock_and_exit:
	(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
	return_ACPI_STATUS(status);
+5 −0
Original line number Diff line number Diff line
@@ -726,6 +726,11 @@ typedef u8 acpi_adr_space_type;
#define ACPI_ADR_SPACE_DATA_TABLE       (acpi_adr_space_type) 0x7E	/* Internal to ACPICA only */
#define ACPI_ADR_SPACE_FIXED_HARDWARE   (acpi_adr_space_type) 0x7F

/* Values for _REG connection code */

#define ACPI_REG_DISCONNECT             0
#define ACPI_REG_CONNECT                1

/*
 * bit_register IDs
 *