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 ¤t_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