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