162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This file does the necessary interface mapping between the bootwrapper
462306a36Sopenharmony_ci * device tree operations and the interface provided by shared source
562306a36Sopenharmony_ci * files flatdevicetree.[ch].
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2007 David Gibson, IBM Corporation.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <stddef.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <page.h>
1362306a36Sopenharmony_ci#include <libfdt.h>
1462306a36Sopenharmony_ci#include "ops.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define DEBUG	0
1762306a36Sopenharmony_ci#define BAD_ERROR(err)	(((err) < 0) \
1862306a36Sopenharmony_ci			 && ((err) != -FDT_ERR_NOTFOUND) \
1962306a36Sopenharmony_ci			 && ((err) != -FDT_ERR_EXISTS))
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define check_err(err) \
2262306a36Sopenharmony_ci	({ \
2362306a36Sopenharmony_ci		if (BAD_ERROR(err) || ((err < 0) && DEBUG)) \
2462306a36Sopenharmony_ci			printf("%s():%d  %s\n\r", __func__, __LINE__, \
2562306a36Sopenharmony_ci			       fdt_strerror(err)); \
2662306a36Sopenharmony_ci		if (BAD_ERROR(err)) \
2762306a36Sopenharmony_ci			exit(); \
2862306a36Sopenharmony_ci		(err < 0) ? -1 : 0; \
2962306a36Sopenharmony_ci	})
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define offset_devp(off)	\
3262306a36Sopenharmony_ci	({ \
3362306a36Sopenharmony_ci		unsigned long _offset = (off); \
3462306a36Sopenharmony_ci		check_err(_offset) ? NULL : (void *)(_offset+1); \
3562306a36Sopenharmony_ci	})
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define devp_offset_find(devp)	(((unsigned long)(devp))-1)
3862306a36Sopenharmony_ci#define devp_offset(devp)	(devp ? ((unsigned long)(devp))-1 : 0)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void *fdt;
4162306a36Sopenharmony_cistatic void *buf; /* = NULL */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define EXPAND_GRANULARITY	1024
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void expand_buf(int minexpand)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int size = fdt_totalsize(fdt);
4862306a36Sopenharmony_ci	int rc;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	size = _ALIGN(size + minexpand, EXPAND_GRANULARITY);
5162306a36Sopenharmony_ci	buf = platform_ops.realloc(buf, size);
5262306a36Sopenharmony_ci	if (!buf)
5362306a36Sopenharmony_ci		fatal("Couldn't find %d bytes to expand device tree\n\r", size);
5462306a36Sopenharmony_ci	rc = fdt_open_into(fdt, buf, size);
5562306a36Sopenharmony_ci	if (rc != 0)
5662306a36Sopenharmony_ci		fatal("Couldn't expand fdt into new buffer: %s\n\r",
5762306a36Sopenharmony_ci		      fdt_strerror(rc));
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	fdt = buf;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void *fdt_wrapper_finddevice(const char *path)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	return offset_devp(fdt_path_offset(fdt, path));
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int fdt_wrapper_getprop(const void *devp, const char *name,
6862306a36Sopenharmony_ci			       void *buf, const int buflen)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	const void *p;
7162306a36Sopenharmony_ci	int len;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	p = fdt_getprop(fdt, devp_offset(devp), name, &len);
7462306a36Sopenharmony_ci	if (!p)
7562306a36Sopenharmony_ci		return check_err(len);
7662306a36Sopenharmony_ci	memcpy(buf, p, min(len, buflen));
7762306a36Sopenharmony_ci	return len;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int fdt_wrapper_setprop(const void *devp, const char *name,
8162306a36Sopenharmony_ci			       const void *buf, const int len)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int rc;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len);
8662306a36Sopenharmony_ci	if (rc == -FDT_ERR_NOSPACE) {
8762306a36Sopenharmony_ci		expand_buf(len + 16);
8862306a36Sopenharmony_ci		rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return check_err(rc);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int fdt_wrapper_del_node(const void *devp)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return fdt_del_node(fdt, devp_offset(devp));
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void *fdt_wrapper_get_parent(const void *devp)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return offset_devp(fdt_parent_offset(fdt, devp_offset(devp)));
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void *fdt_wrapper_create_node(const void *devp, const char *name)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int offset;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	offset = fdt_add_subnode(fdt, devp_offset(devp), name);
10962306a36Sopenharmony_ci	if (offset == -FDT_ERR_NOSPACE) {
11062306a36Sopenharmony_ci		expand_buf(strlen(name) + 16);
11162306a36Sopenharmony_ci		offset = fdt_add_subnode(fdt, devp_offset(devp), name);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return offset_devp(offset);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void *fdt_wrapper_find_node_by_prop_value(const void *prev,
11862306a36Sopenharmony_ci						 const char *name,
11962306a36Sopenharmony_ci						 const char *val,
12062306a36Sopenharmony_ci						 int len)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int offset = fdt_node_offset_by_prop_value(fdt, devp_offset_find(prev),
12362306a36Sopenharmony_ci	                                           name, val, len);
12462306a36Sopenharmony_ci	return offset_devp(offset);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void *fdt_wrapper_find_node_by_compatible(const void *prev,
12862306a36Sopenharmony_ci						 const char *val)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	int offset = fdt_node_offset_by_compatible(fdt, devp_offset_find(prev),
13162306a36Sopenharmony_ci	                                           val);
13262306a36Sopenharmony_ci	return offset_devp(offset);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic char *fdt_wrapper_get_path(const void *devp, char *buf, int len)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	int rc;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	rc = fdt_get_path(fdt, devp_offset(devp), buf, len);
14062306a36Sopenharmony_ci	if (check_err(rc))
14162306a36Sopenharmony_ci		return NULL;
14262306a36Sopenharmony_ci	return buf;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic unsigned long fdt_wrapper_finalize(void)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int rc;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	rc = fdt_pack(fdt);
15062306a36Sopenharmony_ci	if (rc != 0)
15162306a36Sopenharmony_ci		fatal("Couldn't pack flat tree: %s\n\r",
15262306a36Sopenharmony_ci		      fdt_strerror(rc));
15362306a36Sopenharmony_ci	return (unsigned long)fdt;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_civoid fdt_init(void *blob)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int err;
15962306a36Sopenharmony_ci	int bufsize;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dt_ops.finddevice = fdt_wrapper_finddevice;
16262306a36Sopenharmony_ci	dt_ops.getprop = fdt_wrapper_getprop;
16362306a36Sopenharmony_ci	dt_ops.setprop = fdt_wrapper_setprop;
16462306a36Sopenharmony_ci	dt_ops.get_parent = fdt_wrapper_get_parent;
16562306a36Sopenharmony_ci	dt_ops.create_node = fdt_wrapper_create_node;
16662306a36Sopenharmony_ci	dt_ops.find_node_by_prop_value = fdt_wrapper_find_node_by_prop_value;
16762306a36Sopenharmony_ci	dt_ops.find_node_by_compatible = fdt_wrapper_find_node_by_compatible;
16862306a36Sopenharmony_ci	dt_ops.del_node = fdt_wrapper_del_node;
16962306a36Sopenharmony_ci	dt_ops.get_path = fdt_wrapper_get_path;
17062306a36Sopenharmony_ci	dt_ops.finalize = fdt_wrapper_finalize;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Make sure the dt blob is the right version and so forth */
17362306a36Sopenharmony_ci	fdt = blob;
17462306a36Sopenharmony_ci	bufsize = fdt_totalsize(fdt) + EXPAND_GRANULARITY;
17562306a36Sopenharmony_ci	buf = malloc(bufsize);
17662306a36Sopenharmony_ci	if(!buf)
17762306a36Sopenharmony_ci		fatal("malloc failed. can't relocate the device tree\n\r");
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	err = fdt_open_into(fdt, buf, bufsize);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (err != 0)
18262306a36Sopenharmony_ci		fatal("fdt_init(): %s\n\r", fdt_strerror(err));
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	fdt = buf;
18562306a36Sopenharmony_ci}
186