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