xref: /linux/drivers/acpi/acpica/exfldio.c (revision c3bdd5e65185f46150b3bac103b3854040487857)
195857638SErik Schmauss // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
21da177e4SLinus Torvalds /******************************************************************************
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Module Name: exfldio - Aml Field I/O
51da177e4SLinus Torvalds  *
6*840c02caSBob Moore  * Copyright (C) 2000 - 2019, Intel Corp.
71da177e4SLinus Torvalds  *
895857638SErik Schmauss  *****************************************************************************/
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #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"
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #define _COMPONENT          ACPI_EXECUTER
181da177e4SLinus Torvalds ACPI_MODULE_NAME("exfldio")
191da177e4SLinus Torvalds 
2044f6c012SRobert Moore /* Local prototypes */
2144f6c012SRobert Moore static acpi_status
224be44fcdSLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
231f86e8c1SLv Zheng 		       u32 field_datum_byte_offset, u64 *value, u32 read_write);
2444f6c012SRobert Moore 
2544f6c012SRobert Moore static u8
265df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value);
2744f6c012SRobert Moore 
2844f6c012SRobert Moore static acpi_status
294be44fcdSLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
3044f6c012SRobert Moore 		     u32 field_datum_byte_offset);
3144f6c012SRobert Moore 
321da177e4SLinus Torvalds /*******************************************************************************
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_setup_region
351da177e4SLinus Torvalds  *
3644f6c012SRobert Moore  * PARAMETERS:  obj_desc                - Field to be read or written
371da177e4SLinus Torvalds  *              field_datum_byte_offset - Byte offset of this datum within the
381da177e4SLinus Torvalds  *                                        parent field
391da177e4SLinus Torvalds  *
401da177e4SLinus Torvalds  * RETURN:      Status
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  * DESCRIPTION: Common processing for acpi_ex_extract_from_field and
431da177e4SLinus Torvalds  *              acpi_ex_insert_into_field. Initialize the Region if necessary and
441da177e4SLinus Torvalds  *              validate the request.
451da177e4SLinus Torvalds  *
461da177e4SLinus Torvalds  ******************************************************************************/
471da177e4SLinus Torvalds 
4844f6c012SRobert Moore static acpi_status
494be44fcdSLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
501da177e4SLinus Torvalds 		     u32 field_datum_byte_offset)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds 	acpi_status status = AE_OK;
531da177e4SLinus Torvalds 	union acpi_operand_object *rgn_desc;
54ec463666SBob Moore 	u8 space_id;
551da177e4SLinus Torvalds 
56b229cf92SBob Moore 	ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset);
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	rgn_desc = obj_desc->common_field.region_obj;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	/* We must have a valid region */
611da177e4SLinus Torvalds 
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,
651da177e4SLinus Torvalds 			    acpi_ut_get_object_type_name(rgn_desc)));
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
681da177e4SLinus Torvalds 	}
691da177e4SLinus Torvalds 
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 
811da177e4SLinus Torvalds 	/*
821da177e4SLinus Torvalds 	 * If the Region Address and Length have not been previously evaluated,
831da177e4SLinus Torvalds 	 * evaluate them now and save the results.
841da177e4SLinus Torvalds 	 */
851da177e4SLinus Torvalds 	if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) {
861da177e4SLinus Torvalds 		status = acpi_ds_get_region_arguments(rgn_desc);
871da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
881da177e4SLinus Torvalds 			return_ACPI_STATUS(status);
891da177e4SLinus Torvalds 		}
901da177e4SLinus Torvalds 	}
911da177e4SLinus Torvalds 
92b229cf92SBob Moore 	/*
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
95b229cf92SBob Moore 	 */
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) {
9952fc0b02SBob Moore 
1006557a49aSLin Ming 		/* SMBus or IPMI has a non-linear address space */
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 		return_ACPI_STATUS(AE_OK);
1031da177e4SLinus Torvalds 	}
1041da177e4SLinus Torvalds #ifdef ACPI_UNDER_DEVELOPMENT
1051da177e4SLinus Torvalds 	/*
1061da177e4SLinus Torvalds 	 * If the Field access is any_acc, we can now compute the optimal
1071da177e4SLinus Torvalds 	 * access (because we know know the length of the parent region)
1081da177e4SLinus Torvalds 	 */
1091da177e4SLinus Torvalds 	if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
1101da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
1111da177e4SLinus Torvalds 			return_ACPI_STATUS(status);
1121da177e4SLinus Torvalds 		}
1131da177e4SLinus Torvalds 	}
1141da177e4SLinus Torvalds #endif
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	/*
1171da177e4SLinus Torvalds 	 * Validate the request. The entire request from the byte offset for a
1181da177e4SLinus Torvalds 	 * length of one field datum (access width) must fit within the region.
1191da177e4SLinus Torvalds 	 * (Region length is specified in bytes)
1201da177e4SLinus Torvalds 	 */
1214119532cSBob Moore 	if (rgn_desc->region.length <
12209387b43SBob Moore 	    (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
1234119532cSBob Moore 	     obj_desc->common_field.access_byte_width)) {
1241da177e4SLinus Torvalds 		if (acpi_gbl_enable_interpreter_slack) {
1251da177e4SLinus Torvalds 			/*
1261da177e4SLinus Torvalds 			 * Slack mode only:  We will go ahead and allow access to this
1271da177e4SLinus Torvalds 			 * field if it is within the region length rounded up to the next
12867a119f9SBob Moore 			 * access width boundary. acpi_size cast for 64-bit compile.
1291da177e4SLinus Torvalds 			 */
1301da177e4SLinus Torvalds 			if (ACPI_ROUND_UP(rgn_desc->region.length,
1314be44fcdSLen Brown 					  obj_desc->common_field.
1324be44fcdSLen Brown 					  access_byte_width) >=
13367a119f9SBob Moore 			    ((acpi_size)obj_desc->common_field.
13467a119f9SBob Moore 			     base_byte_offset +
13567a119f9SBob Moore 			     obj_desc->common_field.access_byte_width +
13667a119f9SBob Moore 			     field_datum_byte_offset)) {
1371da177e4SLinus Torvalds 				return_ACPI_STATUS(AE_OK);
1381da177e4SLinus Torvalds 			}
1391da177e4SLinus Torvalds 		}
1401da177e4SLinus Torvalds 
1414be44fcdSLen Brown 		if (rgn_desc->region.length <
1424be44fcdSLen Brown 		    obj_desc->common_field.access_byte_width) {
1431da177e4SLinus Torvalds 			/*
1441da177e4SLinus Torvalds 			 * This is the case where the access_type (acc_word, etc.) is wider
1451da177e4SLinus Torvalds 			 * than the region itself. For example, a region of length one
1461da177e4SLinus Torvalds 			 * byte, and a field with Dword access specified.
1471da177e4SLinus Torvalds 			 */
148b8e4d893SBob Moore 			ACPI_ERROR((AE_INFO,
1491fad8738SBob Moore 				    "Field [%4.4s] access width (%u bytes) "
1501fad8738SBob Moore 				    "too large for region [%4.4s] (length %u)",
151b8e4d893SBob Moore 				    acpi_ut_get_node_name(obj_desc->
152b8e4d893SBob Moore 							  common_field.node),
153b8e4d893SBob Moore 				    obj_desc->common_field.access_byte_width,
154b8e4d893SBob Moore 				    acpi_ut_get_node_name(rgn_desc->region.
155b8e4d893SBob Moore 							  node),
156b8e4d893SBob Moore 				    rgn_desc->region.length));
1571da177e4SLinus Torvalds 		}
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 		/*
1601da177e4SLinus Torvalds 		 * Offset rounded up to next multiple of field width
1611da177e4SLinus Torvalds 		 * exceeds region length, indicate an error
1621da177e4SLinus Torvalds 		 */
163b8e4d893SBob Moore 		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)",
166b8e4d893SBob Moore 			    acpi_ut_get_node_name(obj_desc->common_field.node),
167b8e4d893SBob Moore 			    obj_desc->common_field.base_byte_offset,
168b8e4d893SBob Moore 			    field_datum_byte_offset,
169b8e4d893SBob Moore 			    obj_desc->common_field.access_byte_width,
170b8e4d893SBob Moore 			    acpi_ut_get_node_name(rgn_desc->region.node),
171b8e4d893SBob Moore 			    rgn_desc->region.length));
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 		return_ACPI_STATUS(AE_AML_REGION_LIMIT);
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	return_ACPI_STATUS(AE_OK);
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /*******************************************************************************
1801da177e4SLinus Torvalds  *
1811da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_access_region
1821da177e4SLinus Torvalds  *
18344f6c012SRobert Moore  * PARAMETERS:  obj_desc                - Field to be read
1841da177e4SLinus Torvalds  *              field_datum_byte_offset - Byte offset of this datum within the
1851da177e4SLinus Torvalds  *                                        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-
1891da177e4SLinus Torvalds  *                                        dependent flags
1901da177e4SLinus Torvalds  *
1911da177e4SLinus Torvalds  * RETURN:      Status
1921da177e4SLinus Torvalds  *
1931da177e4SLinus Torvalds  * DESCRIPTION: Read or Write a single field datum to an Operation Region.
1941da177e4SLinus Torvalds  *
1951da177e4SLinus Torvalds  ******************************************************************************/
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds acpi_status
1984be44fcdSLen Brown acpi_ex_access_region(union acpi_operand_object *obj_desc,
1995df7e6cbSBob Moore 		      u32 field_datum_byte_offset, u64 *value, u32 function)
2001da177e4SLinus Torvalds {
2011da177e4SLinus Torvalds 	acpi_status status;
2021da177e4SLinus Torvalds 	union acpi_operand_object *rgn_desc;
203f5407af3SBob Moore 	u32 region_offset;
2041da177e4SLinus Torvalds 
205b229cf92SBob Moore 	ACPI_FUNCTION_TRACE(ex_access_region);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	/*
2081da177e4SLinus Torvalds 	 * Ensure that the region operands are fully evaluated and verify
2091da177e4SLinus Torvalds 	 * the validity of the request
2101da177e4SLinus Torvalds 	 */
2111da177e4SLinus Torvalds 	status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset);
2121da177e4SLinus Torvalds 	if (ACPI_FAILURE(status)) {
2131da177e4SLinus Torvalds 		return_ACPI_STATUS(status);
2141da177e4SLinus Torvalds 	}
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 	/*
2171da177e4SLinus Torvalds 	 * The physical address of this field datum is:
2181da177e4SLinus Torvalds 	 *
2191da177e4SLinus Torvalds 	 * 1) The base of the region, plus
2201da177e4SLinus Torvalds 	 * 2) The base offset of the field, plus
2211da177e4SLinus Torvalds 	 * 3) The current offset into the field
2221da177e4SLinus Torvalds 	 */
2231da177e4SLinus Torvalds 	rgn_desc = obj_desc->common_field.region_obj;
224f5407af3SBob Moore 	region_offset =
2254be44fcdSLen Brown 	    obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	if ((function & ACPI_IO_MASK) == ACPI_READ) {
2281da177e4SLinus Torvalds 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]"));
2294be44fcdSLen Brown 	} else {
2301da177e4SLinus Torvalds 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]"));
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
234cc2080b0SLv Zheng 			      " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %8.8X%8.8X\n",
2354be44fcdSLen Brown 			      acpi_ut_get_region_name(rgn_desc->region.
2364be44fcdSLen Brown 						      space_id),
2371da177e4SLinus Torvalds 			      rgn_desc->region.space_id,
2381da177e4SLinus Torvalds 			      obj_desc->common_field.access_byte_width,
2391da177e4SLinus Torvalds 			      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)));
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	/* Invoke the appropriate address_space/op_region handler */
2451da177e4SLinus Torvalds 
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.
2504be44fcdSLen Brown 							   access_byte_width),
2514be44fcdSLen Brown 						value);
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	if (ACPI_FAILURE(status)) {
2541da177e4SLinus Torvalds 		if (status == AE_NOT_IMPLEMENTED) {
255b8e4d893SBob Moore 			ACPI_ERROR((AE_INFO,
2561b74dfb2SBob Moore 				    "Region %s (ID=%u) not implemented",
257b8e4d893SBob Moore 				    acpi_ut_get_region_name(rgn_desc->region.
2584be44fcdSLen Brown 							    space_id),
2591da177e4SLinus Torvalds 				    rgn_desc->region.space_id));
2604be44fcdSLen Brown 		} else if (status == AE_NOT_EXIST) {
261b8e4d893SBob Moore 			ACPI_ERROR((AE_INFO,
2621b74dfb2SBob Moore 				    "Region %s (ID=%u) has no handler",
263b8e4d893SBob Moore 				    acpi_ut_get_region_name(rgn_desc->region.
2644be44fcdSLen Brown 							    space_id),
2651da177e4SLinus Torvalds 				    rgn_desc->region.space_id));
2661da177e4SLinus Torvalds 		}
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	return_ACPI_STATUS(status);
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds /*******************************************************************************
2731da177e4SLinus Torvalds  *
2741da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_register_overflow
2751da177e4SLinus Torvalds  *
27644f6c012SRobert Moore  * PARAMETERS:  obj_desc                - Register(Field) to be written
277ba494beeSBob Moore  *              value                   - Value to be stored
2781da177e4SLinus Torvalds  *
2791da177e4SLinus Torvalds  * RETURN:      TRUE if value overflows the field, FALSE otherwise
2801da177e4SLinus Torvalds  *
2811da177e4SLinus Torvalds  * DESCRIPTION: Check if a value is out of range of the field being written.
2821da177e4SLinus Torvalds  *              Used to check if the values written to Index and Bank registers
2831da177e4SLinus Torvalds  *              are out of range. Normally, the value is simply truncated
2841da177e4SLinus Torvalds  *              to fit the field, but this case is most likely a serious
2851da177e4SLinus Torvalds  *              coding error in the ASL.
2861da177e4SLinus Torvalds  *
2871da177e4SLinus Torvalds  ******************************************************************************/
2881da177e4SLinus Torvalds 
28944f6c012SRobert Moore static u8
2905df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value)
2911da177e4SLinus Torvalds {
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) {
2941da177e4SLinus Torvalds 		/*
2951da177e4SLinus Torvalds 		 * The field is large enough to hold the maximum integer, so we can
2961da177e4SLinus Torvalds 		 * never overflow it.
2971da177e4SLinus Torvalds 		 */
2981da177e4SLinus Torvalds 		return (FALSE);
2991da177e4SLinus Torvalds 	}
3001da177e4SLinus Torvalds 
3015df7e6cbSBob Moore 	if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) {
3021da177e4SLinus Torvalds 		/*
3031da177e4SLinus Torvalds 		 * The Value is larger than the maximum value that can fit into
3041da177e4SLinus Torvalds 		 * the register.
3051da177e4SLinus Torvalds 		 */
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 
3111da177e4SLinus Torvalds 		return (TRUE);
3121da177e4SLinus Torvalds 	}
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	/* The Value will fit into the field with no truncation */
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	return (FALSE);
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds /*******************************************************************************
3201da177e4SLinus Torvalds  *
3211da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_field_datum_io
3221da177e4SLinus Torvalds  *
32344f6c012SRobert Moore  * PARAMETERS:  obj_desc                - Field to be read
3241da177e4SLinus Torvalds  *              field_datum_byte_offset - Byte offset of this datum within the
3251da177e4SLinus Torvalds  *                                        parent field
326ba494beeSBob Moore  *              value                   - Where to store value (must be 64 bits)
3271da177e4SLinus Torvalds  *              read_write              - Read or Write flag
3281da177e4SLinus Torvalds  *
3291da177e4SLinus Torvalds  * RETURN:      Status
3301da177e4SLinus Torvalds  *
3311da177e4SLinus Torvalds  * DESCRIPTION: Read or Write a single datum of a field. The field_type is
3321da177e4SLinus Torvalds  *              demultiplexed here to handle the different types of fields
3331da177e4SLinus Torvalds  *              (buffer_field, region_field, index_field, bank_field)
3341da177e4SLinus Torvalds  *
3351da177e4SLinus Torvalds  ******************************************************************************/
3361da177e4SLinus Torvalds 
33744f6c012SRobert Moore static acpi_status
3384be44fcdSLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
3395df7e6cbSBob Moore 		       u32 field_datum_byte_offset, u64 *value, u32 read_write)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	acpi_status status;
3425df7e6cbSBob Moore 	u64 local_value;
3431da177e4SLinus Torvalds 
344b229cf92SBob Moore 	ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	if (read_write == ACPI_READ) {
3471da177e4SLinus Torvalds 		if (!value) {
3481da177e4SLinus Torvalds 			local_value = 0;
34944f6c012SRobert Moore 
35044f6c012SRobert Moore 			/* To support reads without saving return value */
35144f6c012SRobert Moore 			value = &local_value;
3521da177e4SLinus Torvalds 		}
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds 		/* Clear the entire return buffer first, [Very Important!] */
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds 		*value = 0;
3571da177e4SLinus Torvalds 	}
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	/*
3601da177e4SLinus Torvalds 	 * The four types of fields are:
3611da177e4SLinus Torvalds 	 *
3621da177e4SLinus Torvalds 	 * buffer_field - Read/write from/to a Buffer
3631da177e4SLinus Torvalds 	 * region_field - Read/write from/to a Operation Region.
36444f6c012SRobert Moore 	 * bank_field  - Write to a Bank Register, then read/write from/to an
36544f6c012SRobert Moore 	 *               operation_region
36644f6c012SRobert Moore 	 * index_field - Write to an Index Register, then read/write from/to a
36744f6c012SRobert Moore 	 *               Data Register
3681da177e4SLinus Torvalds 	 */
3693371c19cSBob Moore 	switch (obj_desc->common.type) {
3701da177e4SLinus Torvalds 	case ACPI_TYPE_BUFFER_FIELD:
3711da177e4SLinus Torvalds 		/*
3721da177e4SLinus Torvalds 		 * If the buffer_field arguments have not been previously evaluated,
3731da177e4SLinus Torvalds 		 * evaluate them now and save the results.
3741da177e4SLinus Torvalds 		 */
3751da177e4SLinus Torvalds 		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
3761da177e4SLinus Torvalds 			status = acpi_ds_get_buffer_field_arguments(obj_desc);
3771da177e4SLinus Torvalds 			if (ACPI_FAILURE(status)) {
3781da177e4SLinus Torvalds 				return_ACPI_STATUS(status);
3791da177e4SLinus Torvalds 			}
3801da177e4SLinus Torvalds 		}
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 		if (read_write == ACPI_READ) {
3831da177e4SLinus Torvalds 			/*
3841da177e4SLinus Torvalds 			 * Copy the data from the source buffer.
3851da177e4SLinus Torvalds 			 * Length is the field width in bytes.
3861da177e4SLinus Torvalds 			 */
3874fa4616eSBob Moore 			memcpy(value,
3884be44fcdSLen Brown 			       (obj_desc->buffer_field.buffer_obj)->buffer.
3894be44fcdSLen Brown 			       pointer +
39044f6c012SRobert Moore 			       obj_desc->buffer_field.base_byte_offset +
39144f6c012SRobert Moore 			       field_datum_byte_offset,
3921da177e4SLinus Torvalds 			       obj_desc->common_field.access_byte_width);
3934be44fcdSLen Brown 		} else {
3941da177e4SLinus Torvalds 			/*
3951da177e4SLinus Torvalds 			 * Copy the data to the target buffer.
3961da177e4SLinus Torvalds 			 * Length is the field width in bytes.
3971da177e4SLinus Torvalds 			 */
3984fa4616eSBob Moore 			memcpy((obj_desc->buffer_field.buffer_obj)->buffer.
3994be44fcdSLen Brown 			       pointer +
40044f6c012SRobert Moore 			       obj_desc->buffer_field.base_byte_offset +
4014be44fcdSLen Brown 			       field_datum_byte_offset, value,
4024be44fcdSLen Brown 			       obj_desc->common_field.access_byte_width);
4031da177e4SLinus Torvalds 		}
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 		status = AE_OK;
4061da177e4SLinus Torvalds 		break;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	case ACPI_TYPE_LOCAL_BANK_FIELD:
40944f6c012SRobert Moore 		/*
41044f6c012SRobert Moore 		 * Ensure that the bank_value is not beyond the capacity of
41144f6c012SRobert Moore 		 * the register
41244f6c012SRobert Moore 		 */
4131da177e4SLinus Torvalds 		if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj,
4145df7e6cbSBob Moore 					      (u64) obj_desc->bank_field.
4155df7e6cbSBob Moore 					      value)) {
4161da177e4SLinus Torvalds 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
4171da177e4SLinus Torvalds 		}
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 		/*
4201da177e4SLinus Torvalds 		 * For bank_fields, we must write the bank_value to the bank_register
4211da177e4SLinus Torvalds 		 * (itself a region_field) before we can access the data.
4221da177e4SLinus Torvalds 		 */
4234be44fcdSLen Brown 		status =
4244be44fcdSLen Brown 		    acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj,
4251da177e4SLinus Torvalds 					      &obj_desc->bank_field.value,
4264be44fcdSLen Brown 					      sizeof(obj_desc->bank_field.
4274be44fcdSLen Brown 						     value));
4281da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
4291da177e4SLinus Torvalds 			return_ACPI_STATUS(status);
4301da177e4SLinus Torvalds 		}
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 		/*
4331da177e4SLinus Torvalds 		 * Now that the Bank has been selected, fall through to the
4341da177e4SLinus Torvalds 		 * region_field case and write the datum to the Operation Region
4351da177e4SLinus Torvalds 		 */
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 		/*lint -fallthrough */
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	case ACPI_TYPE_LOCAL_REGION_FIELD:
4401da177e4SLinus Torvalds 		/*
4411da177e4SLinus Torvalds 		 * For simple region_fields, we just directly access the owning
4421da177e4SLinus Torvalds 		 * Operation Region.
4431da177e4SLinus Torvalds 		 */
4444be44fcdSLen Brown 		status =
4454be44fcdSLen Brown 		    acpi_ex_access_region(obj_desc, field_datum_byte_offset,
4464be44fcdSLen Brown 					  value, read_write);
4471da177e4SLinus Torvalds 		break;
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	case ACPI_TYPE_LOCAL_INDEX_FIELD:
45044f6c012SRobert Moore 		/*
45144f6c012SRobert Moore 		 * Ensure that the index_value is not beyond the capacity of
45244f6c012SRobert Moore 		 * the register
45344f6c012SRobert Moore 		 */
4541da177e4SLinus Torvalds 		if (acpi_ex_register_overflow(obj_desc->index_field.index_obj,
4555df7e6cbSBob Moore 					      (u64) obj_desc->index_field.
4565df7e6cbSBob Moore 					      value)) {
4571da177e4SLinus Torvalds 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
4581da177e4SLinus Torvalds 		}
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds 		/* Write the index value to the index_register (itself a region_field) */
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 		field_datum_byte_offset += obj_desc->index_field.value;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
4651da177e4SLinus Torvalds 				  "Write to Index Register: Value %8.8X\n",
4661da177e4SLinus Torvalds 				  field_datum_byte_offset));
4671da177e4SLinus Torvalds 
4684be44fcdSLen Brown 		status =
4694be44fcdSLen Brown 		    acpi_ex_insert_into_field(obj_desc->index_field.index_obj,
4701da177e4SLinus Torvalds 					      &field_datum_byte_offset,
4711da177e4SLinus Torvalds 					      sizeof(field_datum_byte_offset));
4721da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
4731da177e4SLinus Torvalds 			return_ACPI_STATUS(status);
4741da177e4SLinus Torvalds 		}
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 		if (read_write == ACPI_READ) {
47752fc0b02SBob Moore 
4781da177e4SLinus Torvalds 			/* Read the datum from the data_register */
4791da177e4SLinus Torvalds 
480e9a8c6a9SBob Moore 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
481e9a8c6a9SBob Moore 					  "Read from Data Register\n"));
482e9a8c6a9SBob Moore 
4834be44fcdSLen Brown 			status =
4844be44fcdSLen Brown 			    acpi_ex_extract_from_field(obj_desc->index_field.
4854be44fcdSLen Brown 						       data_obj, value,
4865df7e6cbSBob Moore 						       sizeof(u64));
4874be44fcdSLen Brown 		} else {
4881da177e4SLinus Torvalds 			/* Write the datum to the data_register */
4891da177e4SLinus Torvalds 
490e9a8c6a9SBob Moore 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
491e9a8c6a9SBob Moore 					  "Write to Data Register: Value %8.8X%8.8X\n",
492e9a8c6a9SBob Moore 					  ACPI_FORMAT_UINT64(*value)));
493e9a8c6a9SBob Moore 
4944be44fcdSLen Brown 			status =
4954be44fcdSLen Brown 			    acpi_ex_insert_into_field(obj_desc->index_field.
4964be44fcdSLen Brown 						      data_obj, value,
4975df7e6cbSBob Moore 						      sizeof(u64));
4981da177e4SLinus Torvalds 		}
4991da177e4SLinus Torvalds 		break;
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	default:
5021da177e4SLinus Torvalds 
503f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u",
5043371c19cSBob Moore 			    obj_desc->common.type));
5051da177e4SLinus Torvalds 		status = AE_AML_INTERNAL;
5061da177e4SLinus Torvalds 		break;
5071da177e4SLinus Torvalds 	}
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	if (ACPI_SUCCESS(status)) {
5101da177e4SLinus Torvalds 		if (read_write == ACPI_READ) {
51144f6c012SRobert Moore 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
512b27d6597SBob Moore 					  "Value Read %8.8X%8.8X, Width %u\n",
5131da177e4SLinus Torvalds 					  ACPI_FORMAT_UINT64(*value),
5144be44fcdSLen Brown 					  obj_desc->common_field.
5154be44fcdSLen Brown 					  access_byte_width));
5164be44fcdSLen Brown 		} else {
51744f6c012SRobert Moore 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
518b27d6597SBob Moore 					  "Value Written %8.8X%8.8X, Width %u\n",
5191da177e4SLinus Torvalds 					  ACPI_FORMAT_UINT64(*value),
5204be44fcdSLen Brown 					  obj_desc->common_field.
5214be44fcdSLen Brown 					  access_byte_width));
5221da177e4SLinus Torvalds 		}
5231da177e4SLinus Torvalds 	}
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	return_ACPI_STATUS(status);
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds /*******************************************************************************
5291da177e4SLinus Torvalds  *
5301da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_write_with_update_rule
5311da177e4SLinus Torvalds  *
53244f6c012SRobert Moore  * PARAMETERS:  obj_desc                - Field to be written
533ba494beeSBob Moore  *              mask                    - bitmask within field datum
53444f6c012SRobert Moore  *              field_value             - Value to write
53544f6c012SRobert Moore  *              field_datum_byte_offset - Offset of datum within field
5361da177e4SLinus Torvalds  *
5371da177e4SLinus Torvalds  * RETURN:      Status
5381da177e4SLinus Torvalds  *
5391da177e4SLinus Torvalds  * DESCRIPTION: Apply the field update rule to a field write
5401da177e4SLinus Torvalds  *
5411da177e4SLinus Torvalds  ******************************************************************************/
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds acpi_status
5444be44fcdSLen 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)
5471da177e4SLinus Torvalds {
5481da177e4SLinus Torvalds 	acpi_status status = AE_OK;
5495df7e6cbSBob Moore 	u64 merged_value;
5505df7e6cbSBob Moore 	u64 current_value;
5511da177e4SLinus Torvalds 
552b229cf92SBob Moore 	ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask);
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	/* Start with the new bits  */
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	merged_value = field_value;
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	/* If the mask is all ones, we don't need to worry about the update rule */
5591da177e4SLinus Torvalds 
5605df7e6cbSBob Moore 	if (mask != ACPI_UINT64_MAX) {
56152fc0b02SBob Moore 
5621da177e4SLinus Torvalds 		/* Decode the update rule */
5631da177e4SLinus Torvalds 
5644be44fcdSLen Brown 		switch (obj_desc->common_field.
5654be44fcdSLen Brown 			field_flags & AML_FIELD_UPDATE_RULE_MASK) {
5661da177e4SLinus Torvalds 		case AML_FIELD_UPDATE_PRESERVE:
5671da177e4SLinus Torvalds 			/*
5681da177e4SLinus Torvalds 			 * Check if update rule needs to be applied (not if mask is all
5691da177e4SLinus Torvalds 			 * ones)  The left shift drops the bits we want to ignore.
5701da177e4SLinus Torvalds 			 */
5711da177e4SLinus Torvalds 			if ((~mask << (ACPI_MUL_8(sizeof(mask)) -
5724be44fcdSLen Brown 				       ACPI_MUL_8(obj_desc->common_field.
5734be44fcdSLen Brown 						  access_byte_width))) != 0) {
5741da177e4SLinus Torvalds 				/*
5751da177e4SLinus Torvalds 				 * Read the current contents of the byte/word/dword containing
5761da177e4SLinus Torvalds 				 * the field, and merge with the new field value.
5771da177e4SLinus Torvalds 				 */
5784be44fcdSLen Brown 				status =
5794be44fcdSLen Brown 				    acpi_ex_field_datum_io(obj_desc,
5804be44fcdSLen Brown 							   field_datum_byte_offset,
5814be44fcdSLen Brown 							   &current_value,
5824be44fcdSLen Brown 							   ACPI_READ);
5831da177e4SLinus Torvalds 				if (ACPI_FAILURE(status)) {
5841da177e4SLinus Torvalds 					return_ACPI_STATUS(status);
5851da177e4SLinus Torvalds 				}
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 				merged_value |= (current_value & ~mask);
5881da177e4SLinus Torvalds 			}
5891da177e4SLinus Torvalds 			break;
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds 		case AML_FIELD_UPDATE_WRITE_AS_ONES:
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 			/* Set positions outside the field to all ones */
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 			merged_value |= ~mask;
5961da177e4SLinus Torvalds 			break;
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 		case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 			/* Set positions outside the field to all zeros */
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 			merged_value &= mask;
6031da177e4SLinus Torvalds 			break;
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 		default:
6061da177e4SLinus Torvalds 
607b8e4d893SBob Moore 			ACPI_ERROR((AE_INFO,
608f6a22b0bSBob Moore 				    "Unknown UpdateRule value: 0x%X",
6091fad8738SBob Moore 				    (obj_desc->common_field.field_flags &
6104be44fcdSLen Brown 				     AML_FIELD_UPDATE_RULE_MASK)));
6111da177e4SLinus Torvalds 			return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
6121da177e4SLinus Torvalds 		}
6131da177e4SLinus Torvalds 	}
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds 	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",
6181da177e4SLinus Torvalds 			  ACPI_FORMAT_UINT64(mask),
6191da177e4SLinus Torvalds 			  field_datum_byte_offset,
6201da177e4SLinus Torvalds 			  obj_desc->common_field.access_byte_width,
6211da177e4SLinus Torvalds 			  ACPI_FORMAT_UINT64(field_value),
6221da177e4SLinus Torvalds 			  ACPI_FORMAT_UINT64(merged_value)));
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* Write the merged value */
6251da177e4SLinus Torvalds 
6261fad8738SBob Moore 	status =
6271fad8738SBob Moore 	    acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
6281da177e4SLinus Torvalds 				   &merged_value, ACPI_WRITE);
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	return_ACPI_STATUS(status);
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds /*******************************************************************************
6341da177e4SLinus Torvalds  *
6351da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_extract_from_field
6361da177e4SLinus Torvalds  *
6371da177e4SLinus Torvalds  * PARAMETERS:  obj_desc            - Field to be read
638ba494beeSBob Moore  *              buffer              - Where to store the field data
6391da177e4SLinus Torvalds  *              buffer_length       - Length of Buffer
6401da177e4SLinus Torvalds  *
6411da177e4SLinus Torvalds  * RETURN:      Status
6421da177e4SLinus Torvalds  *
6431da177e4SLinus Torvalds  * DESCRIPTION: Retrieve the current value of the given field
6441da177e4SLinus Torvalds  *
6451da177e4SLinus Torvalds  ******************************************************************************/
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds acpi_status
6484be44fcdSLen Brown acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
6494be44fcdSLen Brown 			   void *buffer, u32 buffer_length)
6501da177e4SLinus Torvalds {
6511da177e4SLinus Torvalds 	acpi_status status;
6525df7e6cbSBob Moore 	u64 raw_datum;
6535df7e6cbSBob Moore 	u64 merged_datum;
6541da177e4SLinus Torvalds 	u32 field_offset = 0;
6551da177e4SLinus Torvalds 	u32 buffer_offset = 0;
6561da177e4SLinus Torvalds 	u32 buffer_tail_bits;
6571da177e4SLinus Torvalds 	u32 datum_count;
6581da177e4SLinus Torvalds 	u32 field_datum_count;
65909387b43SBob Moore 	u32 access_bit_width;
6601da177e4SLinus Torvalds 	u32 i;
6611da177e4SLinus Torvalds 
662b229cf92SBob Moore 	ACPI_FUNCTION_TRACE(ex_extract_from_field);
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	/* Validate target buffer and clear it */
6651da177e4SLinus Torvalds 
6664be44fcdSLen Brown 	if (buffer_length <
6674be44fcdSLen Brown 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) {
668b8e4d893SBob Moore 		ACPI_ERROR((AE_INFO,
669f6a22b0bSBob Moore 			    "Field size %u (bits) is too large for buffer (%u)",
670b8e4d893SBob Moore 			    obj_desc->common_field.bit_length, buffer_length));
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 		return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
6731da177e4SLinus Torvalds 	}
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 	}
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 	/* Compute the number of datums (access width data items) */
7081da177e4SLinus Torvalds 
70909387b43SBob Moore 	datum_count =
71009387b43SBob Moore 	    ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
71109387b43SBob Moore 			     access_bit_width);
71209387b43SBob Moore 
7134be44fcdSLen Brown 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
7144be44fcdSLen Brown 					     obj_desc->common_field.
7154be44fcdSLen Brown 					     start_field_bit_offset,
7164be44fcdSLen Brown 					     access_bit_width);
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 	/* Priming read from the field */
7191da177e4SLinus Torvalds 
7204be44fcdSLen Brown 	status =
7214be44fcdSLen Brown 	    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
7224be44fcdSLen Brown 				   ACPI_READ);
7231da177e4SLinus Torvalds 	if (ACPI_FAILURE(status)) {
7241da177e4SLinus Torvalds 		return_ACPI_STATUS(status);
7251da177e4SLinus Torvalds 	}
7264be44fcdSLen Brown 	merged_datum =
7274be44fcdSLen Brown 	    raw_datum >> obj_desc->common_field.start_field_bit_offset;
7281da177e4SLinus Torvalds 
7291da177e4SLinus Torvalds 	/* Read the rest of the field */
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 	for (i = 1; i < field_datum_count; i++) {
73252fc0b02SBob Moore 
7331da177e4SLinus Torvalds 		/* Get next input datum from the field */
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 		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);
7391da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
7401da177e4SLinus Torvalds 			return_ACPI_STATUS(status);
7411da177e4SLinus Torvalds 		}
7421da177e4SLinus Torvalds 
743967440e3SBob Moore 		/*
744967440e3SBob Moore 		 * Merge with previous datum if necessary.
745967440e3SBob Moore 		 *
746967440e3SBob Moore 		 * Note: Before the shift, check if the shift value will be larger than
747967440e3SBob Moore 		 * the integer size. If so, there is no need to perform the operation.
748967440e3SBob Moore 		 * This avoids the differences in behavior between different compilers
749967440e3SBob Moore 		 * concerning shift values larger than the target data width.
750967440e3SBob Moore 		 */
75109387b43SBob Moore 		if (access_bit_width -
75209387b43SBob Moore 		    obj_desc->common_field.start_field_bit_offset <
753967440e3SBob Moore 		    ACPI_INTEGER_BIT_SIZE) {
754967440e3SBob Moore 			merged_datum |=
75509387b43SBob Moore 			    raw_datum << (access_bit_width -
756967440e3SBob Moore 					  obj_desc->common_field.
757967440e3SBob Moore 					  start_field_bit_offset);
758967440e3SBob Moore 		}
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds 		if (i == datum_count) {
7611da177e4SLinus Torvalds 			break;
7621da177e4SLinus Torvalds 		}
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 		/* Write merged datum to target buffer */
7651da177e4SLinus Torvalds 
7664fa4616eSBob Moore 		memcpy(((char *)buffer) + buffer_offset, &merged_datum,
7671da177e4SLinus Torvalds 		       ACPI_MIN(obj_desc->common_field.access_byte_width,
7681da177e4SLinus Torvalds 				buffer_length - buffer_offset));
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds 		buffer_offset += obj_desc->common_field.access_byte_width;
7714be44fcdSLen Brown 		merged_datum =
7724be44fcdSLen Brown 		    raw_datum >> obj_desc->common_field.start_field_bit_offset;
7731da177e4SLinus Torvalds 	}
7741da177e4SLinus Torvalds 
7751da177e4SLinus Torvalds 	/* Mask off any extra bits in the last datum */
7761da177e4SLinus Torvalds 
77709387b43SBob Moore 	buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
7781da177e4SLinus Torvalds 	if (buffer_tail_bits) {
7791da177e4SLinus Torvalds 		merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
7801da177e4SLinus Torvalds 	}
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	/* Write the last datum to the buffer */
7831da177e4SLinus Torvalds 
7844fa4616eSBob Moore 	memcpy(((char *)buffer) + buffer_offset, &merged_datum,
7851da177e4SLinus Torvalds 	       ACPI_MIN(obj_desc->common_field.access_byte_width,
7861da177e4SLinus Torvalds 			buffer_length - buffer_offset));
7871da177e4SLinus Torvalds 
7881da177e4SLinus Torvalds 	return_ACPI_STATUS(AE_OK);
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds 
7911da177e4SLinus Torvalds /*******************************************************************************
7921da177e4SLinus Torvalds  *
7931da177e4SLinus Torvalds  * FUNCTION:    acpi_ex_insert_into_field
7941da177e4SLinus Torvalds  *
7951da177e4SLinus Torvalds  * PARAMETERS:  obj_desc            - Field to be written
796ba494beeSBob Moore  *              buffer              - Data to be written
7971da177e4SLinus Torvalds  *              buffer_length       - Length of Buffer
7981da177e4SLinus Torvalds  *
7991da177e4SLinus Torvalds  * RETURN:      Status
8001da177e4SLinus Torvalds  *
8011da177e4SLinus Torvalds  * DESCRIPTION: Store the Buffer contents into the given field
8021da177e4SLinus Torvalds  *
8031da177e4SLinus Torvalds  ******************************************************************************/
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds acpi_status
8064be44fcdSLen Brown acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
8074be44fcdSLen Brown 			  void *buffer, u32 buffer_length)
8081da177e4SLinus Torvalds {
80909387b43SBob Moore 	void *new_buffer;
8101da177e4SLinus Torvalds 	acpi_status status;
8115df7e6cbSBob Moore 	u64 mask;
8125df7e6cbSBob Moore 	u64 width_mask;
8135df7e6cbSBob Moore 	u64 merged_datum;
8145df7e6cbSBob Moore 	u64 raw_datum = 0;
8151da177e4SLinus Torvalds 	u32 field_offset = 0;
8161da177e4SLinus Torvalds 	u32 buffer_offset = 0;
8171da177e4SLinus Torvalds 	u32 buffer_tail_bits;
8181da177e4SLinus Torvalds 	u32 datum_count;
8191da177e4SLinus Torvalds 	u32 field_datum_count;
82009387b43SBob Moore 	u32 access_bit_width;
8219aa6169fSBob Moore 	u32 required_length;
82209387b43SBob Moore 	u32 i;
8231da177e4SLinus Torvalds 
824b229cf92SBob Moore 	ACPI_FUNCTION_TRACE(ex_insert_into_field);
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds 	/* Validate input buffer */
8271da177e4SLinus Torvalds 
8289aa6169fSBob Moore 	new_buffer = NULL;
8299aa6169fSBob Moore 	required_length =
8309aa6169fSBob Moore 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
8311fad8738SBob Moore 
8329aa6169fSBob Moore 	/*
8339aa6169fSBob Moore 	 * We must have a buffer that is at least as long as the field
8349aa6169fSBob Moore 	 * we are writing to. This is because individual fields are
8359aa6169fSBob Moore 	 * indivisible and partial writes are not supported -- as per
8369aa6169fSBob Moore 	 * the ACPI specification.
8379aa6169fSBob Moore 	 */
8389aa6169fSBob Moore 	if (buffer_length < required_length) {
8391da177e4SLinus Torvalds 
8409aa6169fSBob Moore 		/* We need to create a new buffer */
8419aa6169fSBob Moore 
8429aa6169fSBob Moore 		new_buffer = ACPI_ALLOCATE_ZEROED(required_length);
8439aa6169fSBob Moore 		if (!new_buffer) {
8449aa6169fSBob Moore 			return_ACPI_STATUS(AE_NO_MEMORY);
8459aa6169fSBob Moore 		}
8469aa6169fSBob Moore 
8479aa6169fSBob Moore 		/*
8489aa6169fSBob Moore 		 * Copy the original data to the new buffer, starting
8499aa6169fSBob Moore 		 * at Byte zero. All unused (upper) bytes of the
8509aa6169fSBob Moore 		 * buffer will be 0.
8519aa6169fSBob Moore 		 */
8524fa4616eSBob Moore 		memcpy((char *)new_buffer, (char *)buffer, buffer_length);
8539aa6169fSBob Moore 		buffer = new_buffer;
8549aa6169fSBob Moore 		buffer_length = required_length;
8551da177e4SLinus Torvalds 	}
8561da177e4SLinus Torvalds 
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 */
867967440e3SBob Moore 
8689222aa82SLv Zheng 	width_mask = ACPI_MASK_BITS_ABOVE_64(access_bit_width);
869967440e3SBob Moore 	mask = width_mask &
870967440e3SBob Moore 	    ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset);
871967440e3SBob Moore 
872967440e3SBob Moore 	/* Compute the number of datums (access width data items) */
8734119532cSBob Moore 
8744119532cSBob Moore 	datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
87509387b43SBob Moore 				       access_bit_width);
8764119532cSBob Moore 
8774119532cSBob Moore 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
8784119532cSBob Moore 					     obj_desc->common_field.
8794119532cSBob Moore 					     start_field_bit_offset,
8804119532cSBob Moore 					     access_bit_width);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	/* Get initial Datum from the input buffer */
8831da177e4SLinus Torvalds 
8844fa4616eSBob Moore 	memcpy(&raw_datum, buffer,
8851da177e4SLinus Torvalds 	       ACPI_MIN(obj_desc->common_field.access_byte_width,
8861da177e4SLinus Torvalds 			buffer_length - buffer_offset));
8871da177e4SLinus Torvalds 
8884be44fcdSLen Brown 	merged_datum =
8894be44fcdSLen Brown 	    raw_datum << obj_desc->common_field.start_field_bit_offset;
8901da177e4SLinus Torvalds 
8911da177e4SLinus Torvalds 	/* Write the entire field */
8921da177e4SLinus Torvalds 
8931da177e4SLinus Torvalds 	for (i = 1; i < field_datum_count; i++) {
89452fc0b02SBob Moore 
8951da177e4SLinus Torvalds 		/* Write merged datum to the target field */
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 		merged_datum &= mask;
8981fad8738SBob Moore 		status =
8991fad8738SBob Moore 		    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
9004be44fcdSLen Brown 						   field_offset);
9011da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
9029aa6169fSBob Moore 			goto exit;
9031da177e4SLinus Torvalds 		}
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 		field_offset += obj_desc->common_field.access_byte_width;
906967440e3SBob Moore 
907967440e3SBob Moore 		/*
908967440e3SBob Moore 		 * Start new output datum by merging with previous input datum
909967440e3SBob Moore 		 * if necessary.
910967440e3SBob Moore 		 *
911967440e3SBob Moore 		 * Note: Before the shift, check if the shift value will be larger than
912967440e3SBob Moore 		 * the integer size. If so, there is no need to perform the operation.
913967440e3SBob Moore 		 * This avoids the differences in behavior between different compilers
914967440e3SBob Moore 		 * concerning shift values larger than the target data width.
915967440e3SBob Moore 		 */
91609387b43SBob Moore 		if ((access_bit_width -
917967440e3SBob Moore 		     obj_desc->common_field.start_field_bit_offset) <
918967440e3SBob Moore 		    ACPI_INTEGER_BIT_SIZE) {
919967440e3SBob Moore 			merged_datum =
92009387b43SBob Moore 			    raw_datum >> (access_bit_width -
921967440e3SBob Moore 					  obj_desc->common_field.
922967440e3SBob Moore 					  start_field_bit_offset);
923967440e3SBob Moore 		} else {
924967440e3SBob Moore 			merged_datum = 0;
925967440e3SBob Moore 		}
926967440e3SBob Moore 
9274c90ece2SBob Moore 		mask = width_mask;
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds 		if (i == datum_count) {
9301da177e4SLinus Torvalds 			break;
9311da177e4SLinus Torvalds 		}
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 		/* Get the next input datum from the buffer */
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 		buffer_offset += obj_desc->common_field.access_byte_width;
9364fa4616eSBob Moore 		memcpy(&raw_datum, ((char *)buffer) + buffer_offset,
9371da177e4SLinus Torvalds 		       ACPI_MIN(obj_desc->common_field.access_byte_width,
9381da177e4SLinus Torvalds 				buffer_length - buffer_offset));
93909387b43SBob Moore 
9404be44fcdSLen Brown 		merged_datum |=
9414be44fcdSLen Brown 		    raw_datum << obj_desc->common_field.start_field_bit_offset;
9421da177e4SLinus Torvalds 	}
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	/* Mask off any extra bits in the last datum */
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	buffer_tail_bits = (obj_desc->common_field.bit_length +
94744f6c012SRobert Moore 			    obj_desc->common_field.start_field_bit_offset) %
94809387b43SBob Moore 	    access_bit_width;
9491da177e4SLinus Torvalds 	if (buffer_tail_bits) {
9501da177e4SLinus Torvalds 		mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
9511da177e4SLinus Torvalds 	}
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/* Write the last datum to the field */
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds 	merged_datum &= mask;
9561fad8738SBob Moore 	status =
9571fad8738SBob Moore 	    acpi_ex_write_with_update_rule(obj_desc, mask, merged_datum,
9584be44fcdSLen Brown 					   field_offset);
9591da177e4SLinus Torvalds 
9609aa6169fSBob Moore exit:
9619aa6169fSBob Moore 	/* Free temporary buffer if we used one */
9629aa6169fSBob Moore 
9639aa6169fSBob Moore 	if (new_buffer) {
9649aa6169fSBob Moore 		ACPI_FREE(new_buffer);
9659aa6169fSBob Moore 	}
9661da177e4SLinus Torvalds 	return_ACPI_STATUS(status);
9671da177e4SLinus Torvalds }
968