162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Module Name: exserial - field_unit support for serial address spaces 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2000 - 2023, Intel Corp. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci *****************************************************************************/ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <acpi/acpi.h> 1162306a36Sopenharmony_ci#include "accommon.h" 1262306a36Sopenharmony_ci#include "acdispat.h" 1362306a36Sopenharmony_ci#include "acinterp.h" 1462306a36Sopenharmony_ci#include "amlcode.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define _COMPONENT ACPI_EXECUTER 1762306a36Sopenharmony_ciACPI_MODULE_NAME("exserial") 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/******************************************************************************* 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * FUNCTION: acpi_ex_read_gpio 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * PARAMETERS: obj_desc - The named field to read 2462306a36Sopenharmony_ci * buffer - Where the return data is returned 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * RETURN: Status 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * DESCRIPTION: Read from a named field that references a Generic Serial Bus 2962306a36Sopenharmony_ci * field 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci ******************************************************************************/ 3262306a36Sopenharmony_ciacpi_status acpi_ex_read_gpio(union acpi_operand_object *obj_desc, void *buffer) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci acpi_status status; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci ACPI_FUNCTION_TRACE_PTR(ex_read_gpio, obj_desc); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * For GPIO (general_purpose_io), the Address will be the bit offset 4062306a36Sopenharmony_ci * from the previous Connection() operator, making it effectively a 4162306a36Sopenharmony_ci * pin number index. The bit_length is the length of the field, which 4262306a36Sopenharmony_ci * is thus the number of pins. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 4562306a36Sopenharmony_ci "GPIO FieldRead [FROM]: Pin %u Bits %u\n", 4662306a36Sopenharmony_ci obj_desc->field.pin_number_index, 4762306a36Sopenharmony_ci obj_desc->field.bit_length)); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* Lock entire transaction if requested */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* Perform the read */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, ACPI_READ); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 5862306a36Sopenharmony_ci return_ACPI_STATUS(status); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/******************************************************************************* 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * FUNCTION: acpi_ex_write_gpio 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * PARAMETERS: source_desc - Contains data to write. Expect to be 6662306a36Sopenharmony_ci * an Integer object. 6762306a36Sopenharmony_ci * obj_desc - The named field 6862306a36Sopenharmony_ci * result_desc - Where the return value is returned, if any 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * RETURN: Status 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * DESCRIPTION: Write to a named field that references a General Purpose I/O 7362306a36Sopenharmony_ci * field. 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci ******************************************************************************/ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciacpi_status 7862306a36Sopenharmony_ciacpi_ex_write_gpio(union acpi_operand_object *source_desc, 7962306a36Sopenharmony_ci union acpi_operand_object *obj_desc, 8062306a36Sopenharmony_ci union acpi_operand_object **return_buffer) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci acpi_status status; 8362306a36Sopenharmony_ci void *buffer; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ACPI_FUNCTION_TRACE_PTR(ex_write_gpio, obj_desc); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * For GPIO (general_purpose_io), we will bypass the entire field 8962306a36Sopenharmony_ci * mechanism and handoff the bit address and bit width directly to 9062306a36Sopenharmony_ci * the handler. The Address will be the bit offset 9162306a36Sopenharmony_ci * from the previous Connection() operator, making it effectively a 9262306a36Sopenharmony_ci * pin number index. The bit_length is the length of the field, which 9362306a36Sopenharmony_ci * is thus the number of pins. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (source_desc->common.type != ACPI_TYPE_INTEGER) { 9662306a36Sopenharmony_ci return_ACPI_STATUS(AE_AML_OPERAND_TYPE); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, 10062306a36Sopenharmony_ci "GPIO FieldWrite [FROM]: (%s:%X), Value %.8X [TO]: Pin %u Bits %u\n", 10162306a36Sopenharmony_ci acpi_ut_get_type_name(source_desc->common.type), 10262306a36Sopenharmony_ci source_desc->common.type, 10362306a36Sopenharmony_ci (u32)source_desc->integer.value, 10462306a36Sopenharmony_ci obj_desc->field.pin_number_index, 10562306a36Sopenharmony_ci obj_desc->field.bit_length)); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci buffer = &source_desc->integer.value; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Lock entire transaction if requested */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Perform the write */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, ACPI_WRITE); 11662306a36Sopenharmony_ci acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 11762306a36Sopenharmony_ci return_ACPI_STATUS(status); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/******************************************************************************* 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * FUNCTION: acpi_ex_read_serial_bus 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * PARAMETERS: obj_desc - The named field to read 12562306a36Sopenharmony_ci * return_buffer - Where the return value is returned, if any 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * RETURN: Status 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * DESCRIPTION: Read from a named field that references a serial bus 13062306a36Sopenharmony_ci * (SMBus, IPMI, or GSBus). 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci ******************************************************************************/ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciacpi_status 13562306a36Sopenharmony_ciacpi_ex_read_serial_bus(union acpi_operand_object *obj_desc, 13662306a36Sopenharmony_ci union acpi_operand_object **return_buffer) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci acpi_status status; 13962306a36Sopenharmony_ci u32 buffer_length; 14062306a36Sopenharmony_ci union acpi_operand_object *buffer_desc; 14162306a36Sopenharmony_ci u32 function; 14262306a36Sopenharmony_ci u16 accessor_type; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ACPI_FUNCTION_TRACE_PTR(ex_read_serial_bus, obj_desc); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * This is an SMBus, GSBus or IPMI read. We must create a buffer to 14862306a36Sopenharmony_ci * hold the data and then directly access the region handler. 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Note: SMBus and GSBus protocol value is passed in upper 16-bits 15162306a36Sopenharmony_ci * of Function 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Common buffer format: 15462306a36Sopenharmony_ci * Status; (Byte 0 of the data buffer) 15562306a36Sopenharmony_ci * Length; (Byte 1 of the data buffer) 15662306a36Sopenharmony_ci * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci switch (obj_desc->field.region_obj->region.space_id) { 15962306a36Sopenharmony_ci case ACPI_ADR_SPACE_SMBUS: 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci buffer_length = ACPI_SMBUS_BUFFER_SIZE; 16262306a36Sopenharmony_ci function = ACPI_READ | (obj_desc->field.attribute << 16); 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci case ACPI_ADR_SPACE_IPMI: 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci buffer_length = ACPI_IPMI_BUFFER_SIZE; 16862306a36Sopenharmony_ci function = ACPI_READ; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci case ACPI_ADR_SPACE_GSBUS: 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci accessor_type = obj_desc->field.attribute; 17462306a36Sopenharmony_ci if (accessor_type == AML_FIELD_ATTRIB_RAW_PROCESS_BYTES) { 17562306a36Sopenharmony_ci ACPI_ERROR((AE_INFO, 17662306a36Sopenharmony_ci "Invalid direct read using bidirectional write-then-read protocol")); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return_ACPI_STATUS(AE_AML_PROTOCOL); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci status = 18262306a36Sopenharmony_ci acpi_ex_get_protocol_buffer_length(accessor_type, 18362306a36Sopenharmony_ci &buffer_length); 18462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 18562306a36Sopenharmony_ci ACPI_ERROR((AE_INFO, 18662306a36Sopenharmony_ci "Invalid protocol ID for GSBus: 0x%4.4X", 18762306a36Sopenharmony_ci accessor_type)); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return_ACPI_STATUS(status); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Add header length to get the full size of the buffer */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci buffer_length += ACPI_SERIAL_HEADER_SIZE; 19562306a36Sopenharmony_ci function = ACPI_READ | (accessor_type << 16); 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci case ACPI_ADR_SPACE_PLATFORM_RT: 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE; 20162306a36Sopenharmony_ci function = ACPI_READ; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci default: 20562306a36Sopenharmony_ci return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Create the local transfer buffer that is returned to the caller */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci buffer_desc = acpi_ut_create_buffer_object(buffer_length); 21162306a36Sopenharmony_ci if (!buffer_desc) { 21262306a36Sopenharmony_ci return_ACPI_STATUS(AE_NO_MEMORY); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Lock entire transaction if requested */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Call the region handler for the write-then-read */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci status = acpi_ex_access_region(obj_desc, 0, 22262306a36Sopenharmony_ci ACPI_CAST_PTR(u64, 22362306a36Sopenharmony_ci buffer_desc->buffer. 22462306a36Sopenharmony_ci pointer), function); 22562306a36Sopenharmony_ci acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci *return_buffer = buffer_desc; 22862306a36Sopenharmony_ci return_ACPI_STATUS(status); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/******************************************************************************* 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * FUNCTION: acpi_ex_write_serial_bus 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * PARAMETERS: source_desc - Contains data to write 23662306a36Sopenharmony_ci * obj_desc - The named field 23762306a36Sopenharmony_ci * return_buffer - Where the return value is returned, if any 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * RETURN: Status 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * DESCRIPTION: Write to a named field that references a serial bus 24262306a36Sopenharmony_ci * (SMBus, IPMI, GSBus). 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci ******************************************************************************/ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciacpi_status 24762306a36Sopenharmony_ciacpi_ex_write_serial_bus(union acpi_operand_object *source_desc, 24862306a36Sopenharmony_ci union acpi_operand_object *obj_desc, 24962306a36Sopenharmony_ci union acpi_operand_object **return_buffer) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci acpi_status status; 25262306a36Sopenharmony_ci u32 buffer_length; 25362306a36Sopenharmony_ci u32 data_length; 25462306a36Sopenharmony_ci void *buffer; 25562306a36Sopenharmony_ci union acpi_operand_object *buffer_desc; 25662306a36Sopenharmony_ci u32 function; 25762306a36Sopenharmony_ci u16 accessor_type; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ACPI_FUNCTION_TRACE_PTR(ex_write_serial_bus, obj_desc); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * This is an SMBus, GSBus or IPMI write. We will bypass the entire 26362306a36Sopenharmony_ci * field mechanism and handoff the buffer directly to the handler. 26462306a36Sopenharmony_ci * For these address spaces, the buffer is bidirectional; on a 26562306a36Sopenharmony_ci * write, return data is returned in the same buffer. 26662306a36Sopenharmony_ci * 26762306a36Sopenharmony_ci * Source must be a buffer of sufficient size, these are fixed size: 26862306a36Sopenharmony_ci * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Note: SMBus and GSBus protocol type is passed in upper 16-bits 27162306a36Sopenharmony_ci * of Function 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Common buffer format: 27462306a36Sopenharmony_ci * Status; (Byte 0 of the data buffer) 27562306a36Sopenharmony_ci * Length; (Byte 1 of the data buffer) 27662306a36Sopenharmony_ci * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (source_desc->common.type != ACPI_TYPE_BUFFER) { 27962306a36Sopenharmony_ci ACPI_ERROR((AE_INFO, 28062306a36Sopenharmony_ci "SMBus/IPMI/GenericSerialBus write requires " 28162306a36Sopenharmony_ci "Buffer, found type %s", 28262306a36Sopenharmony_ci acpi_ut_get_object_type_name(source_desc))); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return_ACPI_STATUS(AE_AML_OPERAND_TYPE); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci switch (obj_desc->field.region_obj->region.space_id) { 28862306a36Sopenharmony_ci case ACPI_ADR_SPACE_SMBUS: 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci buffer_length = ACPI_SMBUS_BUFFER_SIZE; 29162306a36Sopenharmony_ci function = ACPI_WRITE | (obj_desc->field.attribute << 16); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci case ACPI_ADR_SPACE_IPMI: 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci buffer_length = ACPI_IPMI_BUFFER_SIZE; 29762306a36Sopenharmony_ci function = ACPI_WRITE; 29862306a36Sopenharmony_ci break; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci case ACPI_ADR_SPACE_GSBUS: 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci accessor_type = obj_desc->field.attribute; 30362306a36Sopenharmony_ci status = 30462306a36Sopenharmony_ci acpi_ex_get_protocol_buffer_length(accessor_type, 30562306a36Sopenharmony_ci &buffer_length); 30662306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 30762306a36Sopenharmony_ci ACPI_ERROR((AE_INFO, 30862306a36Sopenharmony_ci "Invalid protocol ID for GSBus: 0x%4.4X", 30962306a36Sopenharmony_ci accessor_type)); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return_ACPI_STATUS(status); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Add header length to get the full size of the buffer */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci buffer_length += ACPI_SERIAL_HEADER_SIZE; 31762306a36Sopenharmony_ci function = ACPI_WRITE | (accessor_type << 16); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci case ACPI_ADR_SPACE_PLATFORM_RT: 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE; 32362306a36Sopenharmony_ci function = ACPI_WRITE; 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci case ACPI_ADR_SPACE_FIXED_HARDWARE: 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE; 32962306a36Sopenharmony_ci function = ACPI_WRITE; 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci default: 33362306a36Sopenharmony_ci return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Create the transfer/bidirectional/return buffer */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci buffer_desc = acpi_ut_create_buffer_object(buffer_length); 33962306a36Sopenharmony_ci if (!buffer_desc) { 34062306a36Sopenharmony_ci return_ACPI_STATUS(AE_NO_MEMORY); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Copy the input buffer data to the transfer buffer */ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci buffer = buffer_desc->buffer.pointer; 34662306a36Sopenharmony_ci data_length = ACPI_MIN(buffer_length, source_desc->buffer.length); 34762306a36Sopenharmony_ci memcpy(buffer, source_desc->buffer.pointer, data_length); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Lock entire transaction if requested */ 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * Perform the write (returns status and perhaps data in the 35562306a36Sopenharmony_ci * same buffer) 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci status = acpi_ex_access_region(obj_desc, 0, (u64 *)buffer, function); 35862306a36Sopenharmony_ci acpi_ex_release_global_lock(obj_desc->common_field.field_flags); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci *return_buffer = buffer_desc; 36162306a36Sopenharmony_ci return_ACPI_STATUS(status); 36262306a36Sopenharmony_ci} 363