xref: /openbmc/linux/drivers/acpi/acpica/exfldio.c (revision 612c2932)
195857638SErik Schmauss // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
295b482a8SLen Brown /******************************************************************************
395b482a8SLen Brown  *
495b482a8SLen Brown  * Module Name: exfldio - Aml Field I/O
595b482a8SLen Brown  *
6*612c2932SBob Moore  * Copyright (C) 2000 - 2023, Intel Corp.
795b482a8SLen Brown  *
895857638SErik Schmauss  *****************************************************************************/
995b482a8SLen Brown 
1095b482a8SLen Brown #include <acpi/acpi.h>
11e2f7a777SLen Brown #include "accommon.h"
12e2f7a777SLen Brown #include "acinterp.h"
13e2f7a777SLen Brown #include "amlcode.h"
14e2f7a777SLen Brown #include "acevents.h"
15e2f7a777SLen Brown #include "acdispat.h"
1695b482a8SLen Brown 
1795b482a8SLen Brown #define _COMPONENT          ACPI_EXECUTER
1895b482a8SLen Brown ACPI_MODULE_NAME("exfldio")
1995b482a8SLen Brown 
2095b482a8SLen Brown /* Local prototypes */
2195b482a8SLen Brown static acpi_status
2295b482a8SLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
231f86e8c1SLv Zheng 		       u32 field_datum_byte_offset, u64 *value, u32 read_write);
2495b482a8SLen Brown 
2595b482a8SLen Brown static u8
265df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value);
2795b482a8SLen Brown 
2895b482a8SLen Brown static acpi_status
2995b482a8SLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
3095b482a8SLen Brown 		     u32 field_datum_byte_offset);
3195b482a8SLen Brown 
3295b482a8SLen Brown /*******************************************************************************
3395b482a8SLen Brown  *
3495b482a8SLen Brown  * FUNCTION:    acpi_ex_setup_region
3595b482a8SLen Brown  *
3695b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read or written
3795b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
3895b482a8SLen Brown  *                                        parent field
3995b482a8SLen Brown  *
4095b482a8SLen Brown  * RETURN:      Status
4195b482a8SLen Brown  *
4295b482a8SLen Brown  * DESCRIPTION: Common processing for acpi_ex_extract_from_field and
4395b482a8SLen Brown  *              acpi_ex_insert_into_field. Initialize the Region if necessary and
4495b482a8SLen Brown  *              validate the request.
4595b482a8SLen Brown  *
4695b482a8SLen Brown  ******************************************************************************/
4795b482a8SLen Brown 
4895b482a8SLen Brown static acpi_status
acpi_ex_setup_region(union acpi_operand_object * obj_desc,u32 field_datum_byte_offset)4995b482a8SLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
5095b482a8SLen Brown 		     u32 field_datum_byte_offset)
5195b482a8SLen Brown {
5295b482a8SLen Brown 	acpi_status status = AE_OK;
5395b482a8SLen Brown 	union acpi_operand_object *rgn_desc;
54ec463666SBob Moore 	u8 space_id;
5595b482a8SLen Brown 
5695b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset);
5795b482a8SLen Brown 
5895b482a8SLen Brown 	rgn_desc = obj_desc->common_field.region_obj;
5995b482a8SLen Brown 
6095b482a8SLen Brown 	/* We must have a valid region */
6195b482a8SLen Brown 
623371c19cSBob Moore 	if (rgn_desc->common.type != ACPI_TYPE_REGION) {
63f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)",
643371c19cSBob Moore 			    rgn_desc->common.type,
6595b482a8SLen Brown 			    acpi_ut_get_object_type_name(rgn_desc)));
6695b482a8SLen Brown 
6795b482a8SLen Brown 		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
6895b482a8SLen Brown 	}
6995b482a8SLen Brown 
70ec463666SBob Moore 	space_id = rgn_desc->region.space_id;
71ec463666SBob Moore 
72ec463666SBob Moore 	/* Validate the Space ID */
73ec463666SBob Moore 
74ec463666SBob Moore 	if (!acpi_is_valid_space_id(space_id)) {
75ec463666SBob Moore 		ACPI_ERROR((AE_INFO,
76ec463666SBob Moore 			    "Invalid/unknown Address Space ID: 0x%2.2X",
77ec463666SBob Moore 			    space_id));
78ec463666SBob Moore 		return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
79ec463666SBob Moore 	}
80ec463666SBob Moore 
8195b482a8SLen Brown 	/*
8295b482a8SLen Brown 	 * If the Region Address and Length have not been previously evaluated,
8395b482a8SLen Brown 	 * evaluate them now and save the results.
8495b482a8SLen Brown 	 */
8595b482a8SLen Brown 	if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) {
8695b482a8SLen Brown 		status = acpi_ds_get_region_arguments(rgn_desc);
8795b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
8895b482a8SLen Brown 			return_ACPI_STATUS(status);
8995b482a8SLen Brown 		}
9095b482a8SLen Brown 	}
9195b482a8SLen Brown 
9295b482a8SLen Brown 	/*
932da120b6SBob Moore 	 * Exit now for SMBus, GSBus or IPMI address space, it has a non-linear
9409387b43SBob Moore 	 * address space and the request cannot be directly validated
9595b482a8SLen Brown 	 */
96ec463666SBob Moore 	if (space_id == ACPI_ADR_SPACE_SMBUS ||
972da120b6SBob Moore 	    space_id == ACPI_ADR_SPACE_GSBUS ||
98ec463666SBob Moore 	    space_id == ACPI_ADR_SPACE_IPMI) {
9995b482a8SLen Brown 
1006557a49aSLin Ming 		/* SMBus or IPMI has a non-linear address space */
10195b482a8SLen Brown 
10295b482a8SLen Brown 		return_ACPI_STATUS(AE_OK);
10395b482a8SLen Brown 	}
10495b482a8SLen Brown #ifdef ACPI_UNDER_DEVELOPMENT
10595b482a8SLen Brown 	/*
10695b482a8SLen Brown 	 * If the Field access is any_acc, we can now compute the optimal
1071cf0cee1STom Rix 	 * access (because we know the length of the parent region)
10895b482a8SLen Brown 	 */
10995b482a8SLen Brown 	if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
11095b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
11195b482a8SLen Brown 			return_ACPI_STATUS(status);
11295b482a8SLen Brown 		}
11395b482a8SLen Brown 	}
11495b482a8SLen Brown #endif
11595b482a8SLen Brown 
11695b482a8SLen Brown 	/*
11795b482a8SLen Brown 	 * Validate the request. The entire request from the byte offset for a
11895b482a8SLen Brown 	 * length of one field datum (access width) must fit within the region.
11995b482a8SLen Brown 	 * (Region length is specified in bytes)
12095b482a8SLen Brown 	 */
12195b482a8SLen Brown 	if (rgn_desc->region.length <
12209387b43SBob Moore 	    (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
12395b482a8SLen Brown 	     obj_desc->common_field.access_byte_width)) {
12495b482a8SLen Brown 		if (acpi_gbl_enable_interpreter_slack) {
12595b482a8SLen Brown 			/*
12695b482a8SLen Brown 			 * Slack mode only:  We will go ahead and allow access to this
12795b482a8SLen Brown 			 * field if it is within the region length rounded up to the next
12895b482a8SLen Brown 			 * access width boundary. acpi_size cast for 64-bit compile.
12995b482a8SLen Brown 			 */
13095b482a8SLen Brown 			if (ACPI_ROUND_UP(rgn_desc->region.length,
13195b482a8SLen Brown 					  obj_desc->common_field.
13295b482a8SLen Brown 					  access_byte_width) >=
13395b482a8SLen Brown 			    ((acpi_size)obj_desc->common_field.
13495b482a8SLen Brown 			     base_byte_offset +
13595b482a8SLen Brown 			     obj_desc->common_field.access_byte_width +
13695b482a8SLen Brown 			     field_datum_byte_offset)) {
13795b482a8SLen Brown 				return_ACPI_STATUS(AE_OK);
13895b482a8SLen Brown 			}
13995b482a8SLen Brown 		}
14095b482a8SLen Brown 
14195b482a8SLen Brown 		if (rgn_desc->region.length <
14295b482a8SLen Brown 		    obj_desc->common_field.access_byte_width) {
14395b482a8SLen Brown 			/*
14495b482a8SLen Brown 			 * This is the case where the access_type (acc_word, etc.) is wider
14595b482a8SLen Brown 			 * than the region itself. For example, a region of length one
14695b482a8SLen Brown 			 * byte, and a field with Dword access specified.
14795b482a8SLen Brown 			 */
14895b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
1491fad8738SBob Moore 				    "Field [%4.4s] access width (%u bytes) "
1501fad8738SBob Moore 				    "too large for region [%4.4s] (length %u)",
15195b482a8SLen Brown 				    acpi_ut_get_node_name(obj_desc->
15295b482a8SLen Brown 							  common_field.node),
15395b482a8SLen Brown 				    obj_desc->common_field.access_byte_width,
15495b482a8SLen Brown 				    acpi_ut_get_node_name(rgn_desc->region.
15595b482a8SLen Brown 							  node),
15695b482a8SLen Brown 				    rgn_desc->region.length));
15795b482a8SLen Brown 		}
15895b482a8SLen Brown 
15995b482a8SLen Brown 		/*
16095b482a8SLen Brown 		 * Offset rounded up to next multiple of field width
16195b482a8SLen Brown 		 * exceeds region length, indicate an error
16295b482a8SLen Brown 		 */
16395b482a8SLen Brown 		ACPI_ERROR((AE_INFO,
1641fad8738SBob Moore 			    "Field [%4.4s] Base+Offset+Width %u+%u+%u "
1651fad8738SBob Moore 			    "is beyond end of region [%4.4s] (length %u)",
16695b482a8SLen Brown 			    acpi_ut_get_node_name(obj_desc->common_field.node),
16795b482a8SLen Brown 			    obj_desc->common_field.base_byte_offset,
16895b482a8SLen Brown 			    field_datum_byte_offset,
16995b482a8SLen Brown 			    obj_desc->common_field.access_byte_width,
17095b482a8SLen Brown 			    acpi_ut_get_node_name(rgn_desc->region.node),
17195b482a8SLen Brown 			    rgn_desc->region.length));
17295b482a8SLen Brown 
17395b482a8SLen Brown 		return_ACPI_STATUS(AE_AML_REGION_LIMIT);
17495b482a8SLen Brown 	}
17595b482a8SLen Brown 
17695b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
17795b482a8SLen Brown }
17895b482a8SLen Brown 
17995b482a8SLen Brown /*******************************************************************************
18095b482a8SLen Brown  *
18195b482a8SLen Brown  * FUNCTION:    acpi_ex_access_region
18295b482a8SLen Brown  *
18395b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read
18495b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
18595b482a8SLen Brown  *                                        parent field
186ba494beeSBob Moore  *              value                   - Where to store value (must at least
1875df7e6cbSBob Moore  *                                        64 bits)
188ba494beeSBob Moore  *              function                - Read or Write flag plus other region-
18995b482a8SLen Brown  *                                        dependent flags
19095b482a8SLen Brown  *
19195b482a8SLen Brown  * RETURN:      Status
19295b482a8SLen Brown  *
19395b482a8SLen Brown  * DESCRIPTION: Read or Write a single field datum to an Operation Region.
19495b482a8SLen Brown  *
19595b482a8SLen Brown  ******************************************************************************/
19695b482a8SLen Brown 
19795b482a8SLen Brown acpi_status
acpi_ex_access_region(union acpi_operand_object * obj_desc,u32 field_datum_byte_offset,u64 * value,u32 function)19895b482a8SLen Brown acpi_ex_access_region(union acpi_operand_object *obj_desc,
1995df7e6cbSBob Moore 		      u32 field_datum_byte_offset, u64 *value, u32 function)
20095b482a8SLen Brown {
20195b482a8SLen Brown 	acpi_status status;
20295b482a8SLen Brown 	union acpi_operand_object *rgn_desc;
203f5407af3SBob Moore 	u32 region_offset;
20495b482a8SLen Brown 
20595b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_access_region);
20695b482a8SLen Brown 
20795b482a8SLen Brown 	/*
20895b482a8SLen Brown 	 * Ensure that the region operands are fully evaluated and verify
20995b482a8SLen Brown 	 * the validity of the request
21095b482a8SLen Brown 	 */
21195b482a8SLen Brown 	status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset);
21295b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
21395b482a8SLen Brown 		return_ACPI_STATUS(status);
21495b482a8SLen Brown 	}
21595b482a8SLen Brown 
21695b482a8SLen Brown 	/*
21795b482a8SLen Brown 	 * The physical address of this field datum is:
21895b482a8SLen Brown 	 *
21995b482a8SLen Brown 	 * 1) The base of the region, plus
22095b482a8SLen Brown 	 * 2) The base offset of the field, plus
22195b482a8SLen Brown 	 * 3) The current offset into the field
22295b482a8SLen Brown 	 */
22395b482a8SLen Brown 	rgn_desc = obj_desc->common_field.region_obj;
224f5407af3SBob Moore 	region_offset =
22595b482a8SLen Brown 	    obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
22695b482a8SLen Brown 
22795b482a8SLen Brown 	if ((function & ACPI_IO_MASK) == ACPI_READ) {
22895b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]"));
22995b482a8SLen Brown 	} else {
23095b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]"));
23195b482a8SLen Brown 	}
23295b482a8SLen Brown 
23395b482a8SLen Brown 	ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
234cc2080b0SLv Zheng 			      " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
23595b482a8SLen Brown 			      acpi_ut_get_region_name(rgn_desc->region.
23695b482a8SLen Brown 						      space_id),
23795b482a8SLen Brown 			      rgn_desc->region.space_id,
23895b482a8SLen Brown 			      obj_desc->common_field.access_byte_width,
23995b482a8SLen Brown 			      obj_desc->common_field.base_byte_offset,
240cc2080b0SLv Zheng 			      field_datum_byte_offset,
241cc2080b0SLv Zheng 			      ACPI_FORMAT_UINT64(rgn_desc->region.address +
242cc2080b0SLv Zheng 						 region_offset)));
24395b482a8SLen Brown 
24495b482a8SLen Brown 	/* Invoke the appropriate address_space/op_region handler */
24595b482a8SLen Brown 
2469ce81784SBob Moore 	status = acpi_ev_address_space_dispatch(rgn_desc, obj_desc,
2479ce81784SBob Moore 						function, region_offset,
2489ce81784SBob Moore 						ACPI_MUL_8(obj_desc->
2499ce81784SBob Moore 							   common_field.
25095b482a8SLen Brown 							   access_byte_width),
25195b482a8SLen Brown 						value);
25295b482a8SLen Brown 
25395b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
25495b482a8SLen Brown 		if (status == AE_NOT_IMPLEMENTED) {
25595b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
2561b74dfb2SBob Moore 				    "Region %s (ID=%u) not implemented",
25795b482a8SLen Brown 				    acpi_ut_get_region_name(rgn_desc->region.
25895b482a8SLen Brown 							    space_id),
25995b482a8SLen Brown 				    rgn_desc->region.space_id));
26095b482a8SLen Brown 		} else if (status == AE_NOT_EXIST) {
26195b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
2621b74dfb2SBob Moore 				    "Region %s (ID=%u) has no handler",
26395b482a8SLen Brown 				    acpi_ut_get_region_name(rgn_desc->region.
26495b482a8SLen Brown 							    space_id),
26595b482a8SLen Brown 				    rgn_desc->region.space_id));
26695b482a8SLen Brown 		}
26795b482a8SLen Brown 	}
26895b482a8SLen Brown 
26995b482a8SLen Brown 	return_ACPI_STATUS(status);
27095b482a8SLen Brown }
27195b482a8SLen Brown 
27295b482a8SLen Brown /*******************************************************************************
27395b482a8SLen Brown  *
27495b482a8SLen Brown  * FUNCTION:    acpi_ex_register_overflow
27595b482a8SLen Brown  *
27695b482a8SLen Brown  * PARAMETERS:  obj_desc                - Register(Field) to be written
277ba494beeSBob Moore  *              value                   - Value to be stored
27895b482a8SLen Brown  *
27995b482a8SLen Brown  * RETURN:      TRUE if value overflows the field, FALSE otherwise
28095b482a8SLen Brown  *
28195b482a8SLen Brown  * DESCRIPTION: Check if a value is out of range of the field being written.
28295b482a8SLen Brown  *              Used to check if the values written to Index and Bank registers
28395b482a8SLen Brown  *              are out of range. Normally, the value is simply truncated
28495b482a8SLen Brown  *              to fit the field, but this case is most likely a serious
28595b482a8SLen Brown  *              coding error in the ASL.
28695b482a8SLen Brown  *
28795b482a8SLen Brown  ******************************************************************************/
28895b482a8SLen Brown 
28995b482a8SLen Brown static u8
acpi_ex_register_overflow(union acpi_operand_object * obj_desc,u64 value)2905df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value)
29195b482a8SLen Brown {
29295b482a8SLen Brown 
29395b482a8SLen Brown 	if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) {
29495b482a8SLen Brown 		/*
29595b482a8SLen Brown 		 * The field is large enough to hold the maximum integer, so we can
29695b482a8SLen Brown 		 * never overflow it.
29795b482a8SLen Brown 		 */
29895b482a8SLen Brown 		return (FALSE);
29995b482a8SLen Brown 	}
30095b482a8SLen Brown 
3015df7e6cbSBob Moore 	if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) {
30295b482a8SLen Brown 		/*
30395b482a8SLen Brown 		 * The Value is larger than the maximum value that can fit into
30495b482a8SLen Brown 		 * the register.
30595b482a8SLen Brown 		 */
30646dfb09cSBob Moore 		ACPI_ERROR((AE_INFO,
30746dfb09cSBob Moore 			    "Index value 0x%8.8X%8.8X overflows field width 0x%X",
30846dfb09cSBob Moore 			    ACPI_FORMAT_UINT64(value),
30946dfb09cSBob Moore 			    obj_desc->common_field.bit_length));
31046dfb09cSBob Moore 
31195b482a8SLen Brown 		return (TRUE);
31295b482a8SLen Brown 	}
31395b482a8SLen Brown 
31495b482a8SLen Brown 	/* The Value will fit into the field with no truncation */
31595b482a8SLen Brown 
31695b482a8SLen Brown 	return (FALSE);
31795b482a8SLen Brown }
31895b482a8SLen Brown 
31995b482a8SLen Brown /*******************************************************************************
32095b482a8SLen Brown  *
32195b482a8SLen Brown  * FUNCTION:    acpi_ex_field_datum_io
32295b482a8SLen Brown  *
32395b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read
32495b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
32595b482a8SLen Brown  *                                        parent field
326ba494beeSBob Moore  *              value                   - Where to store value (must be 64 bits)
32795b482a8SLen Brown  *              read_write              - Read or Write flag
32895b482a8SLen Brown  *
32995b482a8SLen Brown  * RETURN:      Status
33095b482a8SLen Brown  *
33195b482a8SLen Brown  * DESCRIPTION: Read or Write a single datum of a field. The field_type is
33295b482a8SLen Brown  *              demultiplexed here to handle the different types of fields
33395b482a8SLen Brown  *              (buffer_field, region_field, index_field, bank_field)
33495b482a8SLen Brown  *
33595b482a8SLen Brown  ******************************************************************************/
33695b482a8SLen Brown 
33795b482a8SLen Brown static acpi_status
acpi_ex_field_datum_io(union acpi_operand_object * obj_desc,u32 field_datum_byte_offset,u64 * value,u32 read_write)33895b482a8SLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
3395df7e6cbSBob Moore 		       u32 field_datum_byte_offset, u64 *value, u32 read_write)
34095b482a8SLen Brown {
34195b482a8SLen Brown 	acpi_status status;
3425df7e6cbSBob Moore 	u64 local_value;
34395b482a8SLen Brown 
34495b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset);
34595b482a8SLen Brown 
34695b482a8SLen Brown 	if (read_write == ACPI_READ) {
34795b482a8SLen Brown 		if (!value) {
34895b482a8SLen Brown 			local_value = 0;
34995b482a8SLen Brown 
35095b482a8SLen Brown 			/* To support reads without saving return value */
35195b482a8SLen Brown 			value = &local_value;
35295b482a8SLen Brown 		}
35395b482a8SLen Brown 
35495b482a8SLen Brown 		/* Clear the entire return buffer first, [Very Important!] */
35595b482a8SLen Brown 
35695b482a8SLen Brown 		*value = 0;
35795b482a8SLen Brown 	}
35895b482a8SLen Brown 
35995b482a8SLen Brown 	/*
36095b482a8SLen Brown 	 * The four types of fields are:
36195b482a8SLen Brown 	 *
36295b482a8SLen Brown 	 * buffer_field - Read/write from/to a Buffer
36395b482a8SLen Brown 	 * region_field - Read/write from/to a Operation Region.
36495b482a8SLen Brown 	 * bank_field  - Write to a Bank Register, then read/write from/to an
36595b482a8SLen Brown 	 *               operation_region
36695b482a8SLen Brown 	 * index_field - Write to an Index Register, then read/write from/to a
36795b482a8SLen Brown 	 *               Data Register
36895b482a8SLen Brown 	 */
3693371c19cSBob Moore 	switch (obj_desc->common.type) {
37095b482a8SLen Brown 	case ACPI_TYPE_BUFFER_FIELD:
37195b482a8SLen Brown 		/*
37295b482a8SLen Brown 		 * If the buffer_field arguments have not been previously evaluated,
37395b482a8SLen Brown 		 * evaluate them now and save the results.
37495b482a8SLen Brown 		 */
37595b482a8SLen Brown 		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
37695b482a8SLen Brown 			status = acpi_ds_get_buffer_field_arguments(obj_desc);
37795b482a8SLen Brown 			if (ACPI_FAILURE(status)) {
37895b482a8SLen Brown 				return_ACPI_STATUS(status);
37995b482a8SLen Brown 			}
38095b482a8SLen Brown 		}
38195b482a8SLen Brown 
38295b482a8SLen Brown 		if (read_write == ACPI_READ) {
38395b482a8SLen Brown 			/*
38495b482a8SLen Brown 			 * Copy the data from the source buffer.
38595b482a8SLen Brown 			 * Length is the field width in bytes.
38695b482a8SLen Brown 			 */
3874fa4616eSBob Moore 			memcpy(value,
38895b482a8SLen Brown 			       (obj_desc->buffer_field.buffer_obj)->buffer.
38995b482a8SLen Brown 			       pointer +
39095b482a8SLen Brown 			       obj_desc->buffer_field.base_byte_offset +
39195b482a8SLen Brown 			       field_datum_byte_offset,
39295b482a8SLen Brown 			       obj_desc->common_field.access_byte_width);
39395b482a8SLen Brown 		} else {
39495b482a8SLen Brown 			/*
39595b482a8SLen Brown 			 * Copy the data to the target buffer.
39695b482a8SLen Brown 			 * Length is the field width in bytes.
39795b482a8SLen Brown 			 */
3984fa4616eSBob Moore 			memcpy((obj_desc->buffer_field.buffer_obj)->buffer.
39995b482a8SLen Brown 			       pointer +
40095b482a8SLen Brown 			       obj_desc->buffer_field.base_byte_offset +
40195b482a8SLen Brown 			       field_datum_byte_offset, value,
40295b482a8SLen Brown 			       obj_desc->common_field.access_byte_width);
40395b482a8SLen Brown 		}
40495b482a8SLen Brown 
40595b482a8SLen Brown 		status = AE_OK;
40695b482a8SLen Brown 		break;
40795b482a8SLen Brown 
40895b482a8SLen Brown 	case ACPI_TYPE_LOCAL_BANK_FIELD:
40995b482a8SLen Brown 		/*
41095b482a8SLen Brown 		 * Ensure that the bank_value is not beyond the capacity of
41195b482a8SLen Brown 		 * the register
41295b482a8SLen Brown 		 */
41395b482a8SLen Brown 		if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj,
4145df7e6cbSBob Moore 					      (u64) obj_desc->bank_field.
4155df7e6cbSBob Moore 					      value)) {
41695b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
41795b482a8SLen Brown 		}
41895b482a8SLen Brown 
41995b482a8SLen Brown 		/*
42095b482a8SLen Brown 		 * For bank_fields, we must write the bank_value to the bank_register
42195b482a8SLen Brown 		 * (itself a region_field) before we can access the data.
42295b482a8SLen Brown 		 */
42395b482a8SLen Brown 		status =
42495b482a8SLen Brown 		    acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj,
42595b482a8SLen Brown 					      &obj_desc->bank_field.value,
42695b482a8SLen Brown 					      sizeof(obj_desc->bank_field.
42795b482a8SLen Brown 						     value));
42895b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
42995b482a8SLen Brown 			return_ACPI_STATUS(status);
43095b482a8SLen Brown 		}
43195b482a8SLen Brown 
43295b482a8SLen Brown 		/*
43395b482a8SLen Brown 		 * Now that the Bank has been selected, fall through to the
43495b482a8SLen Brown 		 * region_field case and write the datum to the Operation Region
43595b482a8SLen Brown 		 */
43695b482a8SLen Brown 
437c1a7c2ceSNick Desaulniers 		ACPI_FALLTHROUGH;
43895b482a8SLen Brown 
43995b482a8SLen Brown 	case ACPI_TYPE_LOCAL_REGION_FIELD:
44095b482a8SLen Brown 		/*
44195b482a8SLen Brown 		 * For simple region_fields, we just directly access the owning
44295b482a8SLen Brown 		 * Operation Region.
44395b482a8SLen Brown 		 */
44495b482a8SLen Brown 		status =
44595b482a8SLen Brown 		    acpi_ex_access_region(obj_desc, field_datum_byte_offset,
44695b482a8SLen Brown 					  value, read_write);
44795b482a8SLen Brown 		break;
44895b482a8SLen Brown 
44995b482a8SLen Brown 	case ACPI_TYPE_LOCAL_INDEX_FIELD:
45095b482a8SLen Brown 		/*
45195b482a8SLen Brown 		 * Ensure that the index_value is not beyond the capacity of
45295b482a8SLen Brown 		 * the register
45395b482a8SLen Brown 		 */
45495b482a8SLen Brown 		if (acpi_ex_register_overflow(obj_desc->index_field.index_obj,
4555df7e6cbSBob Moore 					      (u64) obj_desc->index_field.
4565df7e6cbSBob Moore 					      value)) {
45795b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
45895b482a8SLen Brown 		}
45995b482a8SLen Brown 
46095b482a8SLen Brown 		/* Write the index value to the index_register (itself a region_field) */
46195b482a8SLen Brown 
46295b482a8SLen Brown 		field_datum_byte_offset += obj_desc->index_field.value;
46395b482a8SLen Brown 
46495b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
46595b482a8SLen Brown 				  "Write to Index Register: Value %8.8X\n",
46695b482a8SLen Brown 				  field_datum_byte_offset));
46795b482a8SLen Brown 
46895b482a8SLen Brown 		status =
46995b482a8SLen Brown 		    acpi_ex_insert_into_field(obj_desc->index_field.index_obj,
47095b482a8SLen Brown 					      &field_datum_byte_offset,
47195b482a8SLen Brown 					      sizeof(field_datum_byte_offset));
47295b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
47395b482a8SLen Brown 			return_ACPI_STATUS(status);
47495b482a8SLen Brown 		}
47595b482a8SLen Brown 
47695b482a8SLen Brown 		if (read_write == ACPI_READ) {
47795b482a8SLen Brown 
47895b482a8SLen Brown 			/* Read the datum from the data_register */
47995b482a8SLen Brown 
48095b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
48195b482a8SLen Brown 					  "Read from Data Register\n"));
48295b482a8SLen Brown 
48395b482a8SLen Brown 			status =
48495b482a8SLen Brown 			    acpi_ex_extract_from_field(obj_desc->index_field.
48595b482a8SLen Brown 						       data_obj, value,
4865df7e6cbSBob Moore 						       sizeof(u64));
48795b482a8SLen Brown 		} else {
48895b482a8SLen Brown 			/* Write the datum to the data_register */
48995b482a8SLen Brown 
49095b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
49195b482a8SLen Brown 					  "Write to Data Register: Value %8.8X%8.8X\n",
49295b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value)));
49395b482a8SLen Brown 
49495b482a8SLen Brown 			status =
49595b482a8SLen Brown 			    acpi_ex_insert_into_field(obj_desc->index_field.
49695b482a8SLen Brown 						      data_obj, value,
4975df7e6cbSBob Moore 						      sizeof(u64));
49895b482a8SLen Brown 		}
49995b482a8SLen Brown 		break;
50095b482a8SLen Brown 
50195b482a8SLen Brown 	default:
50295b482a8SLen Brown 
503f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u",
5043371c19cSBob Moore 			    obj_desc->common.type));
50595b482a8SLen Brown 		status = AE_AML_INTERNAL;
50695b482a8SLen Brown 		break;
50795b482a8SLen Brown 	}
50895b482a8SLen Brown 
50995b482a8SLen Brown 	if (ACPI_SUCCESS(status)) {
51095b482a8SLen Brown 		if (read_write == ACPI_READ) {
51195b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
512b27d6597SBob Moore 					  "Value Read %8.8X%8.8X, Width %u\n",
51395b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value),
51495b482a8SLen Brown 					  obj_desc->common_field.
51595b482a8SLen Brown 					  access_byte_width));
51695b482a8SLen Brown 		} else {
51795b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
518b27d6597SBob Moore 					  "Value Written %8.8X%8.8X, Width %u\n",
51995b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value),
52095b482a8SLen Brown 					  obj_desc->common_field.
52195b482a8SLen Brown 					  access_byte_width));
52295b482a8SLen Brown 		}
52395b482a8SLen Brown 	}
52495b482a8SLen Brown 
52595b482a8SLen Brown 	return_ACPI_STATUS(status);
52695b482a8SLen Brown }
52795b482a8SLen Brown 
52895b482a8SLen Brown /*******************************************************************************
52995b482a8SLen Brown  *
53095b482a8SLen Brown  * FUNCTION:    acpi_ex_write_with_update_rule
53195b482a8SLen Brown  *
53295b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be written
533ba494beeSBob Moore  *              mask                    - bitmask within field datum
53495b482a8SLen Brown  *              field_value             - Value to write
53595b482a8SLen Brown  *              field_datum_byte_offset - Offset of datum within field
53695b482a8SLen Brown  *
53795b482a8SLen Brown  * RETURN:      Status
53895b482a8SLen Brown  *
53995b482a8SLen Brown  * DESCRIPTION: Apply the field update rule to a field write
54095b482a8SLen Brown  *
54195b482a8SLen Brown  ******************************************************************************/
54295b482a8SLen Brown 
54395b482a8SLen Brown acpi_status
acpi_ex_write_with_update_rule(union acpi_operand_object * obj_desc,u64 mask,u64 field_value,u32 field_datum_byte_offset)54495b482a8SLen Brown acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
5455df7e6cbSBob Moore 			       u64 mask,
5465df7e6cbSBob Moore 			       u64 field_value, u32 field_datum_byte_offset)
54795b482a8SLen Brown {
54895b482a8SLen Brown 	acpi_status status = AE_OK;
5495df7e6cbSBob Moore 	u64 merged_value;
5505df7e6cbSBob Moore 	u64 current_value;
55195b482a8SLen Brown 
55295b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask);
55395b482a8SLen Brown 
55495b482a8SLen Brown 	/* Start with the new bits  */
55595b482a8SLen Brown 
55695b482a8SLen Brown 	merged_value = field_value;
55795b482a8SLen Brown 
55895b482a8SLen Brown 	/* If the mask is all ones, we don't need to worry about the update rule */
55995b482a8SLen Brown 
5605df7e6cbSBob Moore 	if (mask != ACPI_UINT64_MAX) {
56195b482a8SLen Brown 
56295b482a8SLen Brown 		/* Decode the update rule */
56395b482a8SLen Brown 
56495b482a8SLen Brown 		switch (obj_desc->common_field.
56595b482a8SLen Brown 			field_flags & AML_FIELD_UPDATE_RULE_MASK) {
56695b482a8SLen Brown 		case AML_FIELD_UPDATE_PRESERVE:
56795b482a8SLen Brown 			/*
56895b482a8SLen Brown 			 * Check if update rule needs to be applied (not if mask is all
56995b482a8SLen Brown 			 * ones)  The left shift drops the bits we want to ignore.
57095b482a8SLen Brown 			 */
57195b482a8SLen Brown 			if ((~mask << (ACPI_MUL_8(sizeof(mask)) -
57295b482a8SLen Brown 				       ACPI_MUL_8(obj_desc->common_field.
57395b482a8SLen Brown 						  access_byte_width))) != 0) {
57495b482a8SLen Brown 				/*
57595b482a8SLen Brown 				 * Read the current contents of the byte/word/dword containing
57695b482a8SLen Brown 				 * the field, and merge with the new field value.
57795b482a8SLen Brown 				 */
57895b482a8SLen Brown 				status =
57995b482a8SLen Brown 				    acpi_ex_field_datum_io(obj_desc,
58095b482a8SLen Brown 							   field_datum_byte_offset,
58195b482a8SLen Brown 							   &current_value,
58295b482a8SLen Brown 							   ACPI_READ);
58395b482a8SLen Brown 				if (ACPI_FAILURE(status)) {
58495b482a8SLen Brown 					return_ACPI_STATUS(status);
58595b482a8SLen Brown 				}
58695b482a8SLen Brown 
58795b482a8SLen Brown 				merged_value |= (current_value & ~mask);
58895b482a8SLen Brown 			}
58995b482a8SLen Brown 			break;
59095b482a8SLen Brown 
59195b482a8SLen Brown 		case AML_FIELD_UPDATE_WRITE_AS_ONES:
59295b482a8SLen Brown 
59395b482a8SLen Brown 			/* Set positions outside the field to all ones */
59495b482a8SLen Brown 
59595b482a8SLen Brown 			merged_value |= ~mask;
59695b482a8SLen Brown 			break;
59795b482a8SLen Brown 
59895b482a8SLen Brown 		case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
59995b482a8SLen Brown 
60095b482a8SLen Brown 			/* Set positions outside the field to all zeros */
60195b482a8SLen Brown 
60295b482a8SLen Brown 			merged_value &= mask;
60395b482a8SLen Brown 			break;
60495b482a8SLen Brown 
60595b482a8SLen Brown 		default:
60695b482a8SLen Brown 
60795b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
608f6a22b0bSBob Moore 				    "Unknown UpdateRule value: 0x%X",
6091fad8738SBob Moore 				    (obj_desc->common_field.field_flags &
61095b482a8SLen Brown 				     AML_FIELD_UPDATE_RULE_MASK)));
61195b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
61295b482a8SLen Brown 		}
61395b482a8SLen Brown 	}
61495b482a8SLen Brown 
61595b482a8SLen Brown 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
6161fad8738SBob Moore 			  "Mask %8.8X%8.8X, DatumOffset %X, Width %X, "
6171fad8738SBob Moore 			  "Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
61895b482a8SLen Brown 			  ACPI_FORMAT_UINT64(mask),
61995b482a8SLen Brown 			  field_datum_byte_offset,
62095b482a8SLen Brown 			  obj_desc->common_field.access_byte_width,
62195b482a8SLen Brown 			  ACPI_FORMAT_UINT64(field_value),
62295b482a8SLen Brown 			  ACPI_FORMAT_UINT64(merged_value)));
62395b482a8SLen Brown 
62495b482a8SLen Brown 	/* Write the merged value */
62595b482a8SLen Brown 
6261fad8738SBob Moore 	status =
6271fad8738SBob Moore 	    acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
62895b482a8SLen Brown 				   &merged_value, ACPI_WRITE);
62995b482a8SLen Brown 
63095b482a8SLen Brown 	return_ACPI_STATUS(status);
63195b482a8SLen Brown }
63295b482a8SLen Brown 
63395b482a8SLen Brown /*******************************************************************************
63495b482a8SLen Brown  *
63595b482a8SLen Brown  * FUNCTION:    acpi_ex_extract_from_field
63695b482a8SLen Brown  *
63795b482a8SLen Brown  * PARAMETERS:  obj_desc            - Field to be read
638ba494beeSBob Moore  *              buffer              - Where to store the field data
63995b482a8SLen Brown  *              buffer_length       - Length of Buffer
64095b482a8SLen Brown  *
64195b482a8SLen Brown  * RETURN:      Status
64295b482a8SLen Brown  *
64395b482a8SLen Brown  * DESCRIPTION: Retrieve the current value of the given field
64495b482a8SLen Brown  *
64595b482a8SLen Brown  ******************************************************************************/
64695b482a8SLen Brown 
64795b482a8SLen Brown acpi_status
acpi_ex_extract_from_field(union acpi_operand_object * obj_desc,void * buffer,u32 buffer_length)64895b482a8SLen Brown acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
64995b482a8SLen Brown 			   void *buffer, u32 buffer_length)
65095b482a8SLen Brown {
65195b482a8SLen Brown 	acpi_status status;
6525df7e6cbSBob Moore 	u64 raw_datum;
6535df7e6cbSBob Moore 	u64 merged_datum;
65495b482a8SLen Brown 	u32 field_offset = 0;
65595b482a8SLen Brown 	u32 buffer_offset = 0;
65695b482a8SLen Brown 	u32 buffer_tail_bits;
65795b482a8SLen Brown 	u32 datum_count;
65895b482a8SLen Brown 	u32 field_datum_count;
65909387b43SBob Moore 	u32 access_bit_width;
66095b482a8SLen Brown 	u32 i;
66195b482a8SLen Brown 
66295b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_extract_from_field);
66395b482a8SLen Brown 
66495b482a8SLen Brown 	/* Validate target buffer and clear it */
66595b482a8SLen Brown 
66695b482a8SLen Brown 	if (buffer_length <
66795b482a8SLen Brown 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) {
66895b482a8SLen Brown 		ACPI_ERROR((AE_INFO,
669f6a22b0bSBob Moore 			    "Field size %u (bits) is too large for buffer (%u)",
67095b482a8SLen Brown 			    obj_desc->common_field.bit_length, buffer_length));
67195b482a8SLen Brown 
67295b482a8SLen Brown 		return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
67395b482a8SLen Brown 	}
67409387b43SBob Moore 
6754fa4616eSBob Moore 	memset(buffer, 0, buffer_length);
67609387b43SBob Moore 	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
67709387b43SBob Moore 
67809387b43SBob Moore 	/* Handle the simple case here */
67909387b43SBob Moore 
68009387b43SBob Moore 	if ((obj_desc->common_field.start_field_bit_offset == 0) &&
68109387b43SBob Moore 	    (obj_desc->common_field.bit_length == access_bit_width)) {
68261388f9eSBob Moore 		if (buffer_length >= sizeof(u64)) {
68361388f9eSBob Moore 			status =
68461388f9eSBob Moore 			    acpi_ex_field_datum_io(obj_desc, 0, buffer,
68561388f9eSBob Moore 						   ACPI_READ);
68661388f9eSBob Moore 		} else {
68761388f9eSBob Moore 			/* Use raw_datum (u64) to handle buffers < 64 bits */
68861388f9eSBob Moore 
68961388f9eSBob Moore 			status =
69061388f9eSBob Moore 			    acpi_ex_field_datum_io(obj_desc, 0, &raw_datum,
69161388f9eSBob Moore 						   ACPI_READ);
6924fa4616eSBob Moore 			memcpy(buffer, &raw_datum, buffer_length);
69361388f9eSBob Moore 		}
69461388f9eSBob Moore 
69509387b43SBob Moore 		return_ACPI_STATUS(status);
69609387b43SBob Moore 	}
69709387b43SBob Moore 
69809387b43SBob Moore /* TBD: Move to common setup code */
69909387b43SBob Moore 
70009387b43SBob Moore 	/* Field algorithm is limited to sizeof(u64), truncate if needed */
70109387b43SBob Moore 
70209387b43SBob Moore 	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
70309387b43SBob Moore 		obj_desc->common_field.access_byte_width = sizeof(u64);
70409387b43SBob Moore 		access_bit_width = sizeof(u64) * 8;
70509387b43SBob Moore 	}
70695b482a8SLen Brown 
70795b482a8SLen Brown 	/* Compute the number of datums (access width data items) */
70895b482a8SLen Brown 
70909387b43SBob Moore 	datum_count =
71009387b43SBob Moore 	    ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
71109387b43SBob Moore 			     access_bit_width);
71209387b43SBob Moore 
71395b482a8SLen Brown 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
71495b482a8SLen Brown 					     obj_desc->common_field.
71595b482a8SLen Brown 					     start_field_bit_offset,
71695b482a8SLen Brown 					     access_bit_width);
71795b482a8SLen Brown 
71895b482a8SLen Brown 	/* Priming read from the field */
71995b482a8SLen Brown 
72095b482a8SLen Brown 	status =
72195b482a8SLen Brown 	    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
72295b482a8SLen Brown 				   ACPI_READ);
72395b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
72495b482a8SLen Brown 		return_ACPI_STATUS(status);
72595b482a8SLen Brown 	}
72695b482a8SLen Brown 	merged_datum =
72795b482a8SLen Brown 	    raw_datum >> obj_desc->common_field.start_field_bit_offset;
72895b482a8SLen Brown 
72995b482a8SLen Brown 	/* Read the rest of the field */
73095b482a8SLen Brown 
73195b482a8SLen Brown 	for (i = 1; i < field_datum_count; i++) {
73295b482a8SLen Brown 
73395b482a8SLen Brown 		/* Get next input datum from the field */
73495b482a8SLen Brown 
73595b482a8SLen Brown 		field_offset += obj_desc->common_field.access_byte_width;
7361fad8738SBob Moore 		status =
7371fad8738SBob Moore 		    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
7381fad8738SBob Moore 					   ACPI_READ);
73995b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
74095b482a8SLen Brown 			return_ACPI_STATUS(status);
74195b482a8SLen Brown 		}
74295b482a8SLen Brown 
74395b482a8SLen Brown 		/*
74495b482a8SLen Brown 		 * Merge with previous datum if necessary.
74595b482a8SLen Brown 		 *
74695b482a8SLen Brown 		 * Note: Before the shift, check if the shift value will be larger than
74795b482a8SLen Brown 		 * the integer size. If so, there is no need to perform the operation.
74895b482a8SLen Brown 		 * This avoids the differences in behavior between different compilers
74995b482a8SLen Brown 		 * concerning shift values larger than the target data width.
75095b482a8SLen Brown 		 */
75109387b43SBob Moore 		if (access_bit_width -
75209387b43SBob Moore 		    obj_desc->common_field.start_field_bit_offset <
75395b482a8SLen Brown 		    ACPI_INTEGER_BIT_SIZE) {
75495b482a8SLen Brown 			merged_datum |=
75509387b43SBob Moore 			    raw_datum << (access_bit_width -
75695b482a8SLen Brown 					  obj_desc->common_field.
75795b482a8SLen Brown 					  start_field_bit_offset);
75895b482a8SLen Brown 		}
75995b482a8SLen Brown 
76095b482a8SLen Brown 		if (i == datum_count) {
76195b482a8SLen Brown 			break;
76295b482a8SLen Brown 		}
76395b482a8SLen Brown 
76495b482a8SLen Brown 		/* Write merged datum to target buffer */
76595b482a8SLen Brown 
7664fa4616eSBob Moore 		memcpy(((char *)buffer) + buffer_offset, &merged_datum,
76795b482a8SLen Brown 		       ACPI_MIN(obj_desc->common_field.access_byte_width,
76895b482a8SLen Brown 				buffer_length - buffer_offset));
76995b482a8SLen Brown 
77095b482a8SLen Brown 		buffer_offset += obj_desc->common_field.access_byte_width;
77195b482a8SLen Brown 		merged_datum =
77295b482a8SLen Brown 		    raw_datum >> obj_desc->common_field.start_field_bit_offset;
77395b482a8SLen Brown 	}
77495b482a8SLen Brown 
77595b482a8SLen Brown 	/* Mask off any extra bits in the last datum */
77695b482a8SLen Brown 
77709387b43SBob Moore 	buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
77895b482a8SLen Brown 	if (buffer_tail_bits) {
77995b482a8SLen Brown 		merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
78095b482a8SLen Brown 	}
78195b482a8SLen Brown 
78295b482a8SLen Brown 	/* Write the last datum to the buffer */
78395b482a8SLen Brown 
7844fa4616eSBob Moore 	memcpy(((char *)buffer) + buffer_offset, &merged_datum,
78595b482a8SLen Brown 	       ACPI_MIN(obj_desc->common_field.access_byte_width,
78695b482a8SLen Brown 			buffer_length - buffer_offset));
78795b482a8SLen Brown 
78895b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
78995b482a8SLen Brown }
79095b482a8SLen Brown 
79195b482a8SLen Brown /*******************************************************************************
79295b482a8SLen Brown  *
79395b482a8SLen Brown  * FUNCTION:    acpi_ex_insert_into_field
79495b482a8SLen Brown  *
79595b482a8SLen Brown  * PARAMETERS:  obj_desc            - Field to be written
796ba494beeSBob Moore  *              buffer              - Data to be written
79795b482a8SLen Brown  *              buffer_length       - Length of Buffer
79895b482a8SLen Brown  *
79995b482a8SLen Brown  * RETURN:      Status
80095b482a8SLen Brown  *
80195b482a8SLen Brown  * DESCRIPTION: Store the Buffer contents into the given field
80295b482a8SLen Brown  *
80395b482a8SLen Brown  ******************************************************************************/
80495b482a8SLen Brown 
80595b482a8SLen Brown acpi_status
acpi_ex_insert_into_field(union acpi_operand_object * obj_desc,void * buffer,u32 buffer_length)80695b482a8SLen Brown acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
80795b482a8SLen Brown 			  void *buffer, u32 buffer_length)
80895b482a8SLen Brown {
80909387b43SBob Moore 	void *new_buffer;
81095b482a8SLen Brown 	acpi_status status;
8115df7e6cbSBob Moore 	u64 mask;
8125df7e6cbSBob Moore 	u64 width_mask;
8135df7e6cbSBob Moore 	u64 merged_datum;
8145df7e6cbSBob Moore 	u64 raw_datum = 0;
81595b482a8SLen Brown 	u32 field_offset = 0;
81695b482a8SLen Brown 	u32 buffer_offset = 0;
81795b482a8SLen Brown 	u32 buffer_tail_bits;
81895b482a8SLen Brown 	u32 datum_count;
81995b482a8SLen Brown 	u32 field_datum_count;
82009387b43SBob Moore 	u32 access_bit_width;
82195b482a8SLen Brown 	u32 required_length;
82209387b43SBob Moore 	u32 i;
82395b482a8SLen Brown 
82495b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_insert_into_field);
82595b482a8SLen Brown 
82695b482a8SLen Brown 	/* Validate input buffer */
82795b482a8SLen Brown 
82895b482a8SLen Brown 	new_buffer = NULL;
82995b482a8SLen Brown 	required_length =
83095b482a8SLen Brown 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
8311fad8738SBob Moore 
83295b482a8SLen Brown 	/*
83395b482a8SLen Brown 	 * We must have a buffer that is at least as long as the field
83495b482a8SLen Brown 	 * we are writing to. This is because individual fields are
83595b482a8SLen Brown 	 * indivisible and partial writes are not supported -- as per
83695b482a8SLen Brown 	 * the ACPI specification.
83795b482a8SLen Brown 	 */
83895b482a8SLen Brown 	if (buffer_length < required_length) {
83995b482a8SLen Brown 
84095b482a8SLen Brown 		/* We need to create a new buffer */
84195b482a8SLen Brown 
84295b482a8SLen Brown 		new_buffer = ACPI_ALLOCATE_ZEROED(required_length);
84395b482a8SLen Brown 		if (!new_buffer) {
84495b482a8SLen Brown 			return_ACPI_STATUS(AE_NO_MEMORY);
84595b482a8SLen Brown 		}
84695b482a8SLen Brown 
84795b482a8SLen Brown 		/*
84895b482a8SLen Brown 		 * Copy the original data to the new buffer, starting
84995b482a8SLen Brown 		 * at Byte zero. All unused (upper) bytes of the
85095b482a8SLen Brown 		 * buffer will be 0.
85195b482a8SLen Brown 		 */
8524fa4616eSBob Moore 		memcpy((char *)new_buffer, (char *)buffer, buffer_length);
85395b482a8SLen Brown 		buffer = new_buffer;
85495b482a8SLen Brown 		buffer_length = required_length;
85595b482a8SLen Brown 	}
85695b482a8SLen Brown 
85709387b43SBob Moore /* TBD: Move to common setup code */
85809387b43SBob Moore 
85909387b43SBob Moore 	/* Algo is limited to sizeof(u64), so cut the access_byte_width */
86009387b43SBob Moore 	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
86109387b43SBob Moore 		obj_desc->common_field.access_byte_width = sizeof(u64);
86209387b43SBob Moore 	}
86309387b43SBob Moore 
86409387b43SBob Moore 	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
86509387b43SBob Moore 
8669222aa82SLv Zheng 	/* Create the bitmasks used for bit insertion */
86795b482a8SLen Brown 
8689222aa82SLv Zheng 	width_mask = ACPI_MASK_BITS_ABOVE_64(access_bit_width);
86995b482a8SLen Brown 	mask = width_mask &
87095b482a8SLen Brown 	    ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset);
87195b482a8SLen Brown 
87295b482a8SLen Brown 	/* Compute the number of datums (access width data items) */
87395b482a8SLen Brown 
87495b482a8SLen Brown 	datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
87509387b43SBob Moore 				       access_bit_width);
87695b482a8SLen Brown 
87795b482a8SLen Brown 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
87895b482a8SLen Brown 					     obj_desc->common_field.
87995b482a8SLen Brown 					     start_field_bit_offset,
88095b482a8SLen Brown 					     access_bit_width);
88195b482a8SLen Brown 
88295b482a8SLen Brown 	/* Get initial Datum from the input buffer */
88395b482a8SLen Brown 
8844fa4616eSBob Moore 	memcpy(&raw_datum, buffer,
88595b482a8SLen Brown 	       ACPI_MIN(obj_desc->common_field.access_byte_width,
88695b482a8SLen Brown 			buffer_length - buffer_offset));
88795b482a8SLen Brown 
88895b482a8SLen Brown 	merged_datum =
88995b482a8SLen Brown 	    raw_datum << obj_desc->common_field.start_field_bit_offset;
89095b482a8SLen Brown 
89195b482a8SLen Brown 	/* Write the entire field */
89295b482a8SLen Brown 
89395b482a8SLen Brown 	for (i = 1; i < field_datum_count; i++) {
89495b482a8SLen Brown 
89595b482a8SLen Brown 		/* Write merged datum to the target field */
89695b482a8SLen Brown 
89795b482a8SLen Brown 		merged_datum &= mask;
8981fad8738SBob Moore 		status =
8991fad8738SBob Moore 		    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
90095b482a8SLen Brown 						   field_offset);
90195b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
90295b482a8SLen Brown 			goto exit;
90395b482a8SLen Brown 		}
90495b482a8SLen Brown 
90595b482a8SLen Brown 		field_offset += obj_desc->common_field.access_byte_width;
90695b482a8SLen Brown 
90795b482a8SLen Brown 		/*
90895b482a8SLen Brown 		 * Start new output datum by merging with previous input datum
90995b482a8SLen Brown 		 * if necessary.
91095b482a8SLen Brown 		 *
91195b482a8SLen Brown 		 * Note: Before the shift, check if the shift value will be larger than
91295b482a8SLen Brown 		 * the integer size. If so, there is no need to perform the operation.
91395b482a8SLen Brown 		 * This avoids the differences in behavior between different compilers
91495b482a8SLen Brown 		 * concerning shift values larger than the target data width.
91595b482a8SLen Brown 		 */
91609387b43SBob Moore 		if ((access_bit_width -
91795b482a8SLen Brown 		     obj_desc->common_field.start_field_bit_offset) <
91895b482a8SLen Brown 		    ACPI_INTEGER_BIT_SIZE) {
91995b482a8SLen Brown 			merged_datum =
92009387b43SBob Moore 			    raw_datum >> (access_bit_width -
92195b482a8SLen Brown 					  obj_desc->common_field.
92295b482a8SLen Brown 					  start_field_bit_offset);
92395b482a8SLen Brown 		} else {
92495b482a8SLen Brown 			merged_datum = 0;
92595b482a8SLen Brown 		}
92695b482a8SLen Brown 
92795b482a8SLen Brown 		mask = width_mask;
92895b482a8SLen Brown 
92995b482a8SLen Brown 		if (i == datum_count) {
93095b482a8SLen Brown 			break;
93195b482a8SLen Brown 		}
93295b482a8SLen Brown 
93395b482a8SLen Brown 		/* Get the next input datum from the buffer */
93495b482a8SLen Brown 
93595b482a8SLen Brown 		buffer_offset += obj_desc->common_field.access_byte_width;
9364fa4616eSBob Moore 		memcpy(&raw_datum, ((char *)buffer) + buffer_offset,
93795b482a8SLen Brown 		       ACPI_MIN(obj_desc->common_field.access_byte_width,
93895b482a8SLen Brown 				buffer_length - buffer_offset));
93909387b43SBob Moore 
94095b482a8SLen Brown 		merged_datum |=
94195b482a8SLen Brown 		    raw_datum << obj_desc->common_field.start_field_bit_offset;
94295b482a8SLen Brown 	}
94395b482a8SLen Brown 
94495b482a8SLen Brown 	/* Mask off any extra bits in the last datum */
94595b482a8SLen Brown 
94695b482a8SLen Brown 	buffer_tail_bits = (obj_desc->common_field.bit_length +
94795b482a8SLen Brown 			    obj_desc->common_field.start_field_bit_offset) %
94809387b43SBob Moore 	    access_bit_width;
94995b482a8SLen Brown 	if (buffer_tail_bits) {
95095b482a8SLen Brown 		mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
95195b482a8SLen Brown 	}
95295b482a8SLen Brown 
95395b482a8SLen Brown 	/* Write the last datum to the field */
95495b482a8SLen Brown 
95595b482a8SLen Brown 	merged_datum &= mask;
9561fad8738SBob Moore 	status =
9571fad8738SBob Moore 	    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
95895b482a8SLen Brown 					   field_offset);
95995b482a8SLen Brown 
96095b482a8SLen Brown exit:
96195b482a8SLen Brown 	/* Free temporary buffer if we used one */
96295b482a8SLen Brown 
96395b482a8SLen Brown 	if (new_buffer) {
96495b482a8SLen Brown 		ACPI_FREE(new_buffer);
96595b482a8SLen Brown 	}
96695b482a8SLen Brown 	return_ACPI_STATUS(status);
96795b482a8SLen Brown }
968