162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Module Name: exfldio - Aml Field I/O
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *****************************************************************************/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <acpi/acpi.h>
1162306a36Sopenharmony_ci#include "accommon.h"
1262306a36Sopenharmony_ci#include "acinterp.h"
1362306a36Sopenharmony_ci#include "amlcode.h"
1462306a36Sopenharmony_ci#include "acevents.h"
1562306a36Sopenharmony_ci#include "acdispat.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define _COMPONENT          ACPI_EXECUTER
1862306a36Sopenharmony_ciACPI_MODULE_NAME("exfldio")
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Local prototypes */
2162306a36Sopenharmony_cistatic acpi_status
2262306a36Sopenharmony_ciacpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
2362306a36Sopenharmony_ci		       u32 field_datum_byte_offset, u64 *value, u32 read_write);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic u8
2662306a36Sopenharmony_ciacpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic acpi_status
2962306a36Sopenharmony_ciacpi_ex_setup_region(union acpi_operand_object *obj_desc,
3062306a36Sopenharmony_ci		     u32 field_datum_byte_offset);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*******************************************************************************
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * FUNCTION:    acpi_ex_setup_region
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * PARAMETERS:  obj_desc                - Field to be read or written
3762306a36Sopenharmony_ci *              field_datum_byte_offset - Byte offset of this datum within the
3862306a36Sopenharmony_ci *                                        parent field
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * RETURN:      Status
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * DESCRIPTION: Common processing for acpi_ex_extract_from_field and
4362306a36Sopenharmony_ci *              acpi_ex_insert_into_field. Initialize the Region if necessary and
4462306a36Sopenharmony_ci *              validate the request.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci ******************************************************************************/
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic acpi_status
4962306a36Sopenharmony_ciacpi_ex_setup_region(union acpi_operand_object *obj_desc,
5062306a36Sopenharmony_ci		     u32 field_datum_byte_offset)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	acpi_status status = AE_OK;
5362306a36Sopenharmony_ci	union acpi_operand_object *rgn_desc;
5462306a36Sopenharmony_ci	u8 space_id;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	rgn_desc = obj_desc->common_field.region_obj;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* We must have a valid region */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (rgn_desc->common.type != ACPI_TYPE_REGION) {
6362306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)",
6462306a36Sopenharmony_ci			    rgn_desc->common.type,
6562306a36Sopenharmony_ci			    acpi_ut_get_object_type_name(rgn_desc)));
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	space_id = rgn_desc->region.space_id;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Validate the Space ID */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!acpi_is_valid_space_id(space_id)) {
7562306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
7662306a36Sopenharmony_ci			    "Invalid/unknown Address Space ID: 0x%2.2X",
7762306a36Sopenharmony_ci			    space_id));
7862306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/*
8262306a36Sopenharmony_ci	 * If the Region Address and Length have not been previously evaluated,
8362306a36Sopenharmony_ci	 * evaluate them now and save the results.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) {
8662306a36Sopenharmony_ci		status = acpi_ds_get_region_arguments(rgn_desc);
8762306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
8862306a36Sopenharmony_ci			return_ACPI_STATUS(status);
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
9462306a36Sopenharmony_ci	 * address space and the request cannot be directly validated
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	if (space_id == ACPI_ADR_SPACE_SMBUS ||
9762306a36Sopenharmony_ci	    space_id == ACPI_ADR_SPACE_GSBUS ||
9862306a36Sopenharmony_ci	    space_id == ACPI_ADR_SPACE_IPMI) {
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		/* SMBus or IPMI has a non-linear address space */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		return_ACPI_STATUS(AE_OK);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci#ifdef ACPI_UNDER_DEVELOPMENT
10562306a36Sopenharmony_ci	/*
10662306a36Sopenharmony_ci	 * If the Field access is any_acc, we can now compute the optimal
10762306a36Sopenharmony_ci	 * access (because we know the length of the parent region)
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
11062306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
11162306a36Sopenharmony_ci			return_ACPI_STATUS(status);
11262306a36Sopenharmony_ci		}
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci#endif
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * Validate the request. The entire request from the byte offset for a
11862306a36Sopenharmony_ci	 * length of one field datum (access width) must fit within the region.
11962306a36Sopenharmony_ci	 * (Region length is specified in bytes)
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	if (rgn_desc->region.length <
12262306a36Sopenharmony_ci	    (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
12362306a36Sopenharmony_ci	     obj_desc->common_field.access_byte_width)) {
12462306a36Sopenharmony_ci		if (acpi_gbl_enable_interpreter_slack) {
12562306a36Sopenharmony_ci			/*
12662306a36Sopenharmony_ci			 * Slack mode only:  We will go ahead and allow access to this
12762306a36Sopenharmony_ci			 * field if it is within the region length rounded up to the next
12862306a36Sopenharmony_ci			 * access width boundary. acpi_size cast for 64-bit compile.
12962306a36Sopenharmony_ci			 */
13062306a36Sopenharmony_ci			if (ACPI_ROUND_UP(rgn_desc->region.length,
13162306a36Sopenharmony_ci					  obj_desc->common_field.
13262306a36Sopenharmony_ci					  access_byte_width) >=
13362306a36Sopenharmony_ci			    ((acpi_size)obj_desc->common_field.
13462306a36Sopenharmony_ci			     base_byte_offset +
13562306a36Sopenharmony_ci			     obj_desc->common_field.access_byte_width +
13662306a36Sopenharmony_ci			     field_datum_byte_offset)) {
13762306a36Sopenharmony_ci				return_ACPI_STATUS(AE_OK);
13862306a36Sopenharmony_ci			}
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		if (rgn_desc->region.length <
14262306a36Sopenharmony_ci		    obj_desc->common_field.access_byte_width) {
14362306a36Sopenharmony_ci			/*
14462306a36Sopenharmony_ci			 * This is the case where the access_type (acc_word, etc.) is wider
14562306a36Sopenharmony_ci			 * than the region itself. For example, a region of length one
14662306a36Sopenharmony_ci			 * byte, and a field with Dword access specified.
14762306a36Sopenharmony_ci			 */
14862306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO,
14962306a36Sopenharmony_ci				    "Field [%4.4s] access width (%u bytes) "
15062306a36Sopenharmony_ci				    "too large for region [%4.4s] (length %u)",
15162306a36Sopenharmony_ci				    acpi_ut_get_node_name(obj_desc->
15262306a36Sopenharmony_ci							  common_field.node),
15362306a36Sopenharmony_ci				    obj_desc->common_field.access_byte_width,
15462306a36Sopenharmony_ci				    acpi_ut_get_node_name(rgn_desc->region.
15562306a36Sopenharmony_ci							  node),
15662306a36Sopenharmony_ci				    rgn_desc->region.length));
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		/*
16062306a36Sopenharmony_ci		 * Offset rounded up to next multiple of field width
16162306a36Sopenharmony_ci		 * exceeds region length, indicate an error
16262306a36Sopenharmony_ci		 */
16362306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
16462306a36Sopenharmony_ci			    "Field [%4.4s] Base+Offset+Width %u+%u+%u "
16562306a36Sopenharmony_ci			    "is beyond end of region [%4.4s] (length %u)",
16662306a36Sopenharmony_ci			    acpi_ut_get_node_name(obj_desc->common_field.node),
16762306a36Sopenharmony_ci			    obj_desc->common_field.base_byte_offset,
16862306a36Sopenharmony_ci			    field_datum_byte_offset,
16962306a36Sopenharmony_ci			    obj_desc->common_field.access_byte_width,
17062306a36Sopenharmony_ci			    acpi_ut_get_node_name(rgn_desc->region.node),
17162306a36Sopenharmony_ci			    rgn_desc->region.length));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		return_ACPI_STATUS(AE_AML_REGION_LIMIT);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*******************************************************************************
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * FUNCTION:    acpi_ex_access_region
18262306a36Sopenharmony_ci *
18362306a36Sopenharmony_ci * PARAMETERS:  obj_desc                - Field to be read
18462306a36Sopenharmony_ci *              field_datum_byte_offset - Byte offset of this datum within the
18562306a36Sopenharmony_ci *                                        parent field
18662306a36Sopenharmony_ci *              value                   - Where to store value (must at least
18762306a36Sopenharmony_ci *                                        64 bits)
18862306a36Sopenharmony_ci *              function                - Read or Write flag plus other region-
18962306a36Sopenharmony_ci *                                        dependent flags
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * RETURN:      Status
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * DESCRIPTION: Read or Write a single field datum to an Operation Region.
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci ******************************************************************************/
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ciacpi_status
19862306a36Sopenharmony_ciacpi_ex_access_region(union acpi_operand_object *obj_desc,
19962306a36Sopenharmony_ci		      u32 field_datum_byte_offset, u64 *value, u32 function)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	acpi_status status;
20262306a36Sopenharmony_ci	union acpi_operand_object *rgn_desc;
20362306a36Sopenharmony_ci	u32 region_offset;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_access_region);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * Ensure that the region operands are fully evaluated and verify
20962306a36Sopenharmony_ci	 * the validity of the request
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset);
21262306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
21362306a36Sopenharmony_ci		return_ACPI_STATUS(status);
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * The physical address of this field datum is:
21862306a36Sopenharmony_ci	 *
21962306a36Sopenharmony_ci	 * 1) The base of the region, plus
22062306a36Sopenharmony_ci	 * 2) The base offset of the field, plus
22162306a36Sopenharmony_ci	 * 3) The current offset into the field
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	rgn_desc = obj_desc->common_field.region_obj;
22462306a36Sopenharmony_ci	region_offset =
22562306a36Sopenharmony_ci	    obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if ((function & ACPI_IO_MASK) == ACPI_READ) {
22862306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]"));
22962306a36Sopenharmony_ci	} else {
23062306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]"));
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
23462306a36Sopenharmony_ci			      " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
23562306a36Sopenharmony_ci			      acpi_ut_get_region_name(rgn_desc->region.
23662306a36Sopenharmony_ci						      space_id),
23762306a36Sopenharmony_ci			      rgn_desc->region.space_id,
23862306a36Sopenharmony_ci			      obj_desc->common_field.access_byte_width,
23962306a36Sopenharmony_ci			      obj_desc->common_field.base_byte_offset,
24062306a36Sopenharmony_ci			      field_datum_byte_offset,
24162306a36Sopenharmony_ci			      ACPI_FORMAT_UINT64(rgn_desc->region.address +
24262306a36Sopenharmony_ci						 region_offset)));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Invoke the appropriate address_space/op_region handler */
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	status = acpi_ev_address_space_dispatch(rgn_desc, obj_desc,
24762306a36Sopenharmony_ci						function, region_offset,
24862306a36Sopenharmony_ci						ACPI_MUL_8(obj_desc->
24962306a36Sopenharmony_ci							   common_field.
25062306a36Sopenharmony_ci							   access_byte_width),
25162306a36Sopenharmony_ci						value);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
25462306a36Sopenharmony_ci		if (status == AE_NOT_IMPLEMENTED) {
25562306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO,
25662306a36Sopenharmony_ci				    "Region %s (ID=%u) not implemented",
25762306a36Sopenharmony_ci				    acpi_ut_get_region_name(rgn_desc->region.
25862306a36Sopenharmony_ci							    space_id),
25962306a36Sopenharmony_ci				    rgn_desc->region.space_id));
26062306a36Sopenharmony_ci		} else if (status == AE_NOT_EXIST) {
26162306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO,
26262306a36Sopenharmony_ci				    "Region %s (ID=%u) has no handler",
26362306a36Sopenharmony_ci				    acpi_ut_get_region_name(rgn_desc->region.
26462306a36Sopenharmony_ci							    space_id),
26562306a36Sopenharmony_ci				    rgn_desc->region.space_id));
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return_ACPI_STATUS(status);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/*******************************************************************************
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * FUNCTION:    acpi_ex_register_overflow
27562306a36Sopenharmony_ci *
27662306a36Sopenharmony_ci * PARAMETERS:  obj_desc                - Register(Field) to be written
27762306a36Sopenharmony_ci *              value                   - Value to be stored
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * RETURN:      TRUE if value overflows the field, FALSE otherwise
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * DESCRIPTION: Check if a value is out of range of the field being written.
28262306a36Sopenharmony_ci *              Used to check if the values written to Index and Bank registers
28362306a36Sopenharmony_ci *              are out of range. Normally, the value is simply truncated
28462306a36Sopenharmony_ci *              to fit the field, but this case is most likely a serious
28562306a36Sopenharmony_ci *              coding error in the ASL.
28662306a36Sopenharmony_ci *
28762306a36Sopenharmony_ci ******************************************************************************/
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic u8
29062306a36Sopenharmony_ciacpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) {
29462306a36Sopenharmony_ci		/*
29562306a36Sopenharmony_ci		 * The field is large enough to hold the maximum integer, so we can
29662306a36Sopenharmony_ci		 * never overflow it.
29762306a36Sopenharmony_ci		 */
29862306a36Sopenharmony_ci		return (FALSE);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) {
30262306a36Sopenharmony_ci		/*
30362306a36Sopenharmony_ci		 * The Value is larger than the maximum value that can fit into
30462306a36Sopenharmony_ci		 * the register.
30562306a36Sopenharmony_ci		 */
30662306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
30762306a36Sopenharmony_ci			    "Index value 0x%8.8X%8.8X overflows field width 0x%X",
30862306a36Sopenharmony_ci			    ACPI_FORMAT_UINT64(value),
30962306a36Sopenharmony_ci			    obj_desc->common_field.bit_length));
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		return (TRUE);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* The Value will fit into the field with no truncation */
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return (FALSE);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/*******************************************************************************
32062306a36Sopenharmony_ci *
32162306a36Sopenharmony_ci * FUNCTION:    acpi_ex_field_datum_io
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * PARAMETERS:  obj_desc                - Field to be read
32462306a36Sopenharmony_ci *              field_datum_byte_offset - Byte offset of this datum within the
32562306a36Sopenharmony_ci *                                        parent field
32662306a36Sopenharmony_ci *              value                   - Where to store value (must be 64 bits)
32762306a36Sopenharmony_ci *              read_write              - Read or Write flag
32862306a36Sopenharmony_ci *
32962306a36Sopenharmony_ci * RETURN:      Status
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * DESCRIPTION: Read or Write a single datum of a field. The field_type is
33262306a36Sopenharmony_ci *              demultiplexed here to handle the different types of fields
33362306a36Sopenharmony_ci *              (buffer_field, region_field, index_field, bank_field)
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci ******************************************************************************/
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic acpi_status
33862306a36Sopenharmony_ciacpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
33962306a36Sopenharmony_ci		       u32 field_datum_byte_offset, u64 *value, u32 read_write)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	acpi_status status;
34262306a36Sopenharmony_ci	u64 local_value;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (read_write == ACPI_READ) {
34762306a36Sopenharmony_ci		if (!value) {
34862306a36Sopenharmony_ci			local_value = 0;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci			/* To support reads without saving return value */
35162306a36Sopenharmony_ci			value = &local_value;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		/* Clear the entire return buffer first, [Very Important!] */
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		*value = 0;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/*
36062306a36Sopenharmony_ci	 * The four types of fields are:
36162306a36Sopenharmony_ci	 *
36262306a36Sopenharmony_ci	 * buffer_field - Read/write from/to a Buffer
36362306a36Sopenharmony_ci	 * region_field - Read/write from/to a Operation Region.
36462306a36Sopenharmony_ci	 * bank_field  - Write to a Bank Register, then read/write from/to an
36562306a36Sopenharmony_ci	 *               operation_region
36662306a36Sopenharmony_ci	 * index_field - Write to an Index Register, then read/write from/to a
36762306a36Sopenharmony_ci	 *               Data Register
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	switch (obj_desc->common.type) {
37062306a36Sopenharmony_ci	case ACPI_TYPE_BUFFER_FIELD:
37162306a36Sopenharmony_ci		/*
37262306a36Sopenharmony_ci		 * If the buffer_field arguments have not been previously evaluated,
37362306a36Sopenharmony_ci		 * evaluate them now and save the results.
37462306a36Sopenharmony_ci		 */
37562306a36Sopenharmony_ci		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
37662306a36Sopenharmony_ci			status = acpi_ds_get_buffer_field_arguments(obj_desc);
37762306a36Sopenharmony_ci			if (ACPI_FAILURE(status)) {
37862306a36Sopenharmony_ci				return_ACPI_STATUS(status);
37962306a36Sopenharmony_ci			}
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		if (read_write == ACPI_READ) {
38362306a36Sopenharmony_ci			/*
38462306a36Sopenharmony_ci			 * Copy the data from the source buffer.
38562306a36Sopenharmony_ci			 * Length is the field width in bytes.
38662306a36Sopenharmony_ci			 */
38762306a36Sopenharmony_ci			memcpy(value,
38862306a36Sopenharmony_ci			       (obj_desc->buffer_field.buffer_obj)->buffer.
38962306a36Sopenharmony_ci			       pointer +
39062306a36Sopenharmony_ci			       obj_desc->buffer_field.base_byte_offset +
39162306a36Sopenharmony_ci			       field_datum_byte_offset,
39262306a36Sopenharmony_ci			       obj_desc->common_field.access_byte_width);
39362306a36Sopenharmony_ci		} else {
39462306a36Sopenharmony_ci			/*
39562306a36Sopenharmony_ci			 * Copy the data to the target buffer.
39662306a36Sopenharmony_ci			 * Length is the field width in bytes.
39762306a36Sopenharmony_ci			 */
39862306a36Sopenharmony_ci			memcpy((obj_desc->buffer_field.buffer_obj)->buffer.
39962306a36Sopenharmony_ci			       pointer +
40062306a36Sopenharmony_ci			       obj_desc->buffer_field.base_byte_offset +
40162306a36Sopenharmony_ci			       field_datum_byte_offset, value,
40262306a36Sopenharmony_ci			       obj_desc->common_field.access_byte_width);
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		status = AE_OK;
40662306a36Sopenharmony_ci		break;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	case ACPI_TYPE_LOCAL_BANK_FIELD:
40962306a36Sopenharmony_ci		/*
41062306a36Sopenharmony_ci		 * Ensure that the bank_value is not beyond the capacity of
41162306a36Sopenharmony_ci		 * the register
41262306a36Sopenharmony_ci		 */
41362306a36Sopenharmony_ci		if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj,
41462306a36Sopenharmony_ci					      (u64) obj_desc->bank_field.
41562306a36Sopenharmony_ci					      value)) {
41662306a36Sopenharmony_ci			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		/*
42062306a36Sopenharmony_ci		 * For bank_fields, we must write the bank_value to the bank_register
42162306a36Sopenharmony_ci		 * (itself a region_field) before we can access the data.
42262306a36Sopenharmony_ci		 */
42362306a36Sopenharmony_ci		status =
42462306a36Sopenharmony_ci		    acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj,
42562306a36Sopenharmony_ci					      &obj_desc->bank_field.value,
42662306a36Sopenharmony_ci					      sizeof(obj_desc->bank_field.
42762306a36Sopenharmony_ci						     value));
42862306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
42962306a36Sopenharmony_ci			return_ACPI_STATUS(status);
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/*
43362306a36Sopenharmony_ci		 * Now that the Bank has been selected, fall through to the
43462306a36Sopenharmony_ci		 * region_field case and write the datum to the Operation Region
43562306a36Sopenharmony_ci		 */
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		ACPI_FALLTHROUGH;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	case ACPI_TYPE_LOCAL_REGION_FIELD:
44062306a36Sopenharmony_ci		/*
44162306a36Sopenharmony_ci		 * For simple region_fields, we just directly access the owning
44262306a36Sopenharmony_ci		 * Operation Region.
44362306a36Sopenharmony_ci		 */
44462306a36Sopenharmony_ci		status =
44562306a36Sopenharmony_ci		    acpi_ex_access_region(obj_desc, field_datum_byte_offset,
44662306a36Sopenharmony_ci					  value, read_write);
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	case ACPI_TYPE_LOCAL_INDEX_FIELD:
45062306a36Sopenharmony_ci		/*
45162306a36Sopenharmony_ci		 * Ensure that the index_value is not beyond the capacity of
45262306a36Sopenharmony_ci		 * the register
45362306a36Sopenharmony_ci		 */
45462306a36Sopenharmony_ci		if (acpi_ex_register_overflow(obj_desc->index_field.index_obj,
45562306a36Sopenharmony_ci					      (u64) obj_desc->index_field.
45662306a36Sopenharmony_ci					      value)) {
45762306a36Sopenharmony_ci			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		/* Write the index value to the index_register (itself a region_field) */
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		field_datum_byte_offset += obj_desc->index_field.value;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
46562306a36Sopenharmony_ci				  "Write to Index Register: Value %8.8X\n",
46662306a36Sopenharmony_ci				  field_datum_byte_offset));
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		status =
46962306a36Sopenharmony_ci		    acpi_ex_insert_into_field(obj_desc->index_field.index_obj,
47062306a36Sopenharmony_ci					      &field_datum_byte_offset,
47162306a36Sopenharmony_ci					      sizeof(field_datum_byte_offset));
47262306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
47362306a36Sopenharmony_ci			return_ACPI_STATUS(status);
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		if (read_write == ACPI_READ) {
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci			/* Read the datum from the data_register */
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
48162306a36Sopenharmony_ci					  "Read from Data Register\n"));
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci			status =
48462306a36Sopenharmony_ci			    acpi_ex_extract_from_field(obj_desc->index_field.
48562306a36Sopenharmony_ci						       data_obj, value,
48662306a36Sopenharmony_ci						       sizeof(u64));
48762306a36Sopenharmony_ci		} else {
48862306a36Sopenharmony_ci			/* Write the datum to the data_register */
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
49162306a36Sopenharmony_ci					  "Write to Data Register: Value %8.8X%8.8X\n",
49262306a36Sopenharmony_ci					  ACPI_FORMAT_UINT64(*value)));
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci			status =
49562306a36Sopenharmony_ci			    acpi_ex_insert_into_field(obj_desc->index_field.
49662306a36Sopenharmony_ci						      data_obj, value,
49762306a36Sopenharmony_ci						      sizeof(u64));
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci		break;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	default:
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u",
50462306a36Sopenharmony_ci			    obj_desc->common.type));
50562306a36Sopenharmony_ci		status = AE_AML_INTERNAL;
50662306a36Sopenharmony_ci		break;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (ACPI_SUCCESS(status)) {
51062306a36Sopenharmony_ci		if (read_write == ACPI_READ) {
51162306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
51262306a36Sopenharmony_ci					  "Value Read %8.8X%8.8X, Width %u\n",
51362306a36Sopenharmony_ci					  ACPI_FORMAT_UINT64(*value),
51462306a36Sopenharmony_ci					  obj_desc->common_field.
51562306a36Sopenharmony_ci					  access_byte_width));
51662306a36Sopenharmony_ci		} else {
51762306a36Sopenharmony_ci			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
51862306a36Sopenharmony_ci					  "Value Written %8.8X%8.8X, Width %u\n",
51962306a36Sopenharmony_ci					  ACPI_FORMAT_UINT64(*value),
52062306a36Sopenharmony_ci					  obj_desc->common_field.
52162306a36Sopenharmony_ci					  access_byte_width));
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return_ACPI_STATUS(status);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/*******************************************************************************
52962306a36Sopenharmony_ci *
53062306a36Sopenharmony_ci * FUNCTION:    acpi_ex_write_with_update_rule
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * PARAMETERS:  obj_desc                - Field to be written
53362306a36Sopenharmony_ci *              mask                    - bitmask within field datum
53462306a36Sopenharmony_ci *              field_value             - Value to write
53562306a36Sopenharmony_ci *              field_datum_byte_offset - Offset of datum within field
53662306a36Sopenharmony_ci *
53762306a36Sopenharmony_ci * RETURN:      Status
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * DESCRIPTION: Apply the field update rule to a field write
54062306a36Sopenharmony_ci *
54162306a36Sopenharmony_ci ******************************************************************************/
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ciacpi_status
54462306a36Sopenharmony_ciacpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
54562306a36Sopenharmony_ci			       u64 mask,
54662306a36Sopenharmony_ci			       u64 field_value, u32 field_datum_byte_offset)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	acpi_status status = AE_OK;
54962306a36Sopenharmony_ci	u64 merged_value;
55062306a36Sopenharmony_ci	u64 current_value;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Start with the new bits  */
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	merged_value = field_value;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* If the mask is all ones, we don't need to worry about the update rule */
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (mask != ACPI_UINT64_MAX) {
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		/* Decode the update rule */
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		switch (obj_desc->common_field.
56562306a36Sopenharmony_ci			field_flags & AML_FIELD_UPDATE_RULE_MASK) {
56662306a36Sopenharmony_ci		case AML_FIELD_UPDATE_PRESERVE:
56762306a36Sopenharmony_ci			/*
56862306a36Sopenharmony_ci			 * Check if update rule needs to be applied (not if mask is all
56962306a36Sopenharmony_ci			 * ones)  The left shift drops the bits we want to ignore.
57062306a36Sopenharmony_ci			 */
57162306a36Sopenharmony_ci			if ((~mask << (ACPI_MUL_8(sizeof(mask)) -
57262306a36Sopenharmony_ci				       ACPI_MUL_8(obj_desc->common_field.
57362306a36Sopenharmony_ci						  access_byte_width))) != 0) {
57462306a36Sopenharmony_ci				/*
57562306a36Sopenharmony_ci				 * Read the current contents of the byte/word/dword containing
57662306a36Sopenharmony_ci				 * the field, and merge with the new field value.
57762306a36Sopenharmony_ci				 */
57862306a36Sopenharmony_ci				status =
57962306a36Sopenharmony_ci				    acpi_ex_field_datum_io(obj_desc,
58062306a36Sopenharmony_ci							   field_datum_byte_offset,
58162306a36Sopenharmony_ci							   &current_value,
58262306a36Sopenharmony_ci							   ACPI_READ);
58362306a36Sopenharmony_ci				if (ACPI_FAILURE(status)) {
58462306a36Sopenharmony_ci					return_ACPI_STATUS(status);
58562306a36Sopenharmony_ci				}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci				merged_value |= (current_value & ~mask);
58862306a36Sopenharmony_ci			}
58962306a36Sopenharmony_ci			break;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		case AML_FIELD_UPDATE_WRITE_AS_ONES:
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci			/* Set positions outside the field to all ones */
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci			merged_value |= ~mask;
59662306a36Sopenharmony_ci			break;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci			/* Set positions outside the field to all zeros */
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			merged_value &= mask;
60362306a36Sopenharmony_ci			break;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci		default:
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci			ACPI_ERROR((AE_INFO,
60862306a36Sopenharmony_ci				    "Unknown UpdateRule value: 0x%X",
60962306a36Sopenharmony_ci				    (obj_desc->common_field.field_flags &
61062306a36Sopenharmony_ci				     AML_FIELD_UPDATE_RULE_MASK)));
61162306a36Sopenharmony_ci			return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
61662306a36Sopenharmony_ci			  "Mask %8.8X%8.8X, DatumOffset %X, Width %X, "
61762306a36Sopenharmony_ci			  "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
61862306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(mask),
61962306a36Sopenharmony_ci			  field_datum_byte_offset,
62062306a36Sopenharmony_ci			  obj_desc->common_field.access_byte_width,
62162306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(field_value),
62262306a36Sopenharmony_ci			  ACPI_FORMAT_UINT64(merged_value)));
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* Write the merged value */
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	status =
62762306a36Sopenharmony_ci	    acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
62862306a36Sopenharmony_ci				   &merged_value, ACPI_WRITE);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return_ACPI_STATUS(status);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci/*******************************************************************************
63462306a36Sopenharmony_ci *
63562306a36Sopenharmony_ci * FUNCTION:    acpi_ex_extract_from_field
63662306a36Sopenharmony_ci *
63762306a36Sopenharmony_ci * PARAMETERS:  obj_desc            - Field to be read
63862306a36Sopenharmony_ci *              buffer              - Where to store the field data
63962306a36Sopenharmony_ci *              buffer_length       - Length of Buffer
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * RETURN:      Status
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * DESCRIPTION: Retrieve the current value of the given field
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci ******************************************************************************/
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ciacpi_status
64862306a36Sopenharmony_ciacpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
64962306a36Sopenharmony_ci			   void *buffer, u32 buffer_length)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	acpi_status status;
65262306a36Sopenharmony_ci	u64 raw_datum;
65362306a36Sopenharmony_ci	u64 merged_datum;
65462306a36Sopenharmony_ci	u32 field_offset = 0;
65562306a36Sopenharmony_ci	u32 buffer_offset = 0;
65662306a36Sopenharmony_ci	u32 buffer_tail_bits;
65762306a36Sopenharmony_ci	u32 datum_count;
65862306a36Sopenharmony_ci	u32 field_datum_count;
65962306a36Sopenharmony_ci	u32 access_bit_width;
66062306a36Sopenharmony_ci	u32 i;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_extract_from_field);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Validate target buffer and clear it */
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (buffer_length <
66762306a36Sopenharmony_ci	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) {
66862306a36Sopenharmony_ci		ACPI_ERROR((AE_INFO,
66962306a36Sopenharmony_ci			    "Field size %u (bits) is too large for buffer (%u)",
67062306a36Sopenharmony_ci			    obj_desc->common_field.bit_length, buffer_length));
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	memset(buffer, 0, buffer_length);
67662306a36Sopenharmony_ci	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* Handle the simple case here */
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if ((obj_desc->common_field.start_field_bit_offset == 0) &&
68162306a36Sopenharmony_ci	    (obj_desc->common_field.bit_length == access_bit_width)) {
68262306a36Sopenharmony_ci		if (buffer_length >= sizeof(u64)) {
68362306a36Sopenharmony_ci			status =
68462306a36Sopenharmony_ci			    acpi_ex_field_datum_io(obj_desc, 0, buffer,
68562306a36Sopenharmony_ci						   ACPI_READ);
68662306a36Sopenharmony_ci		} else {
68762306a36Sopenharmony_ci			/* Use raw_datum (u64) to handle buffers < 64 bits */
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci			status =
69062306a36Sopenharmony_ci			    acpi_ex_field_datum_io(obj_desc, 0, &raw_datum,
69162306a36Sopenharmony_ci						   ACPI_READ);
69262306a36Sopenharmony_ci			memcpy(buffer, &raw_datum, buffer_length);
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		return_ACPI_STATUS(status);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci/* TBD: Move to common setup code */
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* Field algorithm is limited to sizeof(u64), truncate if needed */
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
70362306a36Sopenharmony_ci		obj_desc->common_field.access_byte_width = sizeof(u64);
70462306a36Sopenharmony_ci		access_bit_width = sizeof(u64) * 8;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Compute the number of datums (access width data items) */
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	datum_count =
71062306a36Sopenharmony_ci	    ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
71162306a36Sopenharmony_ci			     access_bit_width);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
71462306a36Sopenharmony_ci					     obj_desc->common_field.
71562306a36Sopenharmony_ci					     start_field_bit_offset,
71662306a36Sopenharmony_ci					     access_bit_width);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* Priming read from the field */
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	status =
72162306a36Sopenharmony_ci	    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
72262306a36Sopenharmony_ci				   ACPI_READ);
72362306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
72462306a36Sopenharmony_ci		return_ACPI_STATUS(status);
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci	merged_datum =
72762306a36Sopenharmony_ci	    raw_datum >> obj_desc->common_field.start_field_bit_offset;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* Read the rest of the field */
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	for (i = 1; i < field_datum_count; i++) {
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		/* Get next input datum from the field */
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		field_offset += obj_desc->common_field.access_byte_width;
73662306a36Sopenharmony_ci		status =
73762306a36Sopenharmony_ci		    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
73862306a36Sopenharmony_ci					   ACPI_READ);
73962306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
74062306a36Sopenharmony_ci			return_ACPI_STATUS(status);
74162306a36Sopenharmony_ci		}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		/*
74462306a36Sopenharmony_ci		 * Merge with previous datum if necessary.
74562306a36Sopenharmony_ci		 *
74662306a36Sopenharmony_ci		 * Note: Before the shift, check if the shift value will be larger than
74762306a36Sopenharmony_ci		 * the integer size. If so, there is no need to perform the operation.
74862306a36Sopenharmony_ci		 * This avoids the differences in behavior between different compilers
74962306a36Sopenharmony_ci		 * concerning shift values larger than the target data width.
75062306a36Sopenharmony_ci		 */
75162306a36Sopenharmony_ci		if (access_bit_width -
75262306a36Sopenharmony_ci		    obj_desc->common_field.start_field_bit_offset <
75362306a36Sopenharmony_ci		    ACPI_INTEGER_BIT_SIZE) {
75462306a36Sopenharmony_ci			merged_datum |=
75562306a36Sopenharmony_ci			    raw_datum << (access_bit_width -
75662306a36Sopenharmony_ci					  obj_desc->common_field.
75762306a36Sopenharmony_ci					  start_field_bit_offset);
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		if (i == datum_count) {
76162306a36Sopenharmony_ci			break;
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		/* Write merged datum to target buffer */
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		memcpy(((char *)buffer) + buffer_offset, &merged_datum,
76762306a36Sopenharmony_ci		       ACPI_MIN(obj_desc->common_field.access_byte_width,
76862306a36Sopenharmony_ci				buffer_length - buffer_offset));
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		buffer_offset += obj_desc->common_field.access_byte_width;
77162306a36Sopenharmony_ci		merged_datum =
77262306a36Sopenharmony_ci		    raw_datum >> obj_desc->common_field.start_field_bit_offset;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* Mask off any extra bits in the last datum */
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
77862306a36Sopenharmony_ci	if (buffer_tail_bits) {
77962306a36Sopenharmony_ci		merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Write the last datum to the buffer */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	memcpy(((char *)buffer) + buffer_offset, &merged_datum,
78562306a36Sopenharmony_ci	       ACPI_MIN(obj_desc->common_field.access_byte_width,
78662306a36Sopenharmony_ci			buffer_length - buffer_offset));
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	return_ACPI_STATUS(AE_OK);
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci/*******************************************************************************
79262306a36Sopenharmony_ci *
79362306a36Sopenharmony_ci * FUNCTION:    acpi_ex_insert_into_field
79462306a36Sopenharmony_ci *
79562306a36Sopenharmony_ci * PARAMETERS:  obj_desc            - Field to be written
79662306a36Sopenharmony_ci *              buffer              - Data to be written
79762306a36Sopenharmony_ci *              buffer_length       - Length of Buffer
79862306a36Sopenharmony_ci *
79962306a36Sopenharmony_ci * RETURN:      Status
80062306a36Sopenharmony_ci *
80162306a36Sopenharmony_ci * DESCRIPTION: Store the Buffer contents into the given field
80262306a36Sopenharmony_ci *
80362306a36Sopenharmony_ci ******************************************************************************/
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ciacpi_status
80662306a36Sopenharmony_ciacpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
80762306a36Sopenharmony_ci			  void *buffer, u32 buffer_length)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	void *new_buffer;
81062306a36Sopenharmony_ci	acpi_status status;
81162306a36Sopenharmony_ci	u64 mask;
81262306a36Sopenharmony_ci	u64 width_mask;
81362306a36Sopenharmony_ci	u64 merged_datum;
81462306a36Sopenharmony_ci	u64 raw_datum = 0;
81562306a36Sopenharmony_ci	u32 field_offset = 0;
81662306a36Sopenharmony_ci	u32 buffer_offset = 0;
81762306a36Sopenharmony_ci	u32 buffer_tail_bits;
81862306a36Sopenharmony_ci	u32 datum_count;
81962306a36Sopenharmony_ci	u32 field_datum_count;
82062306a36Sopenharmony_ci	u32 access_bit_width;
82162306a36Sopenharmony_ci	u32 required_length;
82262306a36Sopenharmony_ci	u32 i;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	ACPI_FUNCTION_TRACE(ex_insert_into_field);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Validate input buffer */
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	new_buffer = NULL;
82962306a36Sopenharmony_ci	required_length =
83062306a36Sopenharmony_ci	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/*
83362306a36Sopenharmony_ci	 * We must have a buffer that is at least as long as the field
83462306a36Sopenharmony_ci	 * we are writing to. This is because individual fields are
83562306a36Sopenharmony_ci	 * indivisible and partial writes are not supported -- as per
83662306a36Sopenharmony_ci	 * the ACPI specification.
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	if (buffer_length < required_length) {
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		/* We need to create a new buffer */
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		new_buffer = ACPI_ALLOCATE_ZEROED(required_length);
84362306a36Sopenharmony_ci		if (!new_buffer) {
84462306a36Sopenharmony_ci			return_ACPI_STATUS(AE_NO_MEMORY);
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		/*
84862306a36Sopenharmony_ci		 * Copy the original data to the new buffer, starting
84962306a36Sopenharmony_ci		 * at Byte zero. All unused (upper) bytes of the
85062306a36Sopenharmony_ci		 * buffer will be 0.
85162306a36Sopenharmony_ci		 */
85262306a36Sopenharmony_ci		memcpy((char *)new_buffer, (char *)buffer, buffer_length);
85362306a36Sopenharmony_ci		buffer = new_buffer;
85462306a36Sopenharmony_ci		buffer_length = required_length;
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/* TBD: Move to common setup code */
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* Algo is limited to sizeof(u64), so cut the access_byte_width */
86062306a36Sopenharmony_ci	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
86162306a36Sopenharmony_ci		obj_desc->common_field.access_byte_width = sizeof(u64);
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	/* Create the bitmasks used for bit insertion */
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	width_mask = ACPI_MASK_BITS_ABOVE_64(access_bit_width);
86962306a36Sopenharmony_ci	mask = width_mask &
87062306a36Sopenharmony_ci	    ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* Compute the number of datums (access width data items) */
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
87562306a36Sopenharmony_ci				       access_bit_width);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
87862306a36Sopenharmony_ci					     obj_desc->common_field.
87962306a36Sopenharmony_ci					     start_field_bit_offset,
88062306a36Sopenharmony_ci					     access_bit_width);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Get initial Datum from the input buffer */
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	memcpy(&raw_datum, buffer,
88562306a36Sopenharmony_ci	       ACPI_MIN(obj_desc->common_field.access_byte_width,
88662306a36Sopenharmony_ci			buffer_length - buffer_offset));
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	merged_datum =
88962306a36Sopenharmony_ci	    raw_datum << obj_desc->common_field.start_field_bit_offset;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Write the entire field */
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	for (i = 1; i < field_datum_count; i++) {
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		/* Write merged datum to the target field */
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci		merged_datum &= mask;
89862306a36Sopenharmony_ci		status =
89962306a36Sopenharmony_ci		    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
90062306a36Sopenharmony_ci						   field_offset);
90162306a36Sopenharmony_ci		if (ACPI_FAILURE(status)) {
90262306a36Sopenharmony_ci			goto exit;
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		field_offset += obj_desc->common_field.access_byte_width;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		/*
90862306a36Sopenharmony_ci		 * Start new output datum by merging with previous input datum
90962306a36Sopenharmony_ci		 * if necessary.
91062306a36Sopenharmony_ci		 *
91162306a36Sopenharmony_ci		 * Note: Before the shift, check if the shift value will be larger than
91262306a36Sopenharmony_ci		 * the integer size. If so, there is no need to perform the operation.
91362306a36Sopenharmony_ci		 * This avoids the differences in behavior between different compilers
91462306a36Sopenharmony_ci		 * concerning shift values larger than the target data width.
91562306a36Sopenharmony_ci		 */
91662306a36Sopenharmony_ci		if ((access_bit_width -
91762306a36Sopenharmony_ci		     obj_desc->common_field.start_field_bit_offset) <
91862306a36Sopenharmony_ci		    ACPI_INTEGER_BIT_SIZE) {
91962306a36Sopenharmony_ci			merged_datum =
92062306a36Sopenharmony_ci			    raw_datum >> (access_bit_width -
92162306a36Sopenharmony_ci					  obj_desc->common_field.
92262306a36Sopenharmony_ci					  start_field_bit_offset);
92362306a36Sopenharmony_ci		} else {
92462306a36Sopenharmony_ci			merged_datum = 0;
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		mask = width_mask;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		if (i == datum_count) {
93062306a36Sopenharmony_ci			break;
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		/* Get the next input datum from the buffer */
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		buffer_offset += obj_desc->common_field.access_byte_width;
93662306a36Sopenharmony_ci		memcpy(&raw_datum, ((char *)buffer) + buffer_offset,
93762306a36Sopenharmony_ci		       ACPI_MIN(obj_desc->common_field.access_byte_width,
93862306a36Sopenharmony_ci				buffer_length - buffer_offset));
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		merged_datum |=
94162306a36Sopenharmony_ci		    raw_datum << obj_desc->common_field.start_field_bit_offset;
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* Mask off any extra bits in the last datum */
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	buffer_tail_bits = (obj_desc->common_field.bit_length +
94762306a36Sopenharmony_ci			    obj_desc->common_field.start_field_bit_offset) %
94862306a36Sopenharmony_ci	    access_bit_width;
94962306a36Sopenharmony_ci	if (buffer_tail_bits) {
95062306a36Sopenharmony_ci		mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* Write the last datum to the field */
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	merged_datum &= mask;
95662306a36Sopenharmony_ci	status =
95762306a36Sopenharmony_ci	    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
95862306a36Sopenharmony_ci					   field_offset);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ciexit:
96162306a36Sopenharmony_ci	/* Free temporary buffer if we used one */
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (new_buffer) {
96462306a36Sopenharmony_ci		ACPI_FREE(new_buffer);
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci	return_ACPI_STATUS(status);
96762306a36Sopenharmony_ci}
968