2018-10-03 11:45:37 -07:00
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name : exserial - field_unit support for serial address spaces
*
2019-01-14 09:55:25 -08:00
* Copyright ( C ) 2000 - 2019 , Intel Corp .
2018-10-03 11:45:37 -07:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <acpi/acpi.h>
# include "accommon.h"
# include "acdispat.h"
# include "acinterp.h"
# include "amlcode.h"
# define _COMPONENT ACPI_EXECUTER
ACPI_MODULE_NAME ( " exserial " )
/*******************************************************************************
*
* FUNCTION : acpi_ex_read_gpio
*
* PARAMETERS : obj_desc - The named field to read
2019-02-15 13:36:19 -08:00
* buffer - Where the return data is returned
2018-10-03 11:45:37 -07:00
*
* RETURN : Status
*
* DESCRIPTION : Read from a named field that references a Generic Serial Bus
* field
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
acpi_status acpi_ex_read_gpio ( union acpi_operand_object * obj_desc , void * buffer )
{
acpi_status status ;
ACPI_FUNCTION_TRACE_PTR ( ex_read_gpio , obj_desc ) ;
/*
* For GPIO ( general_purpose_io ) , the Address will be the bit offset
* from the previous Connection ( ) operator , making it effectively a
* pin number index . The bit_length 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 " ,
obj_desc - > field . pin_number_index ,
obj_desc - > field . bit_length ) ) ;
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock ( obj_desc - > common_field . field_flags ) ;
/* Perform the read */
status = acpi_ex_access_region ( obj_desc , 0 , ( u64 * ) buffer , ACPI_READ ) ;
acpi_ex_release_global_lock ( obj_desc - > common_field . field_flags ) ;
return_ACPI_STATUS ( status ) ;
}
/*******************************************************************************
*
* FUNCTION : acpi_ex_write_gpio
*
* PARAMETERS : source_desc - Contains data to write . Expect to be
* an Integer object .
* obj_desc - The named field
* result_desc - 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
acpi_ex_write_gpio ( union acpi_operand_object * source_desc ,
union acpi_operand_object * obj_desc ,
union acpi_operand_object * * return_buffer )
{
acpi_status status ;
void * buffer ;
ACPI_FUNCTION_TRACE_PTR ( ex_write_gpio , obj_desc ) ;
/*
* For GPIO ( general_purpose_io ) , 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 bit_length is the length of the field , which
* is thus the number of pins .
*/
if ( source_desc - > 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 " ,
acpi_ut_get_type_name ( source_desc - > common . type ) ,
source_desc - > common . type ,
( u32 ) source_desc - > integer . value ,
obj_desc - > field . pin_number_index ,
obj_desc - > field . bit_length ) ) ;
buffer = & source_desc - > integer . value ;
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock ( obj_desc - > common_field . field_flags ) ;
/* Perform the write */
status = acpi_ex_access_region ( obj_desc , 0 , ( u64 * ) buffer , ACPI_WRITE ) ;
acpi_ex_release_global_lock ( obj_desc - > common_field . field_flags ) ;
return_ACPI_STATUS ( status ) ;
}
/*******************************************************************************
*
* FUNCTION : acpi_ex_read_serial_bus
*
* PARAMETERS : obj_desc - The named field to read
* return_buffer - 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
acpi_ex_read_serial_bus ( union acpi_operand_object * obj_desc ,
union acpi_operand_object * * return_buffer )
{
acpi_status status ;
u32 buffer_length ;
union acpi_operand_object * buffer_desc ;
u32 function ;
u16 accessor_type ;
ACPI_FUNCTION_TRACE_PTR ( ex_read_serial_bus , obj_desc ) ;
/*
* 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 ( obj_desc - > field . region_obj - > region . space_id ) {
case ACPI_ADR_SPACE_SMBUS :
buffer_length = ACPI_SMBUS_BUFFER_SIZE ;
function = ACPI_READ | ( obj_desc - > field . attribute < < 16 ) ;
break ;
case ACPI_ADR_SPACE_IPMI :
buffer_length = ACPI_IPMI_BUFFER_SIZE ;
function = ACPI_READ ;
break ;
case ACPI_ADR_SPACE_GSBUS :
accessor_type = obj_desc - > field . attribute ;
if ( accessor_type = = 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 =
acpi_ex_get_protocol_buffer_length ( accessor_type ,
& buffer_length ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_ERROR ( ( AE_INFO ,
" Invalid protocol ID for GSBus: 0x%4.4X " ,
accessor_type ) ) ;
return_ACPI_STATUS ( status ) ;
}
/* Add header length to get the full size of the buffer */
buffer_length + = ACPI_SERIAL_HEADER_SIZE ;
function = ACPI_READ | ( accessor_type < < 16 ) ;
break ;
default :
return_ACPI_STATUS ( AE_AML_INVALID_SPACE_ID ) ;
}
/* Create the local transfer buffer that is returned to the caller */
buffer_desc = acpi_ut_create_buffer_object ( buffer_length ) ;
if ( ! buffer_desc ) {
return_ACPI_STATUS ( AE_NO_MEMORY ) ;
}
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock ( obj_desc - > common_field . field_flags ) ;
/* Call the region handler for the write-then-read */
status = acpi_ex_access_region ( obj_desc , 0 ,
ACPI_CAST_PTR ( u64 ,
buffer_desc - > buffer .
pointer ) , function ) ;
acpi_ex_release_global_lock ( obj_desc - > common_field . field_flags ) ;
* return_buffer = buffer_desc ;
return_ACPI_STATUS ( status ) ;
}
/*******************************************************************************
*
* FUNCTION : acpi_ex_write_serial_bus
*
* PARAMETERS : source_desc - Contains data to write
* obj_desc - The named field
* return_buffer - 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
acpi_ex_write_serial_bus ( union acpi_operand_object * source_desc ,
union acpi_operand_object * obj_desc ,
union acpi_operand_object * * return_buffer )
{
acpi_status status ;
u32 buffer_length ;
2018-12-13 12:30:34 -08:00
u32 data_length ;
2018-10-03 11:45:37 -07:00
void * buffer ;
union acpi_operand_object * buffer_desc ;
u32 function ;
u16 accessor_type ;
ACPI_FUNCTION_TRACE_PTR ( ex_write_serial_bus , obj_desc ) ;
/*
* 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 ( source_desc - > common . type ! = ACPI_TYPE_BUFFER ) {
ACPI_ERROR ( ( AE_INFO ,
" SMBus/IPMI/GenericSerialBus write requires "
" Buffer, found type %s " ,
acpi_ut_get_object_type_name ( source_desc ) ) ) ;
return_ACPI_STATUS ( AE_AML_OPERAND_TYPE ) ;
}
switch ( obj_desc - > field . region_obj - > region . space_id ) {
case ACPI_ADR_SPACE_SMBUS :
buffer_length = ACPI_SMBUS_BUFFER_SIZE ;
function = ACPI_WRITE | ( obj_desc - > field . attribute < < 16 ) ;
break ;
case ACPI_ADR_SPACE_IPMI :
buffer_length = ACPI_IPMI_BUFFER_SIZE ;
function = ACPI_WRITE ;
break ;
case ACPI_ADR_SPACE_GSBUS :
accessor_type = obj_desc - > field . attribute ;
status =
acpi_ex_get_protocol_buffer_length ( accessor_type ,
& buffer_length ) ;
if ( ACPI_FAILURE ( status ) ) {
ACPI_ERROR ( ( AE_INFO ,
" Invalid protocol ID for GSBus: 0x%4.4X " ,
accessor_type ) ) ;
return_ACPI_STATUS ( status ) ;
}
/* Add header length to get the full size of the buffer */
buffer_length + = ACPI_SERIAL_HEADER_SIZE ;
function = ACPI_WRITE | ( accessor_type < < 16 ) ;
break ;
default :
return_ACPI_STATUS ( AE_AML_INVALID_SPACE_ID ) ;
}
/* Create the transfer/bidirectional/return buffer */
buffer_desc = acpi_ut_create_buffer_object ( buffer_length ) ;
if ( ! buffer_desc ) {
return_ACPI_STATUS ( AE_NO_MEMORY ) ;
}
/* Copy the input buffer data to the transfer buffer */
buffer = buffer_desc - > buffer . pointer ;
2018-12-13 12:30:34 -08:00
data_length = ( buffer_length < source_desc - > buffer . length ?
buffer_length : source_desc - > buffer . length ) ;
memcpy ( buffer , source_desc - > buffer . pointer , data_length ) ;
2018-10-03 11:45:37 -07:00
/* Lock entire transaction if requested */
acpi_ex_acquire_global_lock ( obj_desc - > common_field . field_flags ) ;
/*
* Perform the write ( returns status and perhaps data in the
* same buffer )
*/
status = acpi_ex_access_region ( obj_desc , 0 , ( u64 * ) buffer , function ) ;
acpi_ex_release_global_lock ( obj_desc - > common_field . field_flags ) ;
* return_buffer = buffer_desc ;
return_ACPI_STATUS ( status ) ;
}