| 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 | 
|---|
| 2 | /****************************************************************************** | 
|---|
| 3 | * | 
|---|
| 4 | * Module Name: exserial - field_unit support for serial address spaces | 
|---|
| 5 | * | 
|---|
| 6 | * Copyright (C) 2000 - 2025, Intel Corp. | 
|---|
| 7 | * | 
|---|
| 8 | *****************************************************************************/ | 
|---|
| 9 |  | 
|---|
| 10 | #include <acpi/acpi.h> | 
|---|
| 11 | #include "accommon.h" | 
|---|
| 12 | #include "acdispat.h" | 
|---|
| 13 | #include "acinterp.h" | 
|---|
| 14 | #include "amlcode.h" | 
|---|
| 15 |  | 
|---|
| 16 | #define _COMPONENT          ACPI_EXECUTER | 
|---|
| 17 | ACPI_MODULE_NAME( "exserial") | 
|---|
| 18 |  | 
|---|
| 19 | /******************************************************************************* | 
|---|
| 20 | * | 
|---|
| 21 | * FUNCTION:    acpi_ex_read_gpio | 
|---|
| 22 | * | 
|---|
| 23 | * PARAMETERS:  obj_desc            - The named field to read | 
|---|
| 24 | *              buffer              - Where the return data is returned | 
|---|
| 25 | * | 
|---|
| 26 | * RETURN:      Status | 
|---|
| 27 | * | 
|---|
| 28 | * DESCRIPTION: Read from a named field that references a Generic Serial Bus | 
|---|
| 29 | *              field | 
|---|
| 30 | * | 
|---|
| 31 | ******************************************************************************/ | 
|---|
| 32 | acpi_status acpi_ex_read_gpio(union acpi_operand_object *obj_desc, void *buffer) | 
|---|
| 33 | { | 
|---|
| 34 | acpi_status status; | 
|---|
| 35 |  | 
|---|
| 36 | ACPI_FUNCTION_TRACE_PTR(ex_read_gpio, obj_desc); | 
|---|
| 37 |  | 
|---|
| 38 | /* | 
|---|
| 39 | * For GPIO (general_purpose_io), the Address will be the bit offset | 
|---|
| 40 | * from the previous Connection() operator, making it effectively a | 
|---|
| 41 | * pin number index. The bit_length is the length of the field, which | 
|---|
| 42 | * is thus the number of pins. | 
|---|
| 43 | */ | 
|---|
| 44 | ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | 
|---|
| 45 | "GPIO FieldRead [FROM]:  Pin %u Bits %u\n", | 
|---|
| 46 | obj_desc->field.pin_number_index, | 
|---|
| 47 | obj_desc->field.bit_length)); | 
|---|
| 48 |  | 
|---|
| 49 | /* Lock entire transaction if requested */ | 
|---|
| 50 |  | 
|---|
| 51 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 52 |  | 
|---|
| 53 | /* Perform the read */ | 
|---|
| 54 |  | 
|---|
| 55 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, value: (u64 *)buffer, ACPI_READ); | 
|---|
| 56 |  | 
|---|
| 57 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 58 | return_ACPI_STATUS(status); | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | /******************************************************************************* | 
|---|
| 62 | * | 
|---|
| 63 | * FUNCTION:    acpi_ex_write_gpio | 
|---|
| 64 | * | 
|---|
| 65 | * PARAMETERS:  source_desc         - Contains data to write. Expect to be | 
|---|
| 66 | *                                    an Integer object. | 
|---|
| 67 | *              obj_desc            - The named field | 
|---|
| 68 | *              result_desc         - Where the return value is returned, if any | 
|---|
| 69 | * | 
|---|
| 70 | * RETURN:      Status | 
|---|
| 71 | * | 
|---|
| 72 | * DESCRIPTION: Write to a named field that references a General Purpose I/O | 
|---|
| 73 | *              field. | 
|---|
| 74 | * | 
|---|
| 75 | ******************************************************************************/ | 
|---|
| 76 |  | 
|---|
| 77 | acpi_status | 
|---|
| 78 | acpi_ex_write_gpio(union acpi_operand_object *source_desc, | 
|---|
| 79 | union acpi_operand_object *obj_desc, | 
|---|
| 80 | union acpi_operand_object **return_buffer) | 
|---|
| 81 | { | 
|---|
| 82 | acpi_status status; | 
|---|
| 83 | void *buffer; | 
|---|
| 84 |  | 
|---|
| 85 | ACPI_FUNCTION_TRACE_PTR(ex_write_gpio, obj_desc); | 
|---|
| 86 |  | 
|---|
| 87 | /* | 
|---|
| 88 | * For GPIO (general_purpose_io), we will bypass the entire field | 
|---|
| 89 | * mechanism and handoff the bit address and bit width directly to | 
|---|
| 90 | * the handler. The Address will be the bit offset | 
|---|
| 91 | * from the previous Connection() operator, making it effectively a | 
|---|
| 92 | * pin number index. The bit_length is the length of the field, which | 
|---|
| 93 | * is thus the number of pins. | 
|---|
| 94 | */ | 
|---|
| 95 | if (source_desc->common.type != ACPI_TYPE_INTEGER) { | 
|---|
| 96 | return_ACPI_STATUS(AE_AML_OPERAND_TYPE); | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | ACPI_DEBUG_PRINT((ACPI_DB_BFIELD, | 
|---|
| 100 | "GPIO FieldWrite [FROM]: (%s:%X), Value %.8X  [TO]: Pin %u Bits %u\n", | 
|---|
| 101 | acpi_ut_get_type_name(source_desc->common.type), | 
|---|
| 102 | source_desc->common.type, | 
|---|
| 103 | (u32)source_desc->integer.value, | 
|---|
| 104 | obj_desc->field.pin_number_index, | 
|---|
| 105 | obj_desc->field.bit_length)); | 
|---|
| 106 |  | 
|---|
| 107 | buffer = &source_desc->integer.value; | 
|---|
| 108 |  | 
|---|
| 109 | /* Lock entire transaction if requested */ | 
|---|
| 110 |  | 
|---|
| 111 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 112 |  | 
|---|
| 113 | /* Perform the write */ | 
|---|
| 114 |  | 
|---|
| 115 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, value: (u64 *)buffer, ACPI_WRITE); | 
|---|
| 116 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 117 | return_ACPI_STATUS(status); | 
|---|
| 118 | } | 
|---|
| 119 |  | 
|---|
| 120 | /******************************************************************************* | 
|---|
| 121 | * | 
|---|
| 122 | * FUNCTION:    acpi_ex_read_serial_bus | 
|---|
| 123 | * | 
|---|
| 124 | * PARAMETERS:  obj_desc            - The named field to read | 
|---|
| 125 | *              return_buffer       - Where the return value is returned, if any | 
|---|
| 126 | * | 
|---|
| 127 | * RETURN:      Status | 
|---|
| 128 | * | 
|---|
| 129 | * DESCRIPTION: Read from a named field that references a serial bus | 
|---|
| 130 | *              (SMBus, IPMI, or GSBus). | 
|---|
| 131 | * | 
|---|
| 132 | ******************************************************************************/ | 
|---|
| 133 |  | 
|---|
| 134 | acpi_status | 
|---|
| 135 | acpi_ex_read_serial_bus(union acpi_operand_object *obj_desc, | 
|---|
| 136 | union acpi_operand_object **return_buffer) | 
|---|
| 137 | { | 
|---|
| 138 | acpi_status status; | 
|---|
| 139 | u32 buffer_length; | 
|---|
| 140 | union acpi_operand_object *buffer_desc; | 
|---|
| 141 | u32 function; | 
|---|
| 142 | u16 accessor_type; | 
|---|
| 143 |  | 
|---|
| 144 | ACPI_FUNCTION_TRACE_PTR(ex_read_serial_bus, obj_desc); | 
|---|
| 145 |  | 
|---|
| 146 | /* | 
|---|
| 147 | * This is an SMBus, GSBus or IPMI read. We must create a buffer to | 
|---|
| 148 | * hold the data and then directly access the region handler. | 
|---|
| 149 | * | 
|---|
| 150 | * Note: SMBus and GSBus protocol value is passed in upper 16-bits | 
|---|
| 151 | * of Function | 
|---|
| 152 | * | 
|---|
| 153 | * Common buffer format: | 
|---|
| 154 | *     Status;    (Byte 0 of the data buffer) | 
|---|
| 155 | *     Length;    (Byte 1 of the data buffer) | 
|---|
| 156 | *     Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) | 
|---|
| 157 | */ | 
|---|
| 158 | switch (obj_desc->field.region_obj->region.space_id) { | 
|---|
| 159 | case ACPI_ADR_SPACE_SMBUS: | 
|---|
| 160 |  | 
|---|
| 161 | buffer_length = ACPI_SMBUS_BUFFER_SIZE; | 
|---|
| 162 | function = ACPI_READ | (obj_desc->field.attribute << 16); | 
|---|
| 163 | break; | 
|---|
| 164 |  | 
|---|
| 165 | case ACPI_ADR_SPACE_IPMI: | 
|---|
| 166 |  | 
|---|
| 167 | buffer_length = ACPI_IPMI_BUFFER_SIZE; | 
|---|
| 168 | function = ACPI_READ; | 
|---|
| 169 | break; | 
|---|
| 170 |  | 
|---|
| 171 | case ACPI_ADR_SPACE_GSBUS: | 
|---|
| 172 |  | 
|---|
| 173 | accessor_type = obj_desc->field.attribute; | 
|---|
| 174 | if (accessor_type == AML_FIELD_ATTRIB_RAW_PROCESS_BYTES) { | 
|---|
| 175 | ACPI_ERROR((AE_INFO, | 
|---|
| 176 | "Invalid direct read using bidirectional write-then-read protocol")); | 
|---|
| 177 |  | 
|---|
| 178 | return_ACPI_STATUS(AE_AML_PROTOCOL); | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | status = | 
|---|
| 182 | acpi_ex_get_protocol_buffer_length(protocol_id: accessor_type, | 
|---|
| 183 | return_length: &buffer_length); | 
|---|
| 184 | if (ACPI_FAILURE(status)) { | 
|---|
| 185 | ACPI_ERROR((AE_INFO, | 
|---|
| 186 | "Invalid protocol ID for GSBus: 0x%4.4X", | 
|---|
| 187 | accessor_type)); | 
|---|
| 188 |  | 
|---|
| 189 | return_ACPI_STATUS(status); | 
|---|
| 190 | } | 
|---|
| 191 |  | 
|---|
| 192 | /* Add header length to get the full size of the buffer */ | 
|---|
| 193 |  | 
|---|
| 194 | buffer_length += ACPI_SERIAL_HEADER_SIZE; | 
|---|
| 195 | function = ACPI_READ | (accessor_type << 16); | 
|---|
| 196 | break; | 
|---|
| 197 |  | 
|---|
| 198 | case ACPI_ADR_SPACE_PLATFORM_RT: | 
|---|
| 199 |  | 
|---|
| 200 | buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE; | 
|---|
| 201 | function = ACPI_READ; | 
|---|
| 202 | break; | 
|---|
| 203 |  | 
|---|
| 204 | case ACPI_ADR_SPACE_FIXED_HARDWARE: | 
|---|
| 205 |  | 
|---|
| 206 | buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE; | 
|---|
| 207 | function = ACPI_READ; | 
|---|
| 208 | break; | 
|---|
| 209 |  | 
|---|
| 210 | default: | 
|---|
| 211 | return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | /* Create the local transfer buffer that is returned to the caller */ | 
|---|
| 215 |  | 
|---|
| 216 | buffer_desc = acpi_ut_create_buffer_object(buffer_size: buffer_length); | 
|---|
| 217 | if (!buffer_desc) { | 
|---|
| 218 | return_ACPI_STATUS(AE_NO_MEMORY); | 
|---|
| 219 | } | 
|---|
| 220 |  | 
|---|
| 221 | /* Lock entire transaction if requested */ | 
|---|
| 222 |  | 
|---|
| 223 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 224 |  | 
|---|
| 225 | /* Call the region handler for the write-then-read */ | 
|---|
| 226 |  | 
|---|
| 227 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, | 
|---|
| 228 | ACPI_CAST_PTR(u64, | 
|---|
| 229 | buffer_desc->buffer. | 
|---|
| 230 | pointer), read_write: function); | 
|---|
| 231 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 232 |  | 
|---|
| 233 | *return_buffer = buffer_desc; | 
|---|
| 234 | return_ACPI_STATUS(status); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | /******************************************************************************* | 
|---|
| 238 | * | 
|---|
| 239 | * FUNCTION:    acpi_ex_write_serial_bus | 
|---|
| 240 | * | 
|---|
| 241 | * PARAMETERS:  source_desc         - Contains data to write | 
|---|
| 242 | *              obj_desc            - The named field | 
|---|
| 243 | *              return_buffer       - Where the return value is returned, if any | 
|---|
| 244 | * | 
|---|
| 245 | * RETURN:      Status | 
|---|
| 246 | * | 
|---|
| 247 | * DESCRIPTION: Write to a named field that references a serial bus | 
|---|
| 248 | *              (SMBus, IPMI, GSBus). | 
|---|
| 249 | * | 
|---|
| 250 | ******************************************************************************/ | 
|---|
| 251 |  | 
|---|
| 252 | acpi_status | 
|---|
| 253 | acpi_ex_write_serial_bus(union acpi_operand_object *source_desc, | 
|---|
| 254 | union acpi_operand_object *obj_desc, | 
|---|
| 255 | union acpi_operand_object **return_buffer) | 
|---|
| 256 | { | 
|---|
| 257 | acpi_status status; | 
|---|
| 258 | u32 buffer_length; | 
|---|
| 259 | u32 data_length; | 
|---|
| 260 | void *buffer; | 
|---|
| 261 | union acpi_operand_object *buffer_desc; | 
|---|
| 262 | u32 function; | 
|---|
| 263 | u16 accessor_type; | 
|---|
| 264 |  | 
|---|
| 265 | ACPI_FUNCTION_TRACE_PTR(ex_write_serial_bus, obj_desc); | 
|---|
| 266 |  | 
|---|
| 267 | /* | 
|---|
| 268 | * This is an SMBus, GSBus or IPMI write. We will bypass the entire | 
|---|
| 269 | * field mechanism and handoff the buffer directly to the handler. | 
|---|
| 270 | * For these address spaces, the buffer is bidirectional; on a | 
|---|
| 271 | * write, return data is returned in the same buffer. | 
|---|
| 272 | * | 
|---|
| 273 | * Source must be a buffer of sufficient size, these are fixed size: | 
|---|
| 274 | * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. | 
|---|
| 275 | * | 
|---|
| 276 | * Note: SMBus and GSBus protocol type is passed in upper 16-bits | 
|---|
| 277 | * of Function | 
|---|
| 278 | * | 
|---|
| 279 | * Common buffer format: | 
|---|
| 280 | *     Status;    (Byte 0 of the data buffer) | 
|---|
| 281 | *     Length;    (Byte 1 of the data buffer) | 
|---|
| 282 | *     Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) | 
|---|
| 283 | */ | 
|---|
| 284 | if (source_desc->common.type != ACPI_TYPE_BUFFER) { | 
|---|
| 285 | ACPI_ERROR((AE_INFO, | 
|---|
| 286 | "SMBus/IPMI/GenericSerialBus write requires " | 
|---|
| 287 | "Buffer, found type %s", | 
|---|
| 288 | acpi_ut_get_object_type_name(source_desc))); | 
|---|
| 289 |  | 
|---|
| 290 | return_ACPI_STATUS(AE_AML_OPERAND_TYPE); | 
|---|
| 291 | } | 
|---|
| 292 |  | 
|---|
| 293 | switch (obj_desc->field.region_obj->region.space_id) { | 
|---|
| 294 | case ACPI_ADR_SPACE_SMBUS: | 
|---|
| 295 |  | 
|---|
| 296 | buffer_length = ACPI_SMBUS_BUFFER_SIZE; | 
|---|
| 297 | function = ACPI_WRITE | (obj_desc->field.attribute << 16); | 
|---|
| 298 | break; | 
|---|
| 299 |  | 
|---|
| 300 | case ACPI_ADR_SPACE_IPMI: | 
|---|
| 301 |  | 
|---|
| 302 | buffer_length = ACPI_IPMI_BUFFER_SIZE; | 
|---|
| 303 | function = ACPI_WRITE; | 
|---|
| 304 | break; | 
|---|
| 305 |  | 
|---|
| 306 | case ACPI_ADR_SPACE_GSBUS: | 
|---|
| 307 |  | 
|---|
| 308 | accessor_type = obj_desc->field.attribute; | 
|---|
| 309 | status = | 
|---|
| 310 | acpi_ex_get_protocol_buffer_length(protocol_id: accessor_type, | 
|---|
| 311 | return_length: &buffer_length); | 
|---|
| 312 | if (ACPI_FAILURE(status)) { | 
|---|
| 313 | ACPI_ERROR((AE_INFO, | 
|---|
| 314 | "Invalid protocol ID for GSBus: 0x%4.4X", | 
|---|
| 315 | accessor_type)); | 
|---|
| 316 |  | 
|---|
| 317 | return_ACPI_STATUS(status); | 
|---|
| 318 | } | 
|---|
| 319 |  | 
|---|
| 320 | /* Add header length to get the full size of the buffer */ | 
|---|
| 321 |  | 
|---|
| 322 | buffer_length += ACPI_SERIAL_HEADER_SIZE; | 
|---|
| 323 | function = ACPI_WRITE | (accessor_type << 16); | 
|---|
| 324 | break; | 
|---|
| 325 |  | 
|---|
| 326 | case ACPI_ADR_SPACE_PLATFORM_RT: | 
|---|
| 327 |  | 
|---|
| 328 | buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE; | 
|---|
| 329 | function = ACPI_WRITE; | 
|---|
| 330 | break; | 
|---|
| 331 |  | 
|---|
| 332 | case ACPI_ADR_SPACE_FIXED_HARDWARE: | 
|---|
| 333 |  | 
|---|
| 334 | buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE; | 
|---|
| 335 | function = ACPI_WRITE; | 
|---|
| 336 | break; | 
|---|
| 337 |  | 
|---|
| 338 | default: | 
|---|
| 339 | return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); | 
|---|
| 340 | } | 
|---|
| 341 |  | 
|---|
| 342 | /* Create the transfer/bidirectional/return buffer */ | 
|---|
| 343 |  | 
|---|
| 344 | buffer_desc = acpi_ut_create_buffer_object(buffer_size: buffer_length); | 
|---|
| 345 | if (!buffer_desc) { | 
|---|
| 346 | return_ACPI_STATUS(AE_NO_MEMORY); | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | /* Copy the input buffer data to the transfer buffer */ | 
|---|
| 350 |  | 
|---|
| 351 | buffer = buffer_desc->buffer.pointer; | 
|---|
| 352 | data_length = ACPI_MIN(buffer_length, source_desc->buffer.length); | 
|---|
| 353 | memcpy(to: buffer, from: source_desc->buffer.pointer, len: data_length); | 
|---|
| 354 |  | 
|---|
| 355 | /* Lock entire transaction if requested */ | 
|---|
| 356 |  | 
|---|
| 357 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 358 |  | 
|---|
| 359 | /* | 
|---|
| 360 | * Perform the write (returns status and perhaps data in the | 
|---|
| 361 | * same buffer) | 
|---|
| 362 | */ | 
|---|
| 363 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, value: (u64 *)buffer, read_write: function); | 
|---|
| 364 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); | 
|---|
| 365 |  | 
|---|
| 366 | *return_buffer = buffer_desc; | 
|---|
| 367 | return_ACPI_STATUS(status); | 
|---|
| 368 | } | 
|---|
| 369 |  | 
|---|