xref: /openbmc/linux/drivers/acpi/acpica/exfldio.c (revision ec463666)
195b482a8SLen Brown /******************************************************************************
295b482a8SLen Brown  *
395b482a8SLen Brown  * Module Name: exfldio - Aml Field I/O
495b482a8SLen Brown  *
595b482a8SLen Brown  *****************************************************************************/
695b482a8SLen Brown 
795b482a8SLen Brown /*
8b4e104eaSBob Moore  * Copyright (C) 2000 - 2011, Intel Corp.
995b482a8SLen Brown  * All rights reserved.
1095b482a8SLen Brown  *
1195b482a8SLen Brown  * Redistribution and use in source and binary forms, with or without
1295b482a8SLen Brown  * modification, are permitted provided that the following conditions
1395b482a8SLen Brown  * are met:
1495b482a8SLen Brown  * 1. Redistributions of source code must retain the above copyright
1595b482a8SLen Brown  *    notice, this list of conditions, and the following disclaimer,
1695b482a8SLen Brown  *    without modification.
1795b482a8SLen Brown  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1895b482a8SLen Brown  *    substantially similar to the "NO WARRANTY" disclaimer below
1995b482a8SLen Brown  *    ("Disclaimer") and any redistribution must be conditioned upon
2095b482a8SLen Brown  *    including a substantially similar Disclaimer requirement for further
2195b482a8SLen Brown  *    binary redistribution.
2295b482a8SLen Brown  * 3. Neither the names of the above-listed copyright holders nor the names
2395b482a8SLen Brown  *    of any contributors may be used to endorse or promote products derived
2495b482a8SLen Brown  *    from this software without specific prior written permission.
2595b482a8SLen Brown  *
2695b482a8SLen Brown  * Alternatively, this software may be distributed under the terms of the
2795b482a8SLen Brown  * GNU General Public License ("GPL") version 2 as published by the Free
2895b482a8SLen Brown  * Software Foundation.
2995b482a8SLen Brown  *
3095b482a8SLen Brown  * NO WARRANTY
3195b482a8SLen Brown  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3295b482a8SLen Brown  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3395b482a8SLen Brown  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
3495b482a8SLen Brown  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3595b482a8SLen Brown  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3695b482a8SLen Brown  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3795b482a8SLen Brown  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3895b482a8SLen Brown  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3995b482a8SLen Brown  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
4095b482a8SLen Brown  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4195b482a8SLen Brown  * POSSIBILITY OF SUCH DAMAGES.
4295b482a8SLen Brown  */
4395b482a8SLen Brown 
4495b482a8SLen Brown #include <acpi/acpi.h>
45e2f7a777SLen Brown #include "accommon.h"
46e2f7a777SLen Brown #include "acinterp.h"
47e2f7a777SLen Brown #include "amlcode.h"
48e2f7a777SLen Brown #include "acevents.h"
49e2f7a777SLen Brown #include "acdispat.h"
5095b482a8SLen Brown 
5195b482a8SLen Brown #define _COMPONENT          ACPI_EXECUTER
5295b482a8SLen Brown ACPI_MODULE_NAME("exfldio")
5395b482a8SLen Brown 
5495b482a8SLen Brown /* Local prototypes */
5595b482a8SLen Brown static acpi_status
5695b482a8SLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
5795b482a8SLen Brown 		       u32 field_datum_byte_offset,
585df7e6cbSBob Moore 		       u64 *value, u32 read_write);
5995b482a8SLen Brown 
6095b482a8SLen Brown static u8
615df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value);
6295b482a8SLen Brown 
6395b482a8SLen Brown static acpi_status
6495b482a8SLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
6595b482a8SLen Brown 		     u32 field_datum_byte_offset);
6695b482a8SLen Brown 
6795b482a8SLen Brown /*******************************************************************************
6895b482a8SLen Brown  *
6995b482a8SLen Brown  * FUNCTION:    acpi_ex_setup_region
7095b482a8SLen Brown  *
7195b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read or written
7295b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
7395b482a8SLen Brown  *                                        parent field
7495b482a8SLen Brown  *
7595b482a8SLen Brown  * RETURN:      Status
7695b482a8SLen Brown  *
7795b482a8SLen Brown  * DESCRIPTION: Common processing for acpi_ex_extract_from_field and
7895b482a8SLen Brown  *              acpi_ex_insert_into_field. Initialize the Region if necessary and
7995b482a8SLen Brown  *              validate the request.
8095b482a8SLen Brown  *
8195b482a8SLen Brown  ******************************************************************************/
8295b482a8SLen Brown 
8395b482a8SLen Brown static acpi_status
8495b482a8SLen Brown acpi_ex_setup_region(union acpi_operand_object *obj_desc,
8595b482a8SLen Brown 		     u32 field_datum_byte_offset)
8695b482a8SLen Brown {
8795b482a8SLen Brown 	acpi_status status = AE_OK;
8895b482a8SLen Brown 	union acpi_operand_object *rgn_desc;
89ec463666SBob Moore 	u8 space_id;
9095b482a8SLen Brown 
9195b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_setup_region, field_datum_byte_offset);
9295b482a8SLen Brown 
9395b482a8SLen Brown 	rgn_desc = obj_desc->common_field.region_obj;
9495b482a8SLen Brown 
9595b482a8SLen Brown 	/* We must have a valid region */
9695b482a8SLen Brown 
973371c19cSBob Moore 	if (rgn_desc->common.type != ACPI_TYPE_REGION) {
98f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Needed Region, found type 0x%X (%s)",
993371c19cSBob Moore 			    rgn_desc->common.type,
10095b482a8SLen Brown 			    acpi_ut_get_object_type_name(rgn_desc)));
10195b482a8SLen Brown 
10295b482a8SLen Brown 		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
10395b482a8SLen Brown 	}
10495b482a8SLen Brown 
105ec463666SBob Moore 	space_id = rgn_desc->region.space_id;
106ec463666SBob Moore 
107ec463666SBob Moore 	/* Validate the Space ID */
108ec463666SBob Moore 
109ec463666SBob Moore 	if (!acpi_is_valid_space_id(space_id)) {
110ec463666SBob Moore 		ACPI_ERROR((AE_INFO,
111ec463666SBob Moore 			    "Invalid/unknown Address Space ID: 0x%2.2X",
112ec463666SBob Moore 			    space_id));
113ec463666SBob Moore 		return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID);
114ec463666SBob Moore 	}
115ec463666SBob Moore 
11695b482a8SLen Brown 	/*
11795b482a8SLen Brown 	 * If the Region Address and Length have not been previously evaluated,
11895b482a8SLen Brown 	 * evaluate them now and save the results.
11995b482a8SLen Brown 	 */
12095b482a8SLen Brown 	if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) {
12195b482a8SLen Brown 		status = acpi_ds_get_region_arguments(rgn_desc);
12295b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
12395b482a8SLen Brown 			return_ACPI_STATUS(status);
12495b482a8SLen Brown 		}
12595b482a8SLen Brown 	}
12695b482a8SLen Brown 
1277cb7f45cSLen Brown 	/* Exit if Address/Length have been disallowed by the host OS */
1287cb7f45cSLen Brown 
1297cb7f45cSLen Brown 	if (rgn_desc->common.flags & AOPOBJ_INVALID) {
1307cb7f45cSLen Brown 		return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
1317cb7f45cSLen Brown 	}
1327cb7f45cSLen Brown 
13395b482a8SLen Brown 	/*
13409387b43SBob Moore 	 * Exit now for SMBus or IPMI address space, it has a non-linear
13509387b43SBob Moore 	 * address space and the request cannot be directly validated
13695b482a8SLen Brown 	 */
137ec463666SBob Moore 	if (space_id == ACPI_ADR_SPACE_SMBUS ||
138ec463666SBob Moore 	    space_id == ACPI_ADR_SPACE_IPMI) {
13995b482a8SLen Brown 
1406557a49aSLin Ming 		/* SMBus or IPMI has a non-linear address space */
14195b482a8SLen Brown 
14295b482a8SLen Brown 		return_ACPI_STATUS(AE_OK);
14395b482a8SLen Brown 	}
14495b482a8SLen Brown #ifdef ACPI_UNDER_DEVELOPMENT
14595b482a8SLen Brown 	/*
14695b482a8SLen Brown 	 * If the Field access is any_acc, we can now compute the optimal
14795b482a8SLen Brown 	 * access (because we know know the length of the parent region)
14895b482a8SLen Brown 	 */
14995b482a8SLen Brown 	if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
15095b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
15195b482a8SLen Brown 			return_ACPI_STATUS(status);
15295b482a8SLen Brown 		}
15395b482a8SLen Brown 	}
15495b482a8SLen Brown #endif
15595b482a8SLen Brown 
15695b482a8SLen Brown 	/*
15795b482a8SLen Brown 	 * Validate the request.  The entire request from the byte offset for a
15895b482a8SLen Brown 	 * length of one field datum (access width) must fit within the region.
15995b482a8SLen Brown 	 * (Region length is specified in bytes)
16095b482a8SLen Brown 	 */
16195b482a8SLen Brown 	if (rgn_desc->region.length <
16209387b43SBob Moore 	    (obj_desc->common_field.base_byte_offset + field_datum_byte_offset +
16395b482a8SLen Brown 	     obj_desc->common_field.access_byte_width)) {
16495b482a8SLen Brown 		if (acpi_gbl_enable_interpreter_slack) {
16595b482a8SLen Brown 			/*
16695b482a8SLen Brown 			 * Slack mode only:  We will go ahead and allow access to this
16795b482a8SLen Brown 			 * field if it is within the region length rounded up to the next
16895b482a8SLen Brown 			 * access width boundary. acpi_size cast for 64-bit compile.
16995b482a8SLen Brown 			 */
17095b482a8SLen Brown 			if (ACPI_ROUND_UP(rgn_desc->region.length,
17195b482a8SLen Brown 					  obj_desc->common_field.
17295b482a8SLen Brown 					  access_byte_width) >=
17395b482a8SLen Brown 			    ((acpi_size) obj_desc->common_field.
17495b482a8SLen Brown 			     base_byte_offset +
17595b482a8SLen Brown 			     obj_desc->common_field.access_byte_width +
17695b482a8SLen Brown 			     field_datum_byte_offset)) {
17795b482a8SLen Brown 				return_ACPI_STATUS(AE_OK);
17895b482a8SLen Brown 			}
17995b482a8SLen Brown 		}
18095b482a8SLen Brown 
18195b482a8SLen Brown 		if (rgn_desc->region.length <
18295b482a8SLen Brown 		    obj_desc->common_field.access_byte_width) {
18395b482a8SLen Brown 			/*
18495b482a8SLen Brown 			 * This is the case where the access_type (acc_word, etc.) is wider
18595b482a8SLen Brown 			 * than the region itself.  For example, a region of length one
18695b482a8SLen Brown 			 * byte, and a field with Dword access specified.
18795b482a8SLen Brown 			 */
18895b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
189f6a22b0bSBob Moore 				    "Field [%4.4s] access width (%u bytes) too large for region [%4.4s] (length %u)",
19095b482a8SLen Brown 				    acpi_ut_get_node_name(obj_desc->
19195b482a8SLen Brown 							  common_field.node),
19295b482a8SLen Brown 				    obj_desc->common_field.access_byte_width,
19395b482a8SLen Brown 				    acpi_ut_get_node_name(rgn_desc->region.
19495b482a8SLen Brown 							  node),
19595b482a8SLen Brown 				    rgn_desc->region.length));
19695b482a8SLen Brown 		}
19795b482a8SLen Brown 
19895b482a8SLen Brown 		/*
19995b482a8SLen Brown 		 * Offset rounded up to next multiple of field width
20095b482a8SLen Brown 		 * exceeds region length, indicate an error
20195b482a8SLen Brown 		 */
20295b482a8SLen Brown 		ACPI_ERROR((AE_INFO,
203f6a22b0bSBob Moore 			    "Field [%4.4s] Base+Offset+Width %u+%u+%u is beyond end of region [%4.4s] (length %u)",
20495b482a8SLen Brown 			    acpi_ut_get_node_name(obj_desc->common_field.node),
20595b482a8SLen Brown 			    obj_desc->common_field.base_byte_offset,
20695b482a8SLen Brown 			    field_datum_byte_offset,
20795b482a8SLen Brown 			    obj_desc->common_field.access_byte_width,
20895b482a8SLen Brown 			    acpi_ut_get_node_name(rgn_desc->region.node),
20995b482a8SLen Brown 			    rgn_desc->region.length));
21095b482a8SLen Brown 
21195b482a8SLen Brown 		return_ACPI_STATUS(AE_AML_REGION_LIMIT);
21295b482a8SLen Brown 	}
21395b482a8SLen Brown 
21495b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
21595b482a8SLen Brown }
21695b482a8SLen Brown 
21795b482a8SLen Brown /*******************************************************************************
21895b482a8SLen Brown  *
21995b482a8SLen Brown  * FUNCTION:    acpi_ex_access_region
22095b482a8SLen Brown  *
22195b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read
22295b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
22395b482a8SLen Brown  *                                        parent field
22495b482a8SLen Brown  *              Value                   - Where to store value (must at least
2255df7e6cbSBob Moore  *                                        64 bits)
22695b482a8SLen Brown  *              Function                - Read or Write flag plus other region-
22795b482a8SLen Brown  *                                        dependent flags
22895b482a8SLen Brown  *
22995b482a8SLen Brown  * RETURN:      Status
23095b482a8SLen Brown  *
23195b482a8SLen Brown  * DESCRIPTION: Read or Write a single field datum to an Operation Region.
23295b482a8SLen Brown  *
23395b482a8SLen Brown  ******************************************************************************/
23495b482a8SLen Brown 
23595b482a8SLen Brown acpi_status
23695b482a8SLen Brown acpi_ex_access_region(union acpi_operand_object *obj_desc,
2375df7e6cbSBob Moore 		      u32 field_datum_byte_offset, u64 *value, u32 function)
23895b482a8SLen Brown {
23995b482a8SLen Brown 	acpi_status status;
24095b482a8SLen Brown 	union acpi_operand_object *rgn_desc;
241f5407af3SBob Moore 	u32 region_offset;
24295b482a8SLen Brown 
24395b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_access_region);
24495b482a8SLen Brown 
24595b482a8SLen Brown 	/*
24695b482a8SLen Brown 	 * Ensure that the region operands are fully evaluated and verify
24795b482a8SLen Brown 	 * the validity of the request
24895b482a8SLen Brown 	 */
24995b482a8SLen Brown 	status = acpi_ex_setup_region(obj_desc, field_datum_byte_offset);
25095b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
25195b482a8SLen Brown 		return_ACPI_STATUS(status);
25295b482a8SLen Brown 	}
25395b482a8SLen Brown 
25495b482a8SLen Brown 	/*
25595b482a8SLen Brown 	 * The physical address of this field datum is:
25695b482a8SLen Brown 	 *
25795b482a8SLen Brown 	 * 1) The base of the region, plus
25895b482a8SLen Brown 	 * 2) The base offset of the field, plus
25995b482a8SLen Brown 	 * 3) The current offset into the field
26095b482a8SLen Brown 	 */
26195b482a8SLen Brown 	rgn_desc = obj_desc->common_field.region_obj;
262f5407af3SBob Moore 	region_offset =
26395b482a8SLen Brown 	    obj_desc->common_field.base_byte_offset + field_datum_byte_offset;
26495b482a8SLen Brown 
26595b482a8SLen Brown 	if ((function & ACPI_IO_MASK) == ACPI_READ) {
26695b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[READ]"));
26795b482a8SLen Brown 	} else {
26895b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, "[WRITE]"));
26995b482a8SLen Brown 	}
27095b482a8SLen Brown 
27195b482a8SLen Brown 	ACPI_DEBUG_PRINT_RAW((ACPI_DB_BFIELD,
27295b482a8SLen Brown 			      " Region [%s:%X], Width %X, ByteBase %X, Offset %X at %p\n",
27395b482a8SLen Brown 			      acpi_ut_get_region_name(rgn_desc->region.
27495b482a8SLen Brown 						      space_id),
27595b482a8SLen Brown 			      rgn_desc->region.space_id,
27695b482a8SLen Brown 			      obj_desc->common_field.access_byte_width,
27795b482a8SLen Brown 			      obj_desc->common_field.base_byte_offset,
27895b482a8SLen Brown 			      field_datum_byte_offset, ACPI_CAST_PTR(void,
279f5407af3SBob Moore 								     (rgn_desc->
280f5407af3SBob Moore 								      region.
281f5407af3SBob Moore 								      address +
282f5407af3SBob Moore 								      region_offset))));
28395b482a8SLen Brown 
28495b482a8SLen Brown 	/* Invoke the appropriate address_space/op_region handler */
28595b482a8SLen Brown 
286f5407af3SBob Moore 	status =
287f5407af3SBob Moore 	    acpi_ev_address_space_dispatch(rgn_desc, function, region_offset,
288f5407af3SBob Moore 					   ACPI_MUL_8(obj_desc->common_field.
28995b482a8SLen Brown 						      access_byte_width),
29095b482a8SLen Brown 					   value);
29195b482a8SLen Brown 
29295b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
29395b482a8SLen Brown 		if (status == AE_NOT_IMPLEMENTED) {
29495b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
2951b74dfb2SBob Moore 				    "Region %s (ID=%u) not implemented",
29695b482a8SLen Brown 				    acpi_ut_get_region_name(rgn_desc->region.
29795b482a8SLen Brown 							    space_id),
29895b482a8SLen Brown 				    rgn_desc->region.space_id));
29995b482a8SLen Brown 		} else if (status == AE_NOT_EXIST) {
30095b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
3011b74dfb2SBob Moore 				    "Region %s (ID=%u) has no handler",
30295b482a8SLen Brown 				    acpi_ut_get_region_name(rgn_desc->region.
30395b482a8SLen Brown 							    space_id),
30495b482a8SLen Brown 				    rgn_desc->region.space_id));
30595b482a8SLen Brown 		}
30695b482a8SLen Brown 	}
30795b482a8SLen Brown 
30895b482a8SLen Brown 	return_ACPI_STATUS(status);
30995b482a8SLen Brown }
31095b482a8SLen Brown 
31195b482a8SLen Brown /*******************************************************************************
31295b482a8SLen Brown  *
31395b482a8SLen Brown  * FUNCTION:    acpi_ex_register_overflow
31495b482a8SLen Brown  *
31595b482a8SLen Brown  * PARAMETERS:  obj_desc                - Register(Field) to be written
31695b482a8SLen Brown  *              Value                   - Value to be stored
31795b482a8SLen Brown  *
31895b482a8SLen Brown  * RETURN:      TRUE if value overflows the field, FALSE otherwise
31995b482a8SLen Brown  *
32095b482a8SLen Brown  * DESCRIPTION: Check if a value is out of range of the field being written.
32195b482a8SLen Brown  *              Used to check if the values written to Index and Bank registers
32295b482a8SLen Brown  *              are out of range.  Normally, the value is simply truncated
32395b482a8SLen Brown  *              to fit the field, but this case is most likely a serious
32495b482a8SLen Brown  *              coding error in the ASL.
32595b482a8SLen Brown  *
32695b482a8SLen Brown  ******************************************************************************/
32795b482a8SLen Brown 
32895b482a8SLen Brown static u8
3295df7e6cbSBob Moore acpi_ex_register_overflow(union acpi_operand_object *obj_desc, u64 value)
33095b482a8SLen Brown {
33146dfb09cSBob Moore 	ACPI_FUNCTION_NAME(ex_register_overflow);
33295b482a8SLen Brown 
33395b482a8SLen Brown 	if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) {
33495b482a8SLen Brown 		/*
33595b482a8SLen Brown 		 * The field is large enough to hold the maximum integer, so we can
33695b482a8SLen Brown 		 * never overflow it.
33795b482a8SLen Brown 		 */
33895b482a8SLen Brown 		return (FALSE);
33995b482a8SLen Brown 	}
34095b482a8SLen Brown 
3415df7e6cbSBob Moore 	if (value >= ((u64) 1 << obj_desc->common_field.bit_length)) {
34295b482a8SLen Brown 		/*
34395b482a8SLen Brown 		 * The Value is larger than the maximum value that can fit into
34495b482a8SLen Brown 		 * the register.
34595b482a8SLen Brown 		 */
34646dfb09cSBob Moore 		ACPI_ERROR((AE_INFO,
34746dfb09cSBob Moore 			    "Index value 0x%8.8X%8.8X overflows field width 0x%X",
34846dfb09cSBob Moore 			    ACPI_FORMAT_UINT64(value),
34946dfb09cSBob Moore 			    obj_desc->common_field.bit_length));
35046dfb09cSBob Moore 
35195b482a8SLen Brown 		return (TRUE);
35295b482a8SLen Brown 	}
35395b482a8SLen Brown 
35495b482a8SLen Brown 	/* The Value will fit into the field with no truncation */
35595b482a8SLen Brown 
35695b482a8SLen Brown 	return (FALSE);
35795b482a8SLen Brown }
35895b482a8SLen Brown 
35995b482a8SLen Brown /*******************************************************************************
36095b482a8SLen Brown  *
36195b482a8SLen Brown  * FUNCTION:    acpi_ex_field_datum_io
36295b482a8SLen Brown  *
36395b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be read
36495b482a8SLen Brown  *              field_datum_byte_offset - Byte offset of this datum within the
36595b482a8SLen Brown  *                                        parent field
36695b482a8SLen Brown  *              Value                   - Where to store value (must be 64 bits)
36795b482a8SLen Brown  *              read_write              - Read or Write flag
36895b482a8SLen Brown  *
36995b482a8SLen Brown  * RETURN:      Status
37095b482a8SLen Brown  *
37195b482a8SLen Brown  * DESCRIPTION: Read or Write a single datum of a field.  The field_type is
37295b482a8SLen Brown  *              demultiplexed here to handle the different types of fields
37395b482a8SLen Brown  *              (buffer_field, region_field, index_field, bank_field)
37495b482a8SLen Brown  *
37595b482a8SLen Brown  ******************************************************************************/
37695b482a8SLen Brown 
37795b482a8SLen Brown static acpi_status
37895b482a8SLen Brown acpi_ex_field_datum_io(union acpi_operand_object *obj_desc,
3795df7e6cbSBob Moore 		       u32 field_datum_byte_offset, u64 *value, u32 read_write)
38095b482a8SLen Brown {
38195b482a8SLen Brown 	acpi_status status;
3825df7e6cbSBob Moore 	u64 local_value;
38395b482a8SLen Brown 
38495b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_field_datum_io, field_datum_byte_offset);
38595b482a8SLen Brown 
38695b482a8SLen Brown 	if (read_write == ACPI_READ) {
38795b482a8SLen Brown 		if (!value) {
38895b482a8SLen Brown 			local_value = 0;
38995b482a8SLen Brown 
39095b482a8SLen Brown 			/* To support reads without saving return value */
39195b482a8SLen Brown 			value = &local_value;
39295b482a8SLen Brown 		}
39395b482a8SLen Brown 
39495b482a8SLen Brown 		/* Clear the entire return buffer first, [Very Important!] */
39595b482a8SLen Brown 
39695b482a8SLen Brown 		*value = 0;
39795b482a8SLen Brown 	}
39895b482a8SLen Brown 
39995b482a8SLen Brown 	/*
40095b482a8SLen Brown 	 * The four types of fields are:
40195b482a8SLen Brown 	 *
40295b482a8SLen Brown 	 * buffer_field - Read/write from/to a Buffer
40395b482a8SLen Brown 	 * region_field - Read/write from/to a Operation Region.
40495b482a8SLen Brown 	 * bank_field  - Write to a Bank Register, then read/write from/to an
40595b482a8SLen Brown 	 *               operation_region
40695b482a8SLen Brown 	 * index_field - Write to an Index Register, then read/write from/to a
40795b482a8SLen Brown 	 *               Data Register
40895b482a8SLen Brown 	 */
4093371c19cSBob Moore 	switch (obj_desc->common.type) {
41095b482a8SLen Brown 	case ACPI_TYPE_BUFFER_FIELD:
41195b482a8SLen Brown 		/*
41295b482a8SLen Brown 		 * If the buffer_field arguments have not been previously evaluated,
41395b482a8SLen Brown 		 * evaluate them now and save the results.
41495b482a8SLen Brown 		 */
41595b482a8SLen Brown 		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
41695b482a8SLen Brown 			status = acpi_ds_get_buffer_field_arguments(obj_desc);
41795b482a8SLen Brown 			if (ACPI_FAILURE(status)) {
41895b482a8SLen Brown 				return_ACPI_STATUS(status);
41995b482a8SLen Brown 			}
42095b482a8SLen Brown 		}
42195b482a8SLen Brown 
42295b482a8SLen Brown 		if (read_write == ACPI_READ) {
42395b482a8SLen Brown 			/*
42495b482a8SLen Brown 			 * Copy the data from the source buffer.
42595b482a8SLen Brown 			 * Length is the field width in bytes.
42695b482a8SLen Brown 			 */
42795b482a8SLen Brown 			ACPI_MEMCPY(value,
42895b482a8SLen Brown 				    (obj_desc->buffer_field.buffer_obj)->buffer.
42995b482a8SLen Brown 				    pointer +
43095b482a8SLen Brown 				    obj_desc->buffer_field.base_byte_offset +
43195b482a8SLen Brown 				    field_datum_byte_offset,
43295b482a8SLen Brown 				    obj_desc->common_field.access_byte_width);
43395b482a8SLen Brown 		} else {
43495b482a8SLen Brown 			/*
43595b482a8SLen Brown 			 * Copy the data to the target buffer.
43695b482a8SLen Brown 			 * Length is the field width in bytes.
43795b482a8SLen Brown 			 */
43895b482a8SLen Brown 			ACPI_MEMCPY((obj_desc->buffer_field.buffer_obj)->buffer.
43995b482a8SLen Brown 				    pointer +
44095b482a8SLen Brown 				    obj_desc->buffer_field.base_byte_offset +
44195b482a8SLen Brown 				    field_datum_byte_offset, value,
44295b482a8SLen Brown 				    obj_desc->common_field.access_byte_width);
44395b482a8SLen Brown 		}
44495b482a8SLen Brown 
44595b482a8SLen Brown 		status = AE_OK;
44695b482a8SLen Brown 		break;
44795b482a8SLen Brown 
44895b482a8SLen Brown 	case ACPI_TYPE_LOCAL_BANK_FIELD:
44995b482a8SLen Brown 
45095b482a8SLen Brown 		/*
45195b482a8SLen Brown 		 * Ensure that the bank_value is not beyond the capacity of
45295b482a8SLen Brown 		 * the register
45395b482a8SLen Brown 		 */
45495b482a8SLen Brown 		if (acpi_ex_register_overflow(obj_desc->bank_field.bank_obj,
4555df7e6cbSBob Moore 					      (u64) obj_desc->bank_field.
4565df7e6cbSBob Moore 					      value)) {
45795b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
45895b482a8SLen Brown 		}
45995b482a8SLen Brown 
46095b482a8SLen Brown 		/*
46195b482a8SLen Brown 		 * For bank_fields, we must write the bank_value to the bank_register
46295b482a8SLen Brown 		 * (itself a region_field) before we can access the data.
46395b482a8SLen Brown 		 */
46495b482a8SLen Brown 		status =
46595b482a8SLen Brown 		    acpi_ex_insert_into_field(obj_desc->bank_field.bank_obj,
46695b482a8SLen Brown 					      &obj_desc->bank_field.value,
46795b482a8SLen Brown 					      sizeof(obj_desc->bank_field.
46895b482a8SLen Brown 						     value));
46995b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
47095b482a8SLen Brown 			return_ACPI_STATUS(status);
47195b482a8SLen Brown 		}
47295b482a8SLen Brown 
47395b482a8SLen Brown 		/*
47495b482a8SLen Brown 		 * Now that the Bank has been selected, fall through to the
47595b482a8SLen Brown 		 * region_field case and write the datum to the Operation Region
47695b482a8SLen Brown 		 */
47795b482a8SLen Brown 
47895b482a8SLen Brown 		/*lint -fallthrough */
47995b482a8SLen Brown 
48095b482a8SLen Brown 	case ACPI_TYPE_LOCAL_REGION_FIELD:
48195b482a8SLen Brown 		/*
48295b482a8SLen Brown 		 * For simple region_fields, we just directly access the owning
48395b482a8SLen Brown 		 * Operation Region.
48495b482a8SLen Brown 		 */
48595b482a8SLen Brown 		status =
48695b482a8SLen Brown 		    acpi_ex_access_region(obj_desc, field_datum_byte_offset,
48795b482a8SLen Brown 					  value, read_write);
48895b482a8SLen Brown 		break;
48995b482a8SLen Brown 
49095b482a8SLen Brown 	case ACPI_TYPE_LOCAL_INDEX_FIELD:
49195b482a8SLen Brown 
49295b482a8SLen Brown 		/*
49395b482a8SLen Brown 		 * Ensure that the index_value is not beyond the capacity of
49495b482a8SLen Brown 		 * the register
49595b482a8SLen Brown 		 */
49695b482a8SLen Brown 		if (acpi_ex_register_overflow(obj_desc->index_field.index_obj,
4975df7e6cbSBob Moore 					      (u64) obj_desc->index_field.
4985df7e6cbSBob Moore 					      value)) {
49995b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_REGISTER_LIMIT);
50095b482a8SLen Brown 		}
50195b482a8SLen Brown 
50295b482a8SLen Brown 		/* Write the index value to the index_register (itself a region_field) */
50395b482a8SLen Brown 
50495b482a8SLen Brown 		field_datum_byte_offset += obj_desc->index_field.value;
50595b482a8SLen Brown 
50695b482a8SLen Brown 		ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
50795b482a8SLen Brown 				  "Write to Index Register: Value %8.8X\n",
50895b482a8SLen Brown 				  field_datum_byte_offset));
50995b482a8SLen Brown 
51095b482a8SLen Brown 		status =
51195b482a8SLen Brown 		    acpi_ex_insert_into_field(obj_desc->index_field.index_obj,
51295b482a8SLen Brown 					      &field_datum_byte_offset,
51395b482a8SLen Brown 					      sizeof(field_datum_byte_offset));
51495b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
51595b482a8SLen Brown 			return_ACPI_STATUS(status);
51695b482a8SLen Brown 		}
51795b482a8SLen Brown 
51895b482a8SLen Brown 		if (read_write == ACPI_READ) {
51995b482a8SLen Brown 
52095b482a8SLen Brown 			/* Read the datum from the data_register */
52195b482a8SLen Brown 
52295b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
52395b482a8SLen Brown 					  "Read from Data Register\n"));
52495b482a8SLen Brown 
52595b482a8SLen Brown 			status =
52695b482a8SLen Brown 			    acpi_ex_extract_from_field(obj_desc->index_field.
52795b482a8SLen Brown 						       data_obj, value,
5285df7e6cbSBob Moore 						       sizeof(u64));
52995b482a8SLen Brown 		} else {
53095b482a8SLen Brown 			/* Write the datum to the data_register */
53195b482a8SLen Brown 
53295b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
53395b482a8SLen Brown 					  "Write to Data Register: Value %8.8X%8.8X\n",
53495b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value)));
53595b482a8SLen Brown 
53695b482a8SLen Brown 			status =
53795b482a8SLen Brown 			    acpi_ex_insert_into_field(obj_desc->index_field.
53895b482a8SLen Brown 						      data_obj, value,
5395df7e6cbSBob Moore 						      sizeof(u64));
54095b482a8SLen Brown 		}
54195b482a8SLen Brown 		break;
54295b482a8SLen Brown 
54395b482a8SLen Brown 	default:
54495b482a8SLen Brown 
545f6a22b0bSBob Moore 		ACPI_ERROR((AE_INFO, "Wrong object type in field I/O %u",
5463371c19cSBob Moore 			    obj_desc->common.type));
54795b482a8SLen Brown 		status = AE_AML_INTERNAL;
54895b482a8SLen Brown 		break;
54995b482a8SLen Brown 	}
55095b482a8SLen Brown 
55195b482a8SLen Brown 	if (ACPI_SUCCESS(status)) {
55295b482a8SLen Brown 		if (read_write == ACPI_READ) {
55395b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
554b27d6597SBob Moore 					  "Value Read %8.8X%8.8X, Width %u\n",
55595b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value),
55695b482a8SLen Brown 					  obj_desc->common_field.
55795b482a8SLen Brown 					  access_byte_width));
55895b482a8SLen Brown 		} else {
55995b482a8SLen Brown 			ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
560b27d6597SBob Moore 					  "Value Written %8.8X%8.8X, Width %u\n",
56195b482a8SLen Brown 					  ACPI_FORMAT_UINT64(*value),
56295b482a8SLen Brown 					  obj_desc->common_field.
56395b482a8SLen Brown 					  access_byte_width));
56495b482a8SLen Brown 		}
56595b482a8SLen Brown 	}
56695b482a8SLen Brown 
56795b482a8SLen Brown 	return_ACPI_STATUS(status);
56895b482a8SLen Brown }
56995b482a8SLen Brown 
57095b482a8SLen Brown /*******************************************************************************
57195b482a8SLen Brown  *
57295b482a8SLen Brown  * FUNCTION:    acpi_ex_write_with_update_rule
57395b482a8SLen Brown  *
57495b482a8SLen Brown  * PARAMETERS:  obj_desc                - Field to be written
57595b482a8SLen Brown  *              Mask                    - bitmask within field datum
57695b482a8SLen Brown  *              field_value             - Value to write
57795b482a8SLen Brown  *              field_datum_byte_offset - Offset of datum within field
57895b482a8SLen Brown  *
57995b482a8SLen Brown  * RETURN:      Status
58095b482a8SLen Brown  *
58195b482a8SLen Brown  * DESCRIPTION: Apply the field update rule to a field write
58295b482a8SLen Brown  *
58395b482a8SLen Brown  ******************************************************************************/
58495b482a8SLen Brown 
58595b482a8SLen Brown acpi_status
58695b482a8SLen Brown acpi_ex_write_with_update_rule(union acpi_operand_object *obj_desc,
5875df7e6cbSBob Moore 			       u64 mask,
5885df7e6cbSBob Moore 			       u64 field_value, u32 field_datum_byte_offset)
58995b482a8SLen Brown {
59095b482a8SLen Brown 	acpi_status status = AE_OK;
5915df7e6cbSBob Moore 	u64 merged_value;
5925df7e6cbSBob Moore 	u64 current_value;
59395b482a8SLen Brown 
59495b482a8SLen Brown 	ACPI_FUNCTION_TRACE_U32(ex_write_with_update_rule, mask);
59595b482a8SLen Brown 
59695b482a8SLen Brown 	/* Start with the new bits  */
59795b482a8SLen Brown 
59895b482a8SLen Brown 	merged_value = field_value;
59995b482a8SLen Brown 
60095b482a8SLen Brown 	/* If the mask is all ones, we don't need to worry about the update rule */
60195b482a8SLen Brown 
6025df7e6cbSBob Moore 	if (mask != ACPI_UINT64_MAX) {
60395b482a8SLen Brown 
60495b482a8SLen Brown 		/* Decode the update rule */
60595b482a8SLen Brown 
60695b482a8SLen Brown 		switch (obj_desc->common_field.
60795b482a8SLen Brown 			field_flags & AML_FIELD_UPDATE_RULE_MASK) {
60895b482a8SLen Brown 		case AML_FIELD_UPDATE_PRESERVE:
60995b482a8SLen Brown 			/*
61095b482a8SLen Brown 			 * Check if update rule needs to be applied (not if mask is all
61195b482a8SLen Brown 			 * ones)  The left shift drops the bits we want to ignore.
61295b482a8SLen Brown 			 */
61395b482a8SLen Brown 			if ((~mask << (ACPI_MUL_8(sizeof(mask)) -
61495b482a8SLen Brown 				       ACPI_MUL_8(obj_desc->common_field.
61595b482a8SLen Brown 						  access_byte_width))) != 0) {
61695b482a8SLen Brown 				/*
61795b482a8SLen Brown 				 * Read the current contents of the byte/word/dword containing
61895b482a8SLen Brown 				 * the field, and merge with the new field value.
61995b482a8SLen Brown 				 */
62095b482a8SLen Brown 				status =
62195b482a8SLen Brown 				    acpi_ex_field_datum_io(obj_desc,
62295b482a8SLen Brown 							   field_datum_byte_offset,
62395b482a8SLen Brown 							   &current_value,
62495b482a8SLen Brown 							   ACPI_READ);
62595b482a8SLen Brown 				if (ACPI_FAILURE(status)) {
62695b482a8SLen Brown 					return_ACPI_STATUS(status);
62795b482a8SLen Brown 				}
62895b482a8SLen Brown 
62995b482a8SLen Brown 				merged_value |= (current_value & ~mask);
63095b482a8SLen Brown 			}
63195b482a8SLen Brown 			break;
63295b482a8SLen Brown 
63395b482a8SLen Brown 		case AML_FIELD_UPDATE_WRITE_AS_ONES:
63495b482a8SLen Brown 
63595b482a8SLen Brown 			/* Set positions outside the field to all ones */
63695b482a8SLen Brown 
63795b482a8SLen Brown 			merged_value |= ~mask;
63895b482a8SLen Brown 			break;
63995b482a8SLen Brown 
64095b482a8SLen Brown 		case AML_FIELD_UPDATE_WRITE_AS_ZEROS:
64195b482a8SLen Brown 
64295b482a8SLen Brown 			/* Set positions outside the field to all zeros */
64395b482a8SLen Brown 
64495b482a8SLen Brown 			merged_value &= mask;
64595b482a8SLen Brown 			break;
64695b482a8SLen Brown 
64795b482a8SLen Brown 		default:
64895b482a8SLen Brown 
64995b482a8SLen Brown 			ACPI_ERROR((AE_INFO,
650f6a22b0bSBob Moore 				    "Unknown UpdateRule value: 0x%X",
65195b482a8SLen Brown 				    (obj_desc->common_field.
65295b482a8SLen Brown 				     field_flags &
65395b482a8SLen Brown 				     AML_FIELD_UPDATE_RULE_MASK)));
65495b482a8SLen Brown 			return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
65595b482a8SLen Brown 		}
65695b482a8SLen Brown 	}
65795b482a8SLen Brown 
65895b482a8SLen Brown 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
65995b482a8SLen Brown 			  "Mask %8.8X%8.8X, DatumOffset %X, Width %X, Value %8.8X%8.8X, MergedValue %8.8X%8.8X\n",
66095b482a8SLen Brown 			  ACPI_FORMAT_UINT64(mask),
66195b482a8SLen Brown 			  field_datum_byte_offset,
66295b482a8SLen Brown 			  obj_desc->common_field.access_byte_width,
66395b482a8SLen Brown 			  ACPI_FORMAT_UINT64(field_value),
66495b482a8SLen Brown 			  ACPI_FORMAT_UINT64(merged_value)));
66595b482a8SLen Brown 
66695b482a8SLen Brown 	/* Write the merged value */
66795b482a8SLen Brown 
66895b482a8SLen Brown 	status = acpi_ex_field_datum_io(obj_desc, field_datum_byte_offset,
66995b482a8SLen Brown 					&merged_value, ACPI_WRITE);
67095b482a8SLen Brown 
67195b482a8SLen Brown 	return_ACPI_STATUS(status);
67295b482a8SLen Brown }
67395b482a8SLen Brown 
67495b482a8SLen Brown /*******************************************************************************
67595b482a8SLen Brown  *
67695b482a8SLen Brown  * FUNCTION:    acpi_ex_extract_from_field
67795b482a8SLen Brown  *
67895b482a8SLen Brown  * PARAMETERS:  obj_desc            - Field to be read
67995b482a8SLen Brown  *              Buffer              - Where to store the field data
68095b482a8SLen Brown  *              buffer_length       - Length of Buffer
68195b482a8SLen Brown  *
68295b482a8SLen Brown  * RETURN:      Status
68395b482a8SLen Brown  *
68495b482a8SLen Brown  * DESCRIPTION: Retrieve the current value of the given field
68595b482a8SLen Brown  *
68695b482a8SLen Brown  ******************************************************************************/
68795b482a8SLen Brown 
68895b482a8SLen Brown acpi_status
68995b482a8SLen Brown acpi_ex_extract_from_field(union acpi_operand_object *obj_desc,
69095b482a8SLen Brown 			   void *buffer, u32 buffer_length)
69195b482a8SLen Brown {
69295b482a8SLen Brown 	acpi_status status;
6935df7e6cbSBob Moore 	u64 raw_datum;
6945df7e6cbSBob Moore 	u64 merged_datum;
69595b482a8SLen Brown 	u32 field_offset = 0;
69695b482a8SLen Brown 	u32 buffer_offset = 0;
69795b482a8SLen Brown 	u32 buffer_tail_bits;
69895b482a8SLen Brown 	u32 datum_count;
69995b482a8SLen Brown 	u32 field_datum_count;
70009387b43SBob Moore 	u32 access_bit_width;
70195b482a8SLen Brown 	u32 i;
70295b482a8SLen Brown 
70395b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_extract_from_field);
70495b482a8SLen Brown 
70595b482a8SLen Brown 	/* Validate target buffer and clear it */
70695b482a8SLen Brown 
70795b482a8SLen Brown 	if (buffer_length <
70895b482a8SLen Brown 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length)) {
70995b482a8SLen Brown 		ACPI_ERROR((AE_INFO,
710f6a22b0bSBob Moore 			    "Field size %u (bits) is too large for buffer (%u)",
71195b482a8SLen Brown 			    obj_desc->common_field.bit_length, buffer_length));
71295b482a8SLen Brown 
71395b482a8SLen Brown 		return_ACPI_STATUS(AE_BUFFER_OVERFLOW);
71495b482a8SLen Brown 	}
71509387b43SBob Moore 
71695b482a8SLen Brown 	ACPI_MEMSET(buffer, 0, buffer_length);
71709387b43SBob Moore 	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
71809387b43SBob Moore 
71909387b43SBob Moore 	/* Handle the simple case here */
72009387b43SBob Moore 
72109387b43SBob Moore 	if ((obj_desc->common_field.start_field_bit_offset == 0) &&
72209387b43SBob Moore 	    (obj_desc->common_field.bit_length == access_bit_width)) {
72309387b43SBob Moore 		status = acpi_ex_field_datum_io(obj_desc, 0, buffer, ACPI_READ);
72409387b43SBob Moore 		return_ACPI_STATUS(status);
72509387b43SBob Moore 	}
72609387b43SBob Moore 
72709387b43SBob Moore /* TBD: Move to common setup code */
72809387b43SBob Moore 
72909387b43SBob Moore 	/* Field algorithm is limited to sizeof(u64), truncate if needed */
73009387b43SBob Moore 
73109387b43SBob Moore 	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
73209387b43SBob Moore 		obj_desc->common_field.access_byte_width = sizeof(u64);
73309387b43SBob Moore 		access_bit_width = sizeof(u64) * 8;
73409387b43SBob Moore 	}
73595b482a8SLen Brown 
73695b482a8SLen Brown 	/* Compute the number of datums (access width data items) */
73795b482a8SLen Brown 
73809387b43SBob Moore 	datum_count =
73909387b43SBob Moore 	    ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
74009387b43SBob Moore 			     access_bit_width);
74109387b43SBob Moore 
74295b482a8SLen Brown 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
74395b482a8SLen Brown 					     obj_desc->common_field.
74495b482a8SLen Brown 					     start_field_bit_offset,
74595b482a8SLen Brown 					     access_bit_width);
74695b482a8SLen Brown 
74795b482a8SLen Brown 	/* Priming read from the field */
74895b482a8SLen Brown 
74995b482a8SLen Brown 	status =
75095b482a8SLen Brown 	    acpi_ex_field_datum_io(obj_desc, field_offset, &raw_datum,
75195b482a8SLen Brown 				   ACPI_READ);
75295b482a8SLen Brown 	if (ACPI_FAILURE(status)) {
75395b482a8SLen Brown 		return_ACPI_STATUS(status);
75495b482a8SLen Brown 	}
75595b482a8SLen Brown 	merged_datum =
75695b482a8SLen Brown 	    raw_datum >> obj_desc->common_field.start_field_bit_offset;
75795b482a8SLen Brown 
75895b482a8SLen Brown 	/* Read the rest of the field */
75995b482a8SLen Brown 
76095b482a8SLen Brown 	for (i = 1; i < field_datum_count; i++) {
76195b482a8SLen Brown 
76295b482a8SLen Brown 		/* Get next input datum from the field */
76395b482a8SLen Brown 
76495b482a8SLen Brown 		field_offset += obj_desc->common_field.access_byte_width;
76595b482a8SLen Brown 		status = acpi_ex_field_datum_io(obj_desc, field_offset,
76695b482a8SLen Brown 						&raw_datum, ACPI_READ);
76795b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
76895b482a8SLen Brown 			return_ACPI_STATUS(status);
76995b482a8SLen Brown 		}
77095b482a8SLen Brown 
77195b482a8SLen Brown 		/*
77295b482a8SLen Brown 		 * Merge with previous datum if necessary.
77395b482a8SLen Brown 		 *
77495b482a8SLen Brown 		 * Note: Before the shift, check if the shift value will be larger than
77595b482a8SLen Brown 		 * the integer size. If so, there is no need to perform the operation.
77695b482a8SLen Brown 		 * This avoids the differences in behavior between different compilers
77795b482a8SLen Brown 		 * concerning shift values larger than the target data width.
77895b482a8SLen Brown 		 */
77909387b43SBob Moore 		if (access_bit_width -
78009387b43SBob Moore 		    obj_desc->common_field.start_field_bit_offset <
78195b482a8SLen Brown 		    ACPI_INTEGER_BIT_SIZE) {
78295b482a8SLen Brown 			merged_datum |=
78309387b43SBob Moore 			    raw_datum << (access_bit_width -
78495b482a8SLen Brown 					  obj_desc->common_field.
78595b482a8SLen Brown 					  start_field_bit_offset);
78695b482a8SLen Brown 		}
78795b482a8SLen Brown 
78895b482a8SLen Brown 		if (i == datum_count) {
78995b482a8SLen Brown 			break;
79095b482a8SLen Brown 		}
79195b482a8SLen Brown 
79295b482a8SLen Brown 		/* Write merged datum to target buffer */
79395b482a8SLen Brown 
79495b482a8SLen Brown 		ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum,
79595b482a8SLen Brown 			    ACPI_MIN(obj_desc->common_field.access_byte_width,
79695b482a8SLen Brown 				     buffer_length - buffer_offset));
79795b482a8SLen Brown 
79895b482a8SLen Brown 		buffer_offset += obj_desc->common_field.access_byte_width;
79995b482a8SLen Brown 		merged_datum =
80095b482a8SLen Brown 		    raw_datum >> obj_desc->common_field.start_field_bit_offset;
80195b482a8SLen Brown 	}
80295b482a8SLen Brown 
80395b482a8SLen Brown 	/* Mask off any extra bits in the last datum */
80495b482a8SLen Brown 
80509387b43SBob Moore 	buffer_tail_bits = obj_desc->common_field.bit_length % access_bit_width;
80695b482a8SLen Brown 	if (buffer_tail_bits) {
80795b482a8SLen Brown 		merged_datum &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
80895b482a8SLen Brown 	}
80995b482a8SLen Brown 
81095b482a8SLen Brown 	/* Write the last datum to the buffer */
81195b482a8SLen Brown 
81295b482a8SLen Brown 	ACPI_MEMCPY(((char *)buffer) + buffer_offset, &merged_datum,
81395b482a8SLen Brown 		    ACPI_MIN(obj_desc->common_field.access_byte_width,
81495b482a8SLen Brown 			     buffer_length - buffer_offset));
81595b482a8SLen Brown 
81695b482a8SLen Brown 	return_ACPI_STATUS(AE_OK);
81795b482a8SLen Brown }
81895b482a8SLen Brown 
81995b482a8SLen Brown /*******************************************************************************
82095b482a8SLen Brown  *
82195b482a8SLen Brown  * FUNCTION:    acpi_ex_insert_into_field
82295b482a8SLen Brown  *
82395b482a8SLen Brown  * PARAMETERS:  obj_desc            - Field to be written
82495b482a8SLen Brown  *              Buffer              - Data to be written
82595b482a8SLen Brown  *              buffer_length       - Length of Buffer
82695b482a8SLen Brown  *
82795b482a8SLen Brown  * RETURN:      Status
82895b482a8SLen Brown  *
82995b482a8SLen Brown  * DESCRIPTION: Store the Buffer contents into the given field
83095b482a8SLen Brown  *
83195b482a8SLen Brown  ******************************************************************************/
83295b482a8SLen Brown 
83395b482a8SLen Brown acpi_status
83495b482a8SLen Brown acpi_ex_insert_into_field(union acpi_operand_object *obj_desc,
83595b482a8SLen Brown 			  void *buffer, u32 buffer_length)
83695b482a8SLen Brown {
83709387b43SBob Moore 	void *new_buffer;
83895b482a8SLen Brown 	acpi_status status;
8395df7e6cbSBob Moore 	u64 mask;
8405df7e6cbSBob Moore 	u64 width_mask;
8415df7e6cbSBob Moore 	u64 merged_datum;
8425df7e6cbSBob Moore 	u64 raw_datum = 0;
84395b482a8SLen Brown 	u32 field_offset = 0;
84495b482a8SLen Brown 	u32 buffer_offset = 0;
84595b482a8SLen Brown 	u32 buffer_tail_bits;
84695b482a8SLen Brown 	u32 datum_count;
84795b482a8SLen Brown 	u32 field_datum_count;
84809387b43SBob Moore 	u32 access_bit_width;
84995b482a8SLen Brown 	u32 required_length;
85009387b43SBob Moore 	u32 i;
85195b482a8SLen Brown 
85295b482a8SLen Brown 	ACPI_FUNCTION_TRACE(ex_insert_into_field);
85395b482a8SLen Brown 
85495b482a8SLen Brown 	/* Validate input buffer */
85595b482a8SLen Brown 
85695b482a8SLen Brown 	new_buffer = NULL;
85795b482a8SLen Brown 	required_length =
85895b482a8SLen Brown 	    ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->common_field.bit_length);
85995b482a8SLen Brown 	/*
86095b482a8SLen Brown 	 * We must have a buffer that is at least as long as the field
86195b482a8SLen Brown 	 * we are writing to.  This is because individual fields are
86295b482a8SLen Brown 	 * indivisible and partial writes are not supported -- as per
86395b482a8SLen Brown 	 * the ACPI specification.
86495b482a8SLen Brown 	 */
86595b482a8SLen Brown 	if (buffer_length < required_length) {
86695b482a8SLen Brown 
86795b482a8SLen Brown 		/* We need to create a new buffer */
86895b482a8SLen Brown 
86995b482a8SLen Brown 		new_buffer = ACPI_ALLOCATE_ZEROED(required_length);
87095b482a8SLen Brown 		if (!new_buffer) {
87195b482a8SLen Brown 			return_ACPI_STATUS(AE_NO_MEMORY);
87295b482a8SLen Brown 		}
87395b482a8SLen Brown 
87495b482a8SLen Brown 		/*
87595b482a8SLen Brown 		 * Copy the original data to the new buffer, starting
87695b482a8SLen Brown 		 * at Byte zero.  All unused (upper) bytes of the
87795b482a8SLen Brown 		 * buffer will be 0.
87895b482a8SLen Brown 		 */
87995b482a8SLen Brown 		ACPI_MEMCPY((char *)new_buffer, (char *)buffer, buffer_length);
88095b482a8SLen Brown 		buffer = new_buffer;
88195b482a8SLen Brown 		buffer_length = required_length;
88295b482a8SLen Brown 	}
88395b482a8SLen Brown 
88409387b43SBob Moore /* TBD: Move to common setup code */
88509387b43SBob Moore 
88609387b43SBob Moore 	/* Algo is limited to sizeof(u64), so cut the access_byte_width */
88709387b43SBob Moore 	if (obj_desc->common_field.access_byte_width > sizeof(u64)) {
88809387b43SBob Moore 		obj_desc->common_field.access_byte_width = sizeof(u64);
88909387b43SBob Moore 	}
89009387b43SBob Moore 
89109387b43SBob Moore 	access_bit_width = ACPI_MUL_8(obj_desc->common_field.access_byte_width);
89209387b43SBob Moore 
89395b482a8SLen Brown 	/*
89495b482a8SLen Brown 	 * Create the bitmasks used for bit insertion.
89595b482a8SLen Brown 	 * Note: This if/else is used to bypass compiler differences with the
89695b482a8SLen Brown 	 * shift operator
89795b482a8SLen Brown 	 */
89809387b43SBob Moore 	if (access_bit_width == ACPI_INTEGER_BIT_SIZE) {
8995df7e6cbSBob Moore 		width_mask = ACPI_UINT64_MAX;
90095b482a8SLen Brown 	} else {
90109387b43SBob Moore 		width_mask = ACPI_MASK_BITS_ABOVE(access_bit_width);
90295b482a8SLen Brown 	}
90395b482a8SLen Brown 
90495b482a8SLen Brown 	mask = width_mask &
90595b482a8SLen Brown 	    ACPI_MASK_BITS_BELOW(obj_desc->common_field.start_field_bit_offset);
90695b482a8SLen Brown 
90795b482a8SLen Brown 	/* Compute the number of datums (access width data items) */
90895b482a8SLen Brown 
90995b482a8SLen Brown 	datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length,
91009387b43SBob Moore 				       access_bit_width);
91195b482a8SLen Brown 
91295b482a8SLen Brown 	field_datum_count = ACPI_ROUND_UP_TO(obj_desc->common_field.bit_length +
91395b482a8SLen Brown 					     obj_desc->common_field.
91495b482a8SLen Brown 					     start_field_bit_offset,
91595b482a8SLen Brown 					     access_bit_width);
91695b482a8SLen Brown 
91795b482a8SLen Brown 	/* Get initial Datum from the input buffer */
91895b482a8SLen Brown 
91995b482a8SLen Brown 	ACPI_MEMCPY(&raw_datum, buffer,
92095b482a8SLen Brown 		    ACPI_MIN(obj_desc->common_field.access_byte_width,
92195b482a8SLen Brown 			     buffer_length - buffer_offset));
92295b482a8SLen Brown 
92395b482a8SLen Brown 	merged_datum =
92495b482a8SLen Brown 	    raw_datum << obj_desc->common_field.start_field_bit_offset;
92595b482a8SLen Brown 
92695b482a8SLen Brown 	/* Write the entire field */
92795b482a8SLen Brown 
92895b482a8SLen Brown 	for (i = 1; i < field_datum_count; i++) {
92995b482a8SLen Brown 
93095b482a8SLen Brown 		/* Write merged datum to the target field */
93195b482a8SLen Brown 
93295b482a8SLen Brown 		merged_datum &= mask;
93395b482a8SLen Brown 		status = acpi_ex_write_with_update_rule(obj_desc, mask,
93495b482a8SLen Brown 							merged_datum,
93595b482a8SLen Brown 							field_offset);
93695b482a8SLen Brown 		if (ACPI_FAILURE(status)) {
93795b482a8SLen Brown 			goto exit;
93895b482a8SLen Brown 		}
93995b482a8SLen Brown 
94095b482a8SLen Brown 		field_offset += obj_desc->common_field.access_byte_width;
94195b482a8SLen Brown 
94295b482a8SLen Brown 		/*
94395b482a8SLen Brown 		 * Start new output datum by merging with previous input datum
94495b482a8SLen Brown 		 * if necessary.
94595b482a8SLen Brown 		 *
94695b482a8SLen Brown 		 * Note: Before the shift, check if the shift value will be larger than
94795b482a8SLen Brown 		 * the integer size. If so, there is no need to perform the operation.
94895b482a8SLen Brown 		 * This avoids the differences in behavior between different compilers
94995b482a8SLen Brown 		 * concerning shift values larger than the target data width.
95095b482a8SLen Brown 		 */
95109387b43SBob Moore 		if ((access_bit_width -
95295b482a8SLen Brown 		     obj_desc->common_field.start_field_bit_offset) <
95395b482a8SLen Brown 		    ACPI_INTEGER_BIT_SIZE) {
95495b482a8SLen Brown 			merged_datum =
95509387b43SBob Moore 			    raw_datum >> (access_bit_width -
95695b482a8SLen Brown 					  obj_desc->common_field.
95795b482a8SLen Brown 					  start_field_bit_offset);
95895b482a8SLen Brown 		} else {
95995b482a8SLen Brown 			merged_datum = 0;
96095b482a8SLen Brown 		}
96195b482a8SLen Brown 
96295b482a8SLen Brown 		mask = width_mask;
96395b482a8SLen Brown 
96495b482a8SLen Brown 		if (i == datum_count) {
96595b482a8SLen Brown 			break;
96695b482a8SLen Brown 		}
96795b482a8SLen Brown 
96895b482a8SLen Brown 		/* Get the next input datum from the buffer */
96995b482a8SLen Brown 
97095b482a8SLen Brown 		buffer_offset += obj_desc->common_field.access_byte_width;
97195b482a8SLen Brown 		ACPI_MEMCPY(&raw_datum, ((char *)buffer) + buffer_offset,
97295b482a8SLen Brown 			    ACPI_MIN(obj_desc->common_field.access_byte_width,
97395b482a8SLen Brown 				     buffer_length - buffer_offset));
97409387b43SBob Moore 
97595b482a8SLen Brown 		merged_datum |=
97695b482a8SLen Brown 		    raw_datum << obj_desc->common_field.start_field_bit_offset;
97795b482a8SLen Brown 	}
97895b482a8SLen Brown 
97995b482a8SLen Brown 	/* Mask off any extra bits in the last datum */
98095b482a8SLen Brown 
98195b482a8SLen Brown 	buffer_tail_bits = (obj_desc->common_field.bit_length +
98295b482a8SLen Brown 			    obj_desc->common_field.start_field_bit_offset) %
98309387b43SBob Moore 	    access_bit_width;
98495b482a8SLen Brown 	if (buffer_tail_bits) {
98595b482a8SLen Brown 		mask &= ACPI_MASK_BITS_ABOVE(buffer_tail_bits);
98695b482a8SLen Brown 	}
98795b482a8SLen Brown 
98895b482a8SLen Brown 	/* Write the last datum to the field */
98995b482a8SLen Brown 
99095b482a8SLen Brown 	merged_datum &= mask;
99195b482a8SLen Brown 	status = acpi_ex_write_with_update_rule(obj_desc,
99295b482a8SLen Brown 						mask, merged_datum,
99395b482a8SLen Brown 						field_offset);
99495b482a8SLen Brown 
99595b482a8SLen Brown       exit:
99695b482a8SLen Brown 	/* Free temporary buffer if we used one */
99795b482a8SLen Brown 
99895b482a8SLen Brown 	if (new_buffer) {
99995b482a8SLen Brown 		ACPI_FREE(new_buffer);
100095b482a8SLen Brown 	}
100195b482a8SLen Brown 	return_ACPI_STATUS(status);
100295b482a8SLen Brown }
1003