xref: /openbmc/linux/drivers/acpi/acpica/exfield.c (revision 176f011b)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /******************************************************************************
3  *
4  * Module Name: exfield - AML execution - field_unit read/write
5  *
6  * Copyright (C) 2000 - 2018, 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("exfield")
18 
19 /*
20  * This table maps the various Attrib protocols to the byte transfer
21  * length. Used for the generic serial bus.
22  */
23 #define ACPI_INVALID_PROTOCOL_ID        0x80
24 #define ACPI_MAX_PROTOCOL_ID            0x0F
25 const u8 acpi_protocol_lengths[] = {
26 	ACPI_INVALID_PROTOCOL_ID,	/* 0 - reserved */
27 	ACPI_INVALID_PROTOCOL_ID,	/* 1 - reserved */
28 	0x00,			/* 2 - ATTRIB_QUICK */
29 	ACPI_INVALID_PROTOCOL_ID,	/* 3 - reserved */
30 	0x01,			/* 4 - ATTRIB_SEND_RECEIVE */
31 	ACPI_INVALID_PROTOCOL_ID,	/* 5 - reserved */
32 	0x01,			/* 6 - ATTRIB_BYTE */
33 	ACPI_INVALID_PROTOCOL_ID,	/* 7 - reserved */
34 	0x02,			/* 8 - ATTRIB_WORD */
35 	ACPI_INVALID_PROTOCOL_ID,	/* 9 - reserved */
36 	0xFF,			/* A - ATTRIB_BLOCK  */
37 	0xFF,			/* B - ATTRIB_BYTES */
38 	0x02,			/* C - ATTRIB_PROCESS_CALL */
39 	0xFF,			/* D - ATTRIB_BLOCK_PROCESS_CALL */
40 	0xFF,			/* E - ATTRIB_RAW_BYTES */
41 	0xFF			/* F - ATTRIB_RAW_PROCESS_BYTES */
42 };
43 
44 /*******************************************************************************
45  *
46  * FUNCTION:    acpi_ex_get_protocol_buffer_length
47  *
48  * PARAMETERS:  protocol_id     - The type of the protocol indicated by region
49  *                                field access attributes
50  *              return_length   - Where the protocol byte transfer length is
51  *                                returned
52  *
53  * RETURN:      Status and decoded byte transfer length
54  *
55  * DESCRIPTION: This routine returns the length of the generic_serial_bus
56  *              protocol bytes
57  *
58  ******************************************************************************/
59 
60 acpi_status
61 acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
62 {
63 
64 	if ((protocol_id > ACPI_MAX_PROTOCOL_ID) ||
65 	    (acpi_protocol_lengths[protocol_id] == ACPI_INVALID_PROTOCOL_ID)) {
66 		ACPI_ERROR((AE_INFO,
67 			    "Invalid Field/AccessAs protocol ID: 0x%4.4X",
68 			    protocol_id));
69 
70 		return (AE_AML_PROTOCOL);
71 	}
72 
73 	*return_length = acpi_protocol_lengths[protocol_id];
74 	return (AE_OK);
75 }
76 
77 /*******************************************************************************
78  *
79  * FUNCTION:    acpi_ex_read_data_from_field
80  *
81  * PARAMETERS:  walk_state          - Current execution state
82  *              obj_desc            - The named field
83  *              ret_buffer_desc     - Where the return data object is stored
84  *
85  * RETURN:      Status
86  *
87  * DESCRIPTION: Read from a named field. Returns either an Integer or a
88  *              Buffer, depending on the size of the field.
89  *
90  ******************************************************************************/
91 
92 acpi_status
93 acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
94 			     union acpi_operand_object *obj_desc,
95 			     union acpi_operand_object **ret_buffer_desc)
96 {
97 	acpi_status status;
98 	union acpi_operand_object *buffer_desc;
99 	void *buffer;
100 	u32 buffer_length;
101 
102 	ACPI_FUNCTION_TRACE_PTR(ex_read_data_from_field, obj_desc);
103 
104 	/* Parameter validation */
105 
106 	if (!obj_desc) {
107 		return_ACPI_STATUS(AE_AML_NO_OPERAND);
108 	}
109 	if (!ret_buffer_desc) {
110 		return_ACPI_STATUS(AE_BAD_PARAMETER);
111 	}
112 
113 	if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
114 		/*
115 		 * If the buffer_field arguments have not been previously evaluated,
116 		 * evaluate them now and save the results.
117 		 */
118 		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
119 			status = acpi_ds_get_buffer_field_arguments(obj_desc);
120 			if (ACPI_FAILURE(status)) {
121 				return_ACPI_STATUS(status);
122 			}
123 		}
124 	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
125 		   (obj_desc->field.region_obj->region.space_id ==
126 		    ACPI_ADR_SPACE_SMBUS
127 		    || obj_desc->field.region_obj->region.space_id ==
128 		    ACPI_ADR_SPACE_GSBUS
129 		    || obj_desc->field.region_obj->region.space_id ==
130 		    ACPI_ADR_SPACE_IPMI)) {
131 
132 		/* SMBus, GSBus, IPMI serial */
133 
134 		status = acpi_ex_read_serial_bus(obj_desc, ret_buffer_desc);
135 		return_ACPI_STATUS(status);
136 	}
137 
138 	/*
139 	 * Allocate a buffer for the contents of the field.
140 	 *
141 	 * If the field is larger than the current integer width, create
142 	 * a BUFFER to hold it. Otherwise, use an INTEGER. This allows
143 	 * the use of arithmetic operators on the returned value if the
144 	 * field size is equal or smaller than an Integer.
145 	 *
146 	 * Note: Field.length is in bits.
147 	 */
148 	buffer_length =
149 	    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
150 
151 	if (buffer_length > acpi_gbl_integer_byte_width) {
152 
153 		/* Field is too large for an Integer, create a Buffer instead */
154 
155 		buffer_desc = acpi_ut_create_buffer_object(buffer_length);
156 		if (!buffer_desc) {
157 			return_ACPI_STATUS(AE_NO_MEMORY);
158 		}
159 		buffer = buffer_desc->buffer.pointer;
160 	} else {
161 		/* Field will fit within an Integer (normal case) */
162 
163 		buffer_desc = acpi_ut_create_integer_object((u64) 0);
164 		if (!buffer_desc) {
165 			return_ACPI_STATUS(AE_NO_MEMORY);
166 		}
167 
168 		buffer_length = acpi_gbl_integer_byte_width;
169 		buffer = &buffer_desc->integer.value;
170 	}
171 
172 	if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
173 	    (obj_desc->field.region_obj->region.space_id ==
174 	     ACPI_ADR_SPACE_GPIO)) {
175 
176 		/* General Purpose I/O */
177 
178 		status = acpi_ex_read_gpio(obj_desc, buffer);
179 		goto exit;
180 	}
181 
182 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
183 			  "FieldRead [TO]:   Obj %p, Type %X, Buf %p, ByteLen %X\n",
184 			  obj_desc, obj_desc->common.type, buffer,
185 			  buffer_length));
186 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
187 			  "FieldRead [FROM]: BitLen %X, BitOff %X, ByteOff %X\n",
188 			  obj_desc->common_field.bit_length,
189 			  obj_desc->common_field.start_field_bit_offset,
190 			  obj_desc->common_field.base_byte_offset));
191 
192 	/* Lock entire transaction if requested */
193 
194 	acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
195 
196 	/* Read from the field */
197 
198 	status = acpi_ex_extract_from_field(obj_desc, buffer, buffer_length);
199 	acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
200 
201 exit:
202 	if (ACPI_FAILURE(status)) {
203 		acpi_ut_remove_reference(buffer_desc);
204 	} else {
205 		*ret_buffer_desc = buffer_desc;
206 	}
207 
208 	return_ACPI_STATUS(status);
209 }
210 
211 /*******************************************************************************
212  *
213  * FUNCTION:    acpi_ex_write_data_to_field
214  *
215  * PARAMETERS:  source_desc         - Contains data to write
216  *              obj_desc            - The named field
217  *              result_desc         - Where the return value is returned, if any
218  *
219  * RETURN:      Status
220  *
221  * DESCRIPTION: Write to a named field
222  *
223  ******************************************************************************/
224 
225 acpi_status
226 acpi_ex_write_data_to_field(union acpi_operand_object *source_desc,
227 			    union acpi_operand_object *obj_desc,
228 			    union acpi_operand_object **result_desc)
229 {
230 	acpi_status status;
231 	u32 buffer_length;
232 	void *buffer;
233 
234 	ACPI_FUNCTION_TRACE_PTR(ex_write_data_to_field, obj_desc);
235 
236 	/* Parameter validation */
237 
238 	if (!source_desc || !obj_desc) {
239 		return_ACPI_STATUS(AE_AML_NO_OPERAND);
240 	}
241 
242 	if (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD) {
243 		/*
244 		 * If the buffer_field arguments have not been previously evaluated,
245 		 * evaluate them now and save the results.
246 		 */
247 		if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) {
248 			status = acpi_ds_get_buffer_field_arguments(obj_desc);
249 			if (ACPI_FAILURE(status)) {
250 				return_ACPI_STATUS(status);
251 			}
252 		}
253 	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
254 		   (obj_desc->field.region_obj->region.space_id ==
255 		    ACPI_ADR_SPACE_GPIO)) {
256 
257 		/* General Purpose I/O */
258 
259 		status = acpi_ex_write_gpio(source_desc, obj_desc, result_desc);
260 		return_ACPI_STATUS(status);
261 	} else if ((obj_desc->common.type == ACPI_TYPE_LOCAL_REGION_FIELD) &&
262 		   (obj_desc->field.region_obj->region.space_id ==
263 		    ACPI_ADR_SPACE_SMBUS
264 		    || obj_desc->field.region_obj->region.space_id ==
265 		    ACPI_ADR_SPACE_GSBUS
266 		    || obj_desc->field.region_obj->region.space_id ==
267 		    ACPI_ADR_SPACE_IPMI)) {
268 
269 		/* SMBus, GSBus, IPMI serial */
270 
271 		status =
272 		    acpi_ex_write_serial_bus(source_desc, obj_desc,
273 					     result_desc);
274 		return_ACPI_STATUS(status);
275 	}
276 
277 	/* Get a pointer to the data to be written */
278 
279 	switch (source_desc->common.type) {
280 	case ACPI_TYPE_INTEGER:
281 
282 		buffer = &source_desc->integer.value;
283 		buffer_length = sizeof(source_desc->integer.value);
284 		break;
285 
286 	case ACPI_TYPE_BUFFER:
287 
288 		buffer = source_desc->buffer.pointer;
289 		buffer_length = source_desc->buffer.length;
290 		break;
291 
292 	case ACPI_TYPE_STRING:
293 
294 		buffer = source_desc->string.pointer;
295 		buffer_length = source_desc->string.length;
296 		break;
297 
298 	default:
299 		return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
300 	}
301 
302 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
303 			  "FieldWrite [FROM]: Obj %p (%s:%X), Buf %p, ByteLen %X\n",
304 			  source_desc,
305 			  acpi_ut_get_type_name(source_desc->common.type),
306 			  source_desc->common.type, buffer, buffer_length));
307 
308 	ACPI_DEBUG_PRINT((ACPI_DB_BFIELD,
309 			  "FieldWrite [TO]:   Obj %p (%s:%X), BitLen %X, BitOff %X, ByteOff %X\n",
310 			  obj_desc,
311 			  acpi_ut_get_type_name(obj_desc->common.type),
312 			  obj_desc->common.type,
313 			  obj_desc->common_field.bit_length,
314 			  obj_desc->common_field.start_field_bit_offset,
315 			  obj_desc->common_field.base_byte_offset));
316 
317 	/* Lock entire transaction if requested */
318 
319 	acpi_ex_acquire_global_lock(obj_desc->common_field.field_flags);
320 
321 	/* Write to the field */
322 
323 	status = acpi_ex_insert_into_field(obj_desc, buffer, buffer_length);
324 	acpi_ex_release_global_lock(obj_desc->common_field.field_flags);
325 	return_ACPI_STATUS(status);
326 }
327