| /****************************************************************************** |
| * |
| * Module Name: exserial - FieldUnit support for serial address spaces |
| * |
| *****************************************************************************/ |
| |
| /* |
| * Copyright (C) 2000 - 2023, Intel Corp. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions, and the following disclaimer, |
| * without modification. |
| * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
| * substantially similar to the "NO WARRANTY" disclaimer below |
| * ("Disclaimer") and any redistribution must be conditioned upon |
| * including a substantially similar Disclaimer requirement for further |
| * binary redistribution. |
| * 3. Neither the names of the above-listed copyright holders nor the names |
| * of any contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * Alternatively, this software may be distributed under the terms of the |
| * GNU General Public License ("GPL") version 2 as published by the Free |
| * Software Foundation. |
| * |
| * NO WARRANTY |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
| * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGES. |
| */ |
| |
| #include "acpi.h" |
| #include "accommon.h" |
| #include "acdispat.h" |
| #include "acinterp.h" |
| #include "amlcode.h" |
| |
| |
| #define _COMPONENT ACPI_EXECUTER |
| ACPI_MODULE_NAME ("exserial") |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiExReadGpio |
| * |
| * PARAMETERS: ObjDesc - The named field to read |
| * Buffer - Where the return data is returned |
| * |
| * RETURN: Status |
| * |
| * DESCRIPTION: Read from a named field that references a Generic Serial Bus |
| * field |
| * |
| ******************************************************************************/ |
| |
| ACPI_STATUS |
| AcpiExReadGpio ( |
| ACPI_OPERAND_OBJECT *ObjDesc, |
| void *Buffer) |
| { |
| ACPI_STATUS Status; |
| |
| |
| ACPI_FUNCTION_TRACE_PTR (ExReadGpio, ObjDesc); |
| |
| |
| /* |
| * For GPIO (GeneralPurposeIo), the Address will be the bit offset |
| * from the previous Connection() operator, making it effectively a |
| * pin number index. The BitLength is the length of the field, which |
| * is thus the number of pins. |
| */ |
| ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, |
| "GPIO FieldRead [FROM]: Pin %u Bits %u\n", |
| ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength)); |
| |
| /* Lock entire transaction if requested */ |
| |
| AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| /* Perform the read */ |
| |
| Status = AcpiExAccessRegion ( |
| ObjDesc, 0, (UINT64 *) Buffer, ACPI_READ); |
| |
| AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); |
| return_ACPI_STATUS (Status); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiExWriteGpio |
| * |
| * PARAMETERS: SourceDesc - Contains data to write. Expect to be |
| * an Integer object. |
| * ObjDesc - The named field |
| * ResultDesc - Where the return value is returned, if any |
| * |
| * RETURN: Status |
| * |
| * DESCRIPTION: Write to a named field that references a General Purpose I/O |
| * field. |
| * |
| ******************************************************************************/ |
| |
| ACPI_STATUS |
| AcpiExWriteGpio ( |
| ACPI_OPERAND_OBJECT *SourceDesc, |
| ACPI_OPERAND_OBJECT *ObjDesc, |
| ACPI_OPERAND_OBJECT **ReturnBuffer) |
| { |
| ACPI_STATUS Status; |
| void *Buffer; |
| |
| |
| ACPI_FUNCTION_TRACE_PTR (ExWriteGpio, ObjDesc); |
| |
| |
| /* |
| * For GPIO (GeneralPurposeIo), we will bypass the entire field |
| * mechanism and handoff the bit address and bit width directly to |
| * the handler. The Address will be the bit offset |
| * from the previous Connection() operator, making it effectively a |
| * pin number index. The BitLength is the length of the field, which |
| * is thus the number of pins. |
| */ |
| if (SourceDesc->Common.Type != ACPI_TYPE_INTEGER) |
| { |
| return_ACPI_STATUS (AE_AML_OPERAND_TYPE); |
| } |
| |
| ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, |
| "GPIO FieldWrite [FROM]: (%s:%X), Value %.8X [TO]: Pin %u Bits %u\n", |
| AcpiUtGetTypeName (SourceDesc->Common.Type), |
| SourceDesc->Common.Type, (UINT32) SourceDesc->Integer.Value, |
| ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength)); |
| |
| Buffer = &SourceDesc->Integer.Value; |
| |
| /* Lock entire transaction if requested */ |
| |
| AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| /* Perform the write */ |
| |
| Status = AcpiExAccessRegion ( |
| ObjDesc, 0, (UINT64 *) Buffer, ACPI_WRITE); |
| AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); |
| return_ACPI_STATUS (Status); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiExReadSerialBus |
| * |
| * PARAMETERS: ObjDesc - The named field to read |
| * ReturnBuffer - Where the return value is returned, if any |
| * |
| * RETURN: Status |
| * |
| * DESCRIPTION: Read from a named field that references a serial bus |
| * (SMBus, IPMI, or GSBus). |
| * |
| ******************************************************************************/ |
| |
| ACPI_STATUS |
| AcpiExReadSerialBus ( |
| ACPI_OPERAND_OBJECT *ObjDesc, |
| ACPI_OPERAND_OBJECT **ReturnBuffer) |
| { |
| ACPI_STATUS Status; |
| UINT32 BufferLength; |
| ACPI_OPERAND_OBJECT *BufferDesc; |
| UINT32 Function; |
| UINT16 AccessorType; |
| |
| |
| ACPI_FUNCTION_TRACE_PTR (ExReadSerialBus, ObjDesc); |
| |
| |
| /* |
| * This is an SMBus, GSBus or IPMI read. We must create a buffer to |
| * hold the data and then directly access the region handler. |
| * |
| * Note: SMBus and GSBus protocol value is passed in upper 16-bits |
| * of Function |
| * |
| * Common buffer format: |
| * Status; (Byte 0 of the data buffer) |
| * Length; (Byte 1 of the data buffer) |
| * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) |
| */ |
| switch (ObjDesc->Field.RegionObj->Region.SpaceId) |
| { |
| case ACPI_ADR_SPACE_SMBUS: |
| |
| BufferLength = ACPI_SMBUS_BUFFER_SIZE; |
| Function = ACPI_READ | (ObjDesc->Field.Attribute << 16); |
| break; |
| |
| case ACPI_ADR_SPACE_IPMI: |
| |
| BufferLength = ACPI_IPMI_BUFFER_SIZE; |
| Function = ACPI_READ; |
| break; |
| |
| case ACPI_ADR_SPACE_GSBUS: |
| |
| AccessorType = ObjDesc->Field.Attribute; |
| if (AccessorType == AML_FIELD_ATTRIB_RAW_PROCESS_BYTES) |
| { |
| ACPI_ERROR ((AE_INFO, |
| "Invalid direct read using bidirectional write-then-read protocol")); |
| |
| return_ACPI_STATUS (AE_AML_PROTOCOL); |
| } |
| |
| Status = AcpiExGetProtocolBufferLength (AccessorType, &BufferLength); |
| if (ACPI_FAILURE (Status)) |
| { |
| ACPI_ERROR ((AE_INFO, |
| "Invalid protocol ID for GSBus: 0x%4.4X", AccessorType)); |
| |
| return_ACPI_STATUS (Status); |
| } |
| |
| /* Add header length to get the full size of the buffer */ |
| |
| BufferLength += ACPI_SERIAL_HEADER_SIZE; |
| Function = ACPI_READ | (AccessorType << 16); |
| break; |
| |
| case ACPI_ADR_SPACE_PLATFORM_RT: |
| |
| BufferLength = ACPI_PRM_INPUT_BUFFER_SIZE; |
| Function = ACPI_READ; |
| break; |
| |
| default: |
| return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID); |
| } |
| |
| /* Create the local transfer buffer that is returned to the caller */ |
| |
| BufferDesc = AcpiUtCreateBufferObject (BufferLength); |
| if (!BufferDesc) |
| { |
| return_ACPI_STATUS (AE_NO_MEMORY); |
| } |
| |
| /* Lock entire transaction if requested */ |
| |
| AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| /* Call the region handler for the write-then-read */ |
| |
| Status = AcpiExAccessRegion (ObjDesc, 0, |
| ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer), Function); |
| AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| *ReturnBuffer = BufferDesc; |
| return_ACPI_STATUS (Status); |
| } |
| |
| |
| /******************************************************************************* |
| * |
| * FUNCTION: AcpiExWriteSerialBus |
| * |
| * PARAMETERS: SourceDesc - Contains data to write |
| * ObjDesc - The named field |
| * ReturnBuffer - Where the return value is returned, if any |
| * |
| * RETURN: Status |
| * |
| * DESCRIPTION: Write to a named field that references a serial bus |
| * (SMBus, IPMI, GSBus). |
| * |
| ******************************************************************************/ |
| |
| ACPI_STATUS |
| AcpiExWriteSerialBus ( |
| ACPI_OPERAND_OBJECT *SourceDesc, |
| ACPI_OPERAND_OBJECT *ObjDesc, |
| ACPI_OPERAND_OBJECT **ReturnBuffer) |
| { |
| ACPI_STATUS Status; |
| UINT32 BufferLength; |
| UINT32 DataLength; |
| void *Buffer; |
| ACPI_OPERAND_OBJECT *BufferDesc; |
| UINT32 Function; |
| UINT16 AccessorType; |
| |
| |
| ACPI_FUNCTION_TRACE_PTR (ExWriteSerialBus, ObjDesc); |
| |
| |
| /* |
| * This is an SMBus, GSBus or IPMI write. We will bypass the entire |
| * field mechanism and handoff the buffer directly to the handler. |
| * For these address spaces, the buffer is bidirectional; on a |
| * write, return data is returned in the same buffer. |
| * |
| * Source must be a buffer of sufficient size, these are fixed size: |
| * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. |
| * |
| * Note: SMBus and GSBus protocol type is passed in upper 16-bits |
| * of Function |
| * |
| * Common buffer format: |
| * Status; (Byte 0 of the data buffer) |
| * Length; (Byte 1 of the data buffer) |
| * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) |
| */ |
| if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER) |
| { |
| ACPI_ERROR ((AE_INFO, |
| "SMBus/IPMI/GenericSerialBus write requires " |
| "Buffer, found type %s", |
| AcpiUtGetObjectTypeName (SourceDesc))); |
| |
| return_ACPI_STATUS (AE_AML_OPERAND_TYPE); |
| } |
| |
| switch (ObjDesc->Field.RegionObj->Region.SpaceId) |
| { |
| case ACPI_ADR_SPACE_SMBUS: |
| |
| BufferLength = ACPI_SMBUS_BUFFER_SIZE; |
| Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16); |
| break; |
| |
| case ACPI_ADR_SPACE_IPMI: |
| |
| BufferLength = ACPI_IPMI_BUFFER_SIZE; |
| Function = ACPI_WRITE; |
| break; |
| |
| case ACPI_ADR_SPACE_GSBUS: |
| |
| AccessorType = ObjDesc->Field.Attribute; |
| Status = AcpiExGetProtocolBufferLength (AccessorType, &BufferLength); |
| if (ACPI_FAILURE (Status)) |
| { |
| ACPI_ERROR ((AE_INFO, |
| "Invalid protocol ID for GSBus: 0x%4.4X", AccessorType)); |
| |
| return_ACPI_STATUS (Status); |
| } |
| |
| /* Add header length to get the full size of the buffer */ |
| |
| BufferLength += ACPI_SERIAL_HEADER_SIZE; |
| Function = ACPI_WRITE | (AccessorType << 16); |
| break; |
| |
| case ACPI_ADR_SPACE_PLATFORM_RT: |
| |
| BufferLength = ACPI_PRM_INPUT_BUFFER_SIZE; |
| Function = ACPI_WRITE; |
| break; |
| |
| case ACPI_ADR_SPACE_FIXED_HARDWARE: |
| |
| BufferLength = ACPI_FFH_INPUT_BUFFER_SIZE; |
| Function = ACPI_WRITE; |
| break; |
| |
| default: |
| return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID); |
| } |
| |
| /* Create the transfer/bidirectional/return buffer */ |
| |
| BufferDesc = AcpiUtCreateBufferObject (BufferLength); |
| if (!BufferDesc) |
| { |
| return_ACPI_STATUS (AE_NO_MEMORY); |
| } |
| |
| /* Copy the input buffer data to the transfer buffer */ |
| |
| Buffer = BufferDesc->Buffer.Pointer; |
| DataLength = (BufferLength < SourceDesc->Buffer.Length ? |
| BufferLength : SourceDesc->Buffer.Length); |
| memcpy (Buffer, SourceDesc->Buffer.Pointer, DataLength); |
| |
| /* Lock entire transaction if requested */ |
| |
| AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| /* |
| * Perform the write (returns status and perhaps data in the |
| * same buffer) |
| */ |
| Status = AcpiExAccessRegion ( |
| ObjDesc, 0, (UINT64 *) Buffer, Function); |
| AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); |
| |
| *ReturnBuffer = BufferDesc; |
| return_ACPI_STATUS (Status); |
| } |