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