1 /******************************************************************************
2 *
3 * Module Name: exserial - FieldUnit support for serial address spaces
4 *
5 *****************************************************************************/
6
7 /*
8 * Copyright (C) 2000 - 2023, 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
AcpiExReadGpio(ACPI_OPERAND_OBJECT * ObjDesc,void * Buffer)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
AcpiExWriteGpio(ACPI_OPERAND_OBJECT * SourceDesc,ACPI_OPERAND_OBJECT * ObjDesc,ACPI_OPERAND_OBJECT ** ReturnBuffer)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
AcpiExReadSerialBus(ACPI_OPERAND_OBJECT * ObjDesc,ACPI_OPERAND_OBJECT ** ReturnBuffer)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 default:
255 return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
256 }
257
258 /* Create the local transfer buffer that is returned to the caller */
259
260 BufferDesc = AcpiUtCreateBufferObject (BufferLength);
261 if (!BufferDesc)
262 {
263 return_ACPI_STATUS (AE_NO_MEMORY);
264 }
265
266 /* Lock entire transaction if requested */
267
268 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
269
270 /* Call the region handler for the write-then-read */
271
272 Status = AcpiExAccessRegion (ObjDesc, 0,
273 ACPI_CAST_PTR (UINT64, BufferDesc->Buffer.Pointer), Function);
274 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
275
276 *ReturnBuffer = BufferDesc;
277 return_ACPI_STATUS (Status);
278 }
279
280
281 /*******************************************************************************
282 *
283 * FUNCTION: AcpiExWriteSerialBus
284 *
285 * PARAMETERS: SourceDesc - Contains data to write
286 * ObjDesc - The named field
287 * ReturnBuffer - Where the return value is returned, if any
288 *
289 * RETURN: Status
290 *
291 * DESCRIPTION: Write to a named field that references a serial bus
292 * (SMBus, IPMI, GSBus).
293 *
294 ******************************************************************************/
295
296 ACPI_STATUS
AcpiExWriteSerialBus(ACPI_OPERAND_OBJECT * SourceDesc,ACPI_OPERAND_OBJECT * ObjDesc,ACPI_OPERAND_OBJECT ** ReturnBuffer)297 AcpiExWriteSerialBus (
298 ACPI_OPERAND_OBJECT *SourceDesc,
299 ACPI_OPERAND_OBJECT *ObjDesc,
300 ACPI_OPERAND_OBJECT **ReturnBuffer)
301 {
302 ACPI_STATUS Status;
303 UINT32 BufferLength;
304 UINT32 DataLength;
305 void *Buffer;
306 ACPI_OPERAND_OBJECT *BufferDesc;
307 UINT32 Function;
308 UINT16 AccessorType;
309
310
311 ACPI_FUNCTION_TRACE_PTR (ExWriteSerialBus, ObjDesc);
312
313
314 /*
315 * This is an SMBus, GSBus or IPMI write. We will bypass the entire
316 * field mechanism and handoff the buffer directly to the handler.
317 * For these address spaces, the buffer is bidirectional; on a
318 * write, return data is returned in the same buffer.
319 *
320 * Source must be a buffer of sufficient size, these are fixed size:
321 * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE.
322 *
323 * Note: SMBus and GSBus protocol type is passed in upper 16-bits
324 * of Function
325 *
326 * Common buffer format:
327 * Status; (Byte 0 of the data buffer)
328 * Length; (Byte 1 of the data buffer)
329 * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer)
330 */
331 if (SourceDesc->Common.Type != ACPI_TYPE_BUFFER)
332 {
333 ACPI_ERROR ((AE_INFO,
334 "SMBus/IPMI/GenericSerialBus write requires "
335 "Buffer, found type %s",
336 AcpiUtGetObjectTypeName (SourceDesc)));
337
338 return_ACPI_STATUS (AE_AML_OPERAND_TYPE);
339 }
340
341 switch (ObjDesc->Field.RegionObj->Region.SpaceId)
342 {
343 case ACPI_ADR_SPACE_SMBUS:
344
345 BufferLength = ACPI_SMBUS_BUFFER_SIZE;
346 Function = ACPI_WRITE | (ObjDesc->Field.Attribute << 16);
347 break;
348
349 case ACPI_ADR_SPACE_IPMI:
350
351 BufferLength = ACPI_IPMI_BUFFER_SIZE;
352 Function = ACPI_WRITE;
353 break;
354
355 case ACPI_ADR_SPACE_GSBUS:
356
357 AccessorType = ObjDesc->Field.Attribute;
358 Status = AcpiExGetProtocolBufferLength (AccessorType, &BufferLength);
359 if (ACPI_FAILURE (Status))
360 {
361 ACPI_ERROR ((AE_INFO,
362 "Invalid protocol ID for GSBus: 0x%4.4X", AccessorType));
363
364 return_ACPI_STATUS (Status);
365 }
366
367 /* Add header length to get the full size of the buffer */
368
369 BufferLength += ACPI_SERIAL_HEADER_SIZE;
370 Function = ACPI_WRITE | (AccessorType << 16);
371 break;
372
373 case ACPI_ADR_SPACE_PLATFORM_RT:
374
375 BufferLength = ACPI_PRM_INPUT_BUFFER_SIZE;
376 Function = ACPI_WRITE;
377 break;
378
379 case ACPI_ADR_SPACE_FIXED_HARDWARE:
380
381 BufferLength = ACPI_FFH_INPUT_BUFFER_SIZE;
382 Function = ACPI_WRITE;
383 break;
384
385 default:
386 return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID);
387 }
388
389 /* Create the transfer/bidirectional/return buffer */
390
391 BufferDesc = AcpiUtCreateBufferObject (BufferLength);
392 if (!BufferDesc)
393 {
394 return_ACPI_STATUS (AE_NO_MEMORY);
395 }
396
397 /* Copy the input buffer data to the transfer buffer */
398
399 Buffer = BufferDesc->Buffer.Pointer;
400 DataLength = ACPI_MIN (BufferLength, SourceDesc->Buffer.Length);
401 memcpy (Buffer, SourceDesc->Buffer.Pointer, DataLength);
402
403 /* Lock entire transaction if requested */
404
405 AcpiExAcquireGlobalLock (ObjDesc->CommonField.FieldFlags);
406
407 /*
408 * Perform the write (returns status and perhaps data in the
409 * same buffer)
410 */
411 Status = AcpiExAccessRegion (
412 ObjDesc, 0, (UINT64 *) Buffer, Function);
413 AcpiExReleaseGlobalLock (ObjDesc->CommonField.FieldFlags);
414
415 *ReturnBuffer = BufferDesc;
416 return_ACPI_STATUS (Status);
417 }
418