1 /****************************************************************************** 2 * 3 * Module Name: exserial - FieldUnit support for serial address spaces 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2025, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 #include "acpi.h" 45 #include "accommon.h" 46 #include "acdispat.h" 47 #include "acinterp.h" 48 #include "amlcode.h" 49 50 51 #define _COMPONENT ACPI_EXECUTER 52 ACPI_MODULE_NAME ("exserial") 53 54 55 /******************************************************************************* 56 * 57 * FUNCTION: AcpiExReadGpio 58 * 59 * PARAMETERS: ObjDesc - The named field to read 60 * Buffer - Where the return data is returned 61 * 62 * RETURN: Status 63 * 64 * DESCRIPTION: Read from a named field that references a Generic Serial Bus 65 * field 66 * 67 ******************************************************************************/ 68 69 ACPI_STATUS 70 AcpiExReadGpio ( 71 ACPI_OPERAND_OBJECT *ObjDesc, 72 void *Buffer) 73 { 74 ACPI_STATUS Status; 75 76 77 ACPI_FUNCTION_TRACE_PTR (ExReadGpio, ObjDesc); 78 79 80 /* 81 * For GPIO (GeneralPurposeIo), the Address will be the bit offset 82 * from the previous Connection() operator, making it effectively a 83 * pin number index. The BitLength is the length of the field, which 84 * is thus the number of pins. 85 */ 86 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 87 "GPIO FieldRead [FROM]: Pin %u Bits %u\n", 88 ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength)); 89 90 /* Lock entire transaction if requested */ 91 92 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 93 94 /* Perform the read */ 95 96 Status = AcpiExAccessRegion ( 97 ObjDesc, 0, (UINT64 *) Buffer, ACPI_READ); 98 99 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 100 return_ACPI_STATUS (Status); 101 } 102 103 104 /******************************************************************************* 105 * 106 * FUNCTION: AcpiExWriteGpio 107 * 108 * PARAMETERS: SourceDesc - Contains data to write. Expect to be 109 * an Integer object. 110 * ObjDesc - The named field 111 * ResultDesc - Where the return value is returned, if any 112 * 113 * RETURN: Status 114 * 115 * DESCRIPTION: Write to a named field that references a General Purpose I/O 116 * field. 117 * 118 ******************************************************************************/ 119 120 ACPI_STATUS 121 AcpiExWriteGpio ( 122 ACPI_OPERAND_OBJECT *SourceDesc, 123 ACPI_OPERAND_OBJECT *ObjDesc, 124 ACPI_OPERAND_OBJECT **ReturnBuffer) 125 { 126 ACPI_STATUS Status; 127 void *Buffer; 128 129 130 ACPI_FUNCTION_TRACE_PTR (ExWriteGpio, ObjDesc); 131 132 133 /* 134 * For GPIO (GeneralPurposeIo), we will bypass the entire field 135 * mechanism and handoff the bit address and bit width directly to 136 * the handler. The Address will be the bit offset 137 * from the previous Connection() operator, making it effectively a 138 * pin number index. The BitLength is the length of the field, which 139 * is thus the number of pins. 140 */ 141 if (SourceDesc->Common.Type != ACPI_TYPE_INTEGER) 142 { 143 return_ACPI_STATUS (AE_AML_OPERAND_TYPE); 144 } 145 146 ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, 147 "GPIO FieldWrite [FROM]: (%s:%X), Value %.8X [TO]: Pin %u Bits %u\n", 148 AcpiUtGetTypeName (SourceDesc->Common.Type), 149 SourceDesc->Common.Type, (UINT32) SourceDesc->Integer.Value, 150 ObjDesc->Field.PinNumberIndex, ObjDesc->Field.BitLength)); 151 152 Buffer = &SourceDesc->Integer.Value; 153 154 /* Lock entire transaction if requested */ 155 156 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 157 158 /* Perform the write */ 159 160 Status = AcpiExAccessRegion ( 161 ObjDesc, 0, (UINT64 *) Buffer, ACPI_WRITE); 162 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 163 return_ACPI_STATUS (Status); 164 } 165 166 167 /******************************************************************************* 168 * 169 * FUNCTION: AcpiExReadSerialBus 170 * 171 * PARAMETERS: ObjDesc - The named field to read 172 * ReturnBuffer - Where the return value is returned, if any 173 * 174 * RETURN: Status 175 * 176 * DESCRIPTION: Read from a named field that references a serial bus 177 * (SMBus, IPMI, or GSBus). 178 * 179 ******************************************************************************/ 180 181 ACPI_STATUS 182 AcpiExReadSerialBus ( 183 ACPI_OPERAND_OBJECT *ObjDesc, 184 ACPI_OPERAND_OBJECT **ReturnBuffer) 185 { 186 ACPI_STATUS Status; 187 UINT32 BufferLength; 188 ACPI_OPERAND_OBJECT *BufferDesc; 189 UINT32 Function; 190 UINT16 AccessorType; 191 192 193 ACPI_FUNCTION_TRACE_PTR (ExReadSerialBus, ObjDesc); 194 195 196 /* 197 * This is an SMBus, GSBus or IPMI read. We must create a buffer to 198 * hold the data and then directly access the region handler. 199 * 200 * Note: SMBus and GSBus protocol value is passed in upper 16-bits 201 * of Function 202 * 203 * Common buffer format: 204 * Status; (Byte 0 of the data buffer) 205 * Length; (Byte 1 of the data buffer) 206 * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) 207 */ 208 switch (ObjDesc->Field.RegionObj->Region.SpaceId) 209 { 210 case ACPI_ADR_SPACE_SMBUS: 211 212 BufferLength = ACPI_SMBUS_BUFFER_SIZE; 213 Function = ACPI_READ | (ObjDesc->Field.Attribute << 16); 214 break; 215 216 case ACPI_ADR_SPACE_IPMI: 217 218 BufferLength = ACPI_IPMI_BUFFER_SIZE; 219 Function = ACPI_READ; 220 break; 221 222 case ACPI_ADR_SPACE_GSBUS: 223 224 AccessorType = ObjDesc->Field.Attribute; 225 if (AccessorType == AML_FIELD_ATTRIB_RAW_PROCESS_BYTES) 226 { 227 ACPI_ERROR ((AE_INFO, 228 "Invalid direct read using bidirectional write-then-read protocol")); 229 230 return_ACPI_STATUS (AE_AML_PROTOCOL); 231 } 232 233 Status = AcpiExGetProtocolBufferLength (AccessorType, &BufferLength); 234 if (ACPI_FAILURE (Status)) 235 { 236 ACPI_ERROR ((AE_INFO, 237 "Invalid protocol ID for GSBus: 0x%4.4X", AccessorType)); 238 239 return_ACPI_STATUS (Status); 240 } 241 242 /* Add header length to get the full size of the buffer */ 243 244 BufferLength += ACPI_SERIAL_HEADER_SIZE; 245 Function = ACPI_READ | (AccessorType << 16); 246 break; 247 248 case ACPI_ADR_SPACE_PLATFORM_RT: 249 250 BufferLength = ACPI_PRM_INPUT_BUFFER_SIZE; 251 Function = ACPI_READ; 252 break; 253 254 case ACPI_ADR_SPACE_FIXED_HARDWARE: 255 256 BufferLength = ACPI_FFH_INPUT_BUFFER_SIZE; 257 Function = ACPI_READ; 258 break; 259 260 default: 261 return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID); 262 } 263 264 /* Create the local transfer buffer that is returned to the caller */ 265 266 BufferDesc = AcpiUtCreateBufferObject (BufferLength); 267 if (!BufferDesc) 268 { 269 return_ACPI_STATUS (AE_NO_MEMORY); 270 } 271 272 /* Lock entire transaction if requested */ 273 274 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 275 276 /* Call the region handler for the write-then-read */ 277 278 Status = AcpiExAccessRegion (ObjDesc, 0, 279 ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer), Function); 280 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 281 282 *ReturnBuffer = BufferDesc; 283 return_ACPI_STATUS (Status); 284 } 285 286 287 /******************************************************************************* 288 * 289 * FUNCTION: AcpiExWriteSerialBus 290 * 291 * PARAMETERS: SourceDesc - Contains data to write 292 * ObjDesc - The named field 293 * ReturnBuffer - Where the return value is returned, if any 294 * 295 * RETURN: Status 296 * 297 * DESCRIPTION: Write to a named field that references a serial bus 298 * (SMBus, IPMI, GSBus). 299 * 300 ******************************************************************************/ 301 302 ACPI_STATUS 303 AcpiExWriteSerialBus ( 304 ACPI_OPERAND_OBJECT *SourceDesc, 305 ACPI_OPERAND_OBJECT *ObjDesc, 306 ACPI_OPERAND_OBJECT **ReturnBuffer) 307 { 308 ACPI_STATUS Status; 309 UINT32 BufferLength; 310 UINT32 DataLength; 311 void *Buffer; 312 ACPI_OPERAND_OBJECT *BufferDesc; 313 UINT32 Function; 314 UINT16 AccessorType; 315 316 317 ACPI_FUNCTION_TRACE_PTR (ExWriteSerialBus, ObjDesc); 318 319 320 /* 321 * This is an SMBus, GSBus or IPMI write. We will bypass the entire 322 * field mechanism and handoff the buffer directly to the handler. 323 * For these address spaces, the buffer is bidirectional; on a 324 * write, return data is returned in the same buffer. 325 * 326 * Source must be a buffer of sufficient size, these are fixed size: 327 * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. 328 * 329 * Note: SMBus and GSBus protocol type is passed in upper 16-bits 330 * of Function 331 * 332 * Common buffer format: 333 * Status; (Byte 0 of the data buffer) 334 * Length; (Byte 1 of the data buffer) 335 * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) 336 */ 337 if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER) 338 { 339 ACPI_ERROR ((AE_INFO, 340 "SMBus/IPMI/GenericSerialBus write requires " 341 "Buffer, found type %s", 342 AcpiUtGetObjectTypeName (SourceDesc))); 343 344 return_ACPI_STATUS (AE_AML_OPERAND_TYPE); 345 } 346 347 switch (ObjDesc->Field.RegionObj->Region.SpaceId) 348 { 349 case ACPI_ADR_SPACE_SMBUS: 350 351 BufferLength = ACPI_SMBUS_BUFFER_SIZE; 352 Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16); 353 break; 354 355 case ACPI_ADR_SPACE_IPMI: 356 357 BufferLength = ACPI_IPMI_BUFFER_SIZE; 358 Function = ACPI_WRITE; 359 break; 360 361 case ACPI_ADR_SPACE_GSBUS: 362 363 AccessorType = ObjDesc->Field.Attribute; 364 Status = AcpiExGetProtocolBufferLength (AccessorType, &BufferLength); 365 if (ACPI_FAILURE (Status)) 366 { 367 ACPI_ERROR ((AE_INFO, 368 "Invalid protocol ID for GSBus: 0x%4.4X", AccessorType)); 369 370 return_ACPI_STATUS (Status); 371 } 372 373 /* Add header length to get the full size of the buffer */ 374 375 BufferLength += ACPI_SERIAL_HEADER_SIZE; 376 Function = ACPI_WRITE | (AccessorType << 16); 377 break; 378 379 case ACPI_ADR_SPACE_PLATFORM_RT: 380 381 BufferLength = ACPI_PRM_INPUT_BUFFER_SIZE; 382 Function = ACPI_WRITE; 383 break; 384 385 case ACPI_ADR_SPACE_FIXED_HARDWARE: 386 387 BufferLength = ACPI_FFH_INPUT_BUFFER_SIZE; 388 Function = ACPI_WRITE; 389 break; 390 391 default: 392 return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID); 393 } 394 395 /* Create the transfer/bidirectional/return buffer */ 396 397 BufferDesc = AcpiUtCreateBufferObject (BufferLength); 398 if (!BufferDesc) 399 { 400 return_ACPI_STATUS (AE_NO_MEMORY); 401 } 402 403 /* Copy the input buffer data to the transfer buffer */ 404 405 Buffer = BufferDesc->Buffer.Pointer; 406 DataLength = ACPI_MIN (BufferLength, SourceDesc->Buffer.Length); 407 memcpy (Buffer, SourceDesc->Buffer.Pointer, DataLength); 408 409 /* Lock entire transaction if requested */ 410 411 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags); 412 413 /* 414 * Perform the write (returns status and perhaps data in the 415 * same buffer) 416 */ 417 Status = AcpiExAccessRegion ( 418 ObjDesc, 0, (UINT64 *) Buffer, Function); 419 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags); 420 421 *ReturnBuffer = BufferDesc; 422 return_ACPI_STATUS (Status); 423 } 424