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 - 2023, 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 | default: |
205 | return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); |
206 | } |
207 | |
208 | /* Create the local transfer buffer that is returned to the caller */ |
209 | |
210 | buffer_desc = acpi_ut_create_buffer_object(buffer_size: buffer_length); |
211 | if (!buffer_desc) { |
212 | return_ACPI_STATUS(AE_NO_MEMORY); |
213 | } |
214 | |
215 | /* Lock entire transaction if requested */ |
216 | |
217 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); |
218 | |
219 | /* Call the region handler for the write-then-read */ |
220 | |
221 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, |
222 | ACPI_CAST_PTR(u64, |
223 | buffer_desc->buffer. |
224 | pointer), read_write: function); |
225 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); |
226 | |
227 | *return_buffer = buffer_desc; |
228 | return_ACPI_STATUS(status); |
229 | } |
230 | |
231 | /******************************************************************************* |
232 | * |
233 | * FUNCTION: acpi_ex_write_serial_bus |
234 | * |
235 | * PARAMETERS: source_desc - Contains data to write |
236 | * obj_desc - The named field |
237 | * return_buffer - Where the return value is returned, if any |
238 | * |
239 | * RETURN: Status |
240 | * |
241 | * DESCRIPTION: Write to a named field that references a serial bus |
242 | * (SMBus, IPMI, GSBus). |
243 | * |
244 | ******************************************************************************/ |
245 | |
246 | acpi_status |
247 | acpi_ex_write_serial_bus(union acpi_operand_object *source_desc, |
248 | union acpi_operand_object *obj_desc, |
249 | union acpi_operand_object **return_buffer) |
250 | { |
251 | acpi_status status; |
252 | u32 buffer_length; |
253 | u32 data_length; |
254 | void *buffer; |
255 | union acpi_operand_object *buffer_desc; |
256 | u32 function; |
257 | u16 accessor_type; |
258 | |
259 | ACPI_FUNCTION_TRACE_PTR(ex_write_serial_bus, obj_desc); |
260 | |
261 | /* |
262 | * This is an SMBus, GSBus or IPMI write. We will bypass the entire |
263 | * field mechanism and handoff the buffer directly to the handler. |
264 | * For these address spaces, the buffer is bidirectional; on a |
265 | * write, return data is returned in the same buffer. |
266 | * |
267 | * Source must be a buffer of sufficient size, these are fixed size: |
268 | * ACPI_SMBUS_BUFFER_SIZE, or ACPI_IPMI_BUFFER_SIZE. |
269 | * |
270 | * Note: SMBus and GSBus protocol type is passed in upper 16-bits |
271 | * of Function |
272 | * |
273 | * Common buffer format: |
274 | * Status; (Byte 0 of the data buffer) |
275 | * Length; (Byte 1 of the data buffer) |
276 | * Data[x-1]: (Bytes 2-x of the arbitrary length data buffer) |
277 | */ |
278 | if (source_desc->common.type != ACPI_TYPE_BUFFER) { |
279 | ACPI_ERROR((AE_INFO, |
280 | "SMBus/IPMI/GenericSerialBus write requires " |
281 | "Buffer, found type %s" , |
282 | acpi_ut_get_object_type_name(source_desc))); |
283 | |
284 | return_ACPI_STATUS(AE_AML_OPERAND_TYPE); |
285 | } |
286 | |
287 | switch (obj_desc->field.region_obj->region.space_id) { |
288 | case ACPI_ADR_SPACE_SMBUS: |
289 | |
290 | buffer_length = ACPI_SMBUS_BUFFER_SIZE; |
291 | function = ACPI_WRITE | (obj_desc->field.attribute << 16); |
292 | break; |
293 | |
294 | case ACPI_ADR_SPACE_IPMI: |
295 | |
296 | buffer_length = ACPI_IPMI_BUFFER_SIZE; |
297 | function = ACPI_WRITE; |
298 | break; |
299 | |
300 | case ACPI_ADR_SPACE_GSBUS: |
301 | |
302 | accessor_type = obj_desc->field.attribute; |
303 | status = |
304 | acpi_ex_get_protocol_buffer_length(protocol_id: accessor_type, |
305 | return_length: &buffer_length); |
306 | if (ACPI_FAILURE(status)) { |
307 | ACPI_ERROR((AE_INFO, |
308 | "Invalid protocol ID for GSBus: 0x%4.4X" , |
309 | accessor_type)); |
310 | |
311 | return_ACPI_STATUS(status); |
312 | } |
313 | |
314 | /* Add header length to get the full size of the buffer */ |
315 | |
316 | buffer_length += ACPI_SERIAL_HEADER_SIZE; |
317 | function = ACPI_WRITE | (accessor_type << 16); |
318 | break; |
319 | |
320 | case ACPI_ADR_SPACE_PLATFORM_RT: |
321 | |
322 | buffer_length = ACPI_PRM_INPUT_BUFFER_SIZE; |
323 | function = ACPI_WRITE; |
324 | break; |
325 | |
326 | case ACPI_ADR_SPACE_FIXED_HARDWARE: |
327 | |
328 | buffer_length = ACPI_FFH_INPUT_BUFFER_SIZE; |
329 | function = ACPI_WRITE; |
330 | break; |
331 | |
332 | default: |
333 | return_ACPI_STATUS(AE_AML_INVALID_SPACE_ID); |
334 | } |
335 | |
336 | /* Create the transfer/bidirectional/return buffer */ |
337 | |
338 | buffer_desc = acpi_ut_create_buffer_object(buffer_size: buffer_length); |
339 | if (!buffer_desc) { |
340 | return_ACPI_STATUS(AE_NO_MEMORY); |
341 | } |
342 | |
343 | /* Copy the input buffer data to the transfer buffer */ |
344 | |
345 | buffer = buffer_desc->buffer.pointer; |
346 | data_length = ACPI_MIN(buffer_length, source_desc->buffer.length); |
347 | memcpy(buffer, source_desc->buffer.pointer, data_length); |
348 | |
349 | /* Lock entire transaction if requested */ |
350 | |
351 | acpi_ex_acquire_global_lock(rule: obj_desc->common_field.field_flags); |
352 | |
353 | /* |
354 | * Perform the write (returns status and perhaps data in the |
355 | * same buffer) |
356 | */ |
357 | status = acpi_ex_access_region(obj_desc, field_datum_byte_offset: 0, value: (u64 *)buffer, read_write: function); |
358 | acpi_ex_release_global_lock(rule: obj_desc->common_field.field_flags); |
359 | |
360 | *return_buffer = buffer_desc; |
361 | return_ACPI_STATUS(status); |
362 | } |
363 | |