1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Core driver model support for ACPI table generation
4  *
5  * Copyright 2019 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #define LOG_CATEOGRY	LOGC_ACPI
10 
11 #include <common.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <malloc.h>
15 #include <acpi/acpi_device.h>
16 #include <dm/acpi.h>
17 #include <dm/device-internal.h>
18 #include <dm/root.h>
19 
20 #define MAX_ACPI_ITEMS	100
21 
22 /* Type of table that we collected */
23 enum gen_type_t {
24 	TYPE_NONE,
25 	TYPE_SSDT,
26 	TYPE_DSDT,
27 };
28 
29 /* Type of method to call */
30 enum method_t {
31 	METHOD_WRITE_TABLES,
32 	METHOD_FILL_SSDT,
33 	METHOD_INJECT_DSDT,
34 	METHOD_SETUP_NHLT,
35 };
36 
37 /* Prototype for all methods */
38 typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
39 
40 /**
41  * struct acpi_item - Holds info about ACPI data generated by a driver method
42  *
43  * @dev: Device that generated this data
44  * @type: Table type it refers to
45  * @buf: Buffer containing the data
46  * @size: Size of the data in bytes
47  */
48 struct acpi_item {
49 	struct udevice *dev;
50 	enum gen_type_t type;
51 	char *buf;
52 	int size;
53 };
54 
55 /* List of ACPI items collected */
56 static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
57 static int item_count;
58 
acpi_copy_name(char * out_name,const char * name)59 int acpi_copy_name(char *out_name, const char *name)
60 {
61 	strncpy(out_name, name, ACPI_NAME_LEN);
62 	out_name[ACPI_NAME_LEN] = '\0';
63 
64 	return 0;
65 }
66 
acpi_get_name(const struct udevice * dev,char * out_name)67 int acpi_get_name(const struct udevice *dev, char *out_name)
68 {
69 	struct acpi_ops *aops;
70 	const char *name;
71 	int ret;
72 
73 	aops = device_get_acpi_ops(dev);
74 	if (aops && aops->get_name)
75 		return aops->get_name(dev, out_name);
76 	name = dev_read_string(dev, "acpi,name");
77 	if (name)
78 		return acpi_copy_name(out_name, name);
79 	ret = acpi_device_infer_name(dev, out_name);
80 	if (ret)
81 		return log_msg_ret("dev", ret);
82 
83 	return 0;
84 }
85 
acpi_get_path(const struct udevice * dev,char * out_path,int maxlen)86 int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
87 {
88 	const char *path;
89 	int ret;
90 
91 	path = dev_read_string(dev, "acpi,path");
92 	if (path) {
93 		if (strlen(path) >= maxlen)
94 			return -E2BIG;
95 		strcpy(out_path, path);
96 		return 0;
97 	}
98 	ret = acpi_device_path(dev, out_path, maxlen);
99 	if (ret)
100 		return log_msg_ret("dev", ret);
101 
102 	return 0;
103 }
104 
105 /**
106  * acpi_add_item() - Add a new item to the list of data collected
107  *
108  * @ctx: ACPI context
109  * @dev: Device that generated the data
110  * @type: Table type it refers to
111  * @start: The start of the data (the end is obtained from ctx->current)
112  * @return 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
113  */
acpi_add_item(struct acpi_ctx * ctx,struct udevice * dev,enum gen_type_t type,void * start)114 static int acpi_add_item(struct acpi_ctx *ctx, struct udevice *dev,
115 			 enum gen_type_t type, void *start)
116 {
117 	struct acpi_item *item;
118 	void *end = ctx->current;
119 
120 	if (item_count == MAX_ACPI_ITEMS) {
121 		log_err("Too many items\n");
122 		return log_msg_ret("mem", -ENOSPC);
123 	}
124 
125 	item = &acpi_item[item_count];
126 	item->dev = dev;
127 	item->type = type;
128 	item->size = end - start;
129 	if (!item->size)
130 		return 0;
131 	item->buf = malloc(item->size);
132 	if (!item->buf)
133 		return log_msg_ret("mem", -ENOMEM);
134 	memcpy(item->buf, start, item->size);
135 	item_count++;
136 	log_debug("* %s: Added type %d, %p, size %x\n", dev->name, type, start,
137 		  item->size);
138 
139 	return 0;
140 }
141 
acpi_dump_items(enum acpi_dump_option option)142 void acpi_dump_items(enum acpi_dump_option option)
143 {
144 	int i;
145 
146 	for (i = 0; i < item_count; i++) {
147 		struct acpi_item *item = &acpi_item[i];
148 
149 		printf("dev '%s', type %d, size %x\n", item->dev->name,
150 		       item->type, item->size);
151 		if (option == ACPI_DUMP_CONTENTS) {
152 			print_buffer(0, item->buf, 1, item->size, 0);
153 			printf("\n");
154 		}
155 	}
156 }
157 
find_acpi_item(const char * devname)158 static struct acpi_item *find_acpi_item(const char *devname)
159 {
160 	int i;
161 
162 	for (i = 0; i < item_count; i++) {
163 		struct acpi_item *item = &acpi_item[i];
164 
165 		if (!strcmp(devname, item->dev->name))
166 			return item;
167 	}
168 
169 	return NULL;
170 }
171 
172 /**
173  * sort_acpi_item_type - Sort the ACPI items into the desired order
174  *
175  * This looks up the ordering in the device tree and then adds each item one by
176  * one into the supplied buffer
177  *
178  * @ctx: ACPI context
179  * @start: Start position to put the sorted items. The items will follow each
180  *	other in sorted order
181  * @type: Type of items to sort
182  * @return 0 if OK, -ve on error
183  */
sort_acpi_item_type(struct acpi_ctx * ctx,void * start,enum gen_type_t type)184 static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
185 			       enum gen_type_t type)
186 {
187 	const u32 *order;
188 	int size;
189 	int count;
190 	void *ptr;
191 	void *end = ctx->current;
192 
193 	ptr = start;
194 	order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
195 					"u-boot,acpi-dsdt-order" :
196 					"u-boot,acpi-ssdt-order", &size);
197 	if (!order) {
198 		log_debug("Failed to find ordering, leaving as is\n");
199 		return 0;
200 	}
201 
202 	/*
203 	 * This algorithm rewrites the context buffer without changing its
204 	 * length. So there is no need to update ctx-current
205 	 */
206 	count = size / sizeof(u32);
207 	while (count--) {
208 		struct acpi_item *item;
209 		const char *name;
210 		ofnode node;
211 
212 		node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
213 		name = ofnode_get_name(node);
214 		item = find_acpi_item(name);
215 		if (!item) {
216 			log_err("Failed to find item '%s'\n", name);
217 			return log_msg_ret("find", -ENOENT);
218 		}
219 		if (item->type == type) {
220 			log_debug("   - add %s\n", item->dev->name);
221 			memcpy(ptr, item->buf, item->size);
222 			ptr += item->size;
223 		}
224 	}
225 
226 	/*
227 	 * If the sort order is missing an item then the output will be too
228 	 * small. Report this error since the item needs to be added to the
229 	 * ordering for the ACPI tables to be complete.
230 	 */
231 	if (ptr != end) {
232 		log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
233 		return -ENXIO;
234 	}
235 
236 	return 0;
237 }
238 
acpi_get_method(struct udevice * dev,enum method_t method)239 acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
240 {
241 	struct acpi_ops *aops;
242 
243 	aops = device_get_acpi_ops(dev);
244 	if (aops) {
245 		switch (method) {
246 		case METHOD_WRITE_TABLES:
247 			return aops->write_tables;
248 		case METHOD_FILL_SSDT:
249 			return aops->fill_ssdt;
250 		case METHOD_INJECT_DSDT:
251 			return aops->inject_dsdt;
252 		case METHOD_SETUP_NHLT:
253 			return aops->setup_nhlt;
254 		}
255 	}
256 
257 	return NULL;
258 }
259 
acpi_recurse_method(struct acpi_ctx * ctx,struct udevice * parent,enum method_t method,enum gen_type_t type)260 int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
261 			enum method_t method, enum gen_type_t type)
262 {
263 	struct udevice *dev;
264 	acpi_method func;
265 	int ret;
266 
267 	func = acpi_get_method(parent, method);
268 	if (func) {
269 		void *start = ctx->current;
270 
271 		log_debug("- method %d, %s %p\n", method, parent->name, func);
272 		ret = device_of_to_plat(parent);
273 		if (ret)
274 			return log_msg_ret("ofdata", ret);
275 		ret = func(parent, ctx);
276 		if (ret)
277 			return log_msg_ret("func", ret);
278 
279 		/* Add the item to the internal list */
280 		if (type != TYPE_NONE) {
281 			ret = acpi_add_item(ctx, parent, type, start);
282 			if (ret)
283 				return log_msg_ret("add", ret);
284 		}
285 	}
286 	device_foreach_child(dev, parent) {
287 		ret = acpi_recurse_method(ctx, dev, method, type);
288 		if (ret)
289 			return log_msg_ret("recurse", ret);
290 	}
291 
292 	return 0;
293 }
294 
acpi_fill_ssdt(struct acpi_ctx * ctx)295 int acpi_fill_ssdt(struct acpi_ctx *ctx)
296 {
297 	void *start = ctx->current;
298 	int ret;
299 
300 	log_debug("Writing SSDT tables\n");
301 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
302 	log_debug("Writing SSDT finished, err=%d\n", ret);
303 	ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
304 	if (ret)
305 		return log_msg_ret("build", ret);
306 
307 	return ret;
308 }
309 
acpi_inject_dsdt(struct acpi_ctx * ctx)310 int acpi_inject_dsdt(struct acpi_ctx *ctx)
311 {
312 	void *start = ctx->current;
313 	int ret;
314 
315 	log_debug("Writing DSDT tables\n");
316 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
317 				  TYPE_DSDT);
318 	log_debug("Writing DSDT finished, err=%d\n", ret);
319 	ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
320 	if (ret)
321 		return log_msg_ret("build", ret);
322 
323 	return ret;
324 }
325 
acpi_reset_items(void)326 void acpi_reset_items(void)
327 {
328 	item_count = 0;
329 }
330 
acpi_write_dev_tables(struct acpi_ctx * ctx)331 int acpi_write_dev_tables(struct acpi_ctx *ctx)
332 {
333 	int ret;
334 
335 	log_debug("Writing device tables\n");
336 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
337 				  TYPE_NONE);
338 	log_debug("Writing finished, err=%d\n", ret);
339 
340 	return ret;
341 }
342 
acpi_setup_nhlt(struct acpi_ctx * ctx,struct nhlt * nhlt)343 int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
344 {
345 	int ret;
346 
347 	log_debug("Setup NHLT\n");
348 	ctx->nhlt = nhlt;
349 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
350 	log_debug("Setup finished, err=%d\n", ret);
351 
352 	return ret;
353 }
354