xref: /kvm-unit-tests/lib/devicetree.c (revision 184074189f14c36cb4bccbe67b30178480a4f21a)
1f7f76b85SAndrew Jones /*
2f7f76b85SAndrew Jones  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
3f7f76b85SAndrew Jones  *
4f7f76b85SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
5f7f76b85SAndrew Jones  */
6f7f76b85SAndrew Jones #include "libcflat.h"
7f7f76b85SAndrew Jones #include "libfdt/libfdt.h"
8f7f76b85SAndrew Jones #include "devicetree.h"
9f7f76b85SAndrew Jones 
10f7f76b85SAndrew Jones static const void *fdt;
11f7f76b85SAndrew Jones 
12f7f76b85SAndrew Jones const void *dt_fdt(void)
13f7f76b85SAndrew Jones {
14f7f76b85SAndrew Jones 	return fdt;
15f7f76b85SAndrew Jones }
16f7f76b85SAndrew Jones 
17f7f76b85SAndrew Jones bool dt_available(void)
18f7f76b85SAndrew Jones {
19f7f76b85SAndrew Jones 	return fdt_check_header(fdt) == 0;
20f7f76b85SAndrew Jones }
21f7f76b85SAndrew Jones 
22f7f76b85SAndrew Jones int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
23f7f76b85SAndrew Jones {
24f7f76b85SAndrew Jones 	const struct fdt_property *prop;
25f7f76b85SAndrew Jones 	u32 *nr_cells;
26f7f76b85SAndrew Jones 	int len;
27f7f76b85SAndrew Jones 
28f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
29f7f76b85SAndrew Jones 	if (prop == NULL)
30f7f76b85SAndrew Jones 		return len;
31f7f76b85SAndrew Jones 
32f7f76b85SAndrew Jones 	nr_cells = (u32 *)prop->data;
33f7f76b85SAndrew Jones 	*nr_address_cells = fdt32_to_cpu(*nr_cells);
34f7f76b85SAndrew Jones 
35f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
36f7f76b85SAndrew Jones 	if (prop == NULL)
37f7f76b85SAndrew Jones 		return len;
38f7f76b85SAndrew Jones 
39f7f76b85SAndrew Jones 	nr_cells = (u32 *)prop->data;
40f7f76b85SAndrew Jones 	*nr_size_cells = fdt32_to_cpu(*nr_cells);
41f7f76b85SAndrew Jones 
42f7f76b85SAndrew Jones 	return 0;
43f7f76b85SAndrew Jones }
44f7f76b85SAndrew Jones 
45f7f76b85SAndrew Jones void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
46f7f76b85SAndrew Jones {
47f7f76b85SAndrew Jones 	memset(reg, 0, sizeof(struct dt_reg));
48f7f76b85SAndrew Jones 	reg->nr_address_cells = nr_address_cells;
49f7f76b85SAndrew Jones 	reg->nr_size_cells = nr_size_cells;
50f7f76b85SAndrew Jones }
51f7f76b85SAndrew Jones 
52f7f76b85SAndrew Jones int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
53f7f76b85SAndrew Jones {
54f7f76b85SAndrew Jones 	const struct fdt_property *prop;
55f7f76b85SAndrew Jones 	u32 *cells, i;
56f7f76b85SAndrew Jones 	unsigned nr_tuple_cells;
57f7f76b85SAndrew Jones 	int len;
58f7f76b85SAndrew Jones 
59f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "reg", &len);
60f7f76b85SAndrew Jones 	if (prop == NULL)
61f7f76b85SAndrew Jones 		return len;
62f7f76b85SAndrew Jones 
63f7f76b85SAndrew Jones 	cells = (u32 *)prop->data;
64f7f76b85SAndrew Jones 	nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
65f7f76b85SAndrew Jones 	regidx *= nr_tuple_cells;
66f7f76b85SAndrew Jones 
67f7f76b85SAndrew Jones 	if (regidx + nr_tuple_cells > len/sizeof(u32))
68f7f76b85SAndrew Jones 		return -FDT_ERR_NOTFOUND;
69f7f76b85SAndrew Jones 
70f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_address_cells; ++i)
71f7f76b85SAndrew Jones 		reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
72f7f76b85SAndrew Jones 
73f7f76b85SAndrew Jones 	regidx += reg->nr_address_cells;
74f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_size_cells; ++i)
75f7f76b85SAndrew Jones 		reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
76f7f76b85SAndrew Jones 
77f7f76b85SAndrew Jones 	return 0;
78f7f76b85SAndrew Jones }
79f7f76b85SAndrew Jones 
80f7f76b85SAndrew Jones int dt_pbus_translate_node(int fdtnode, int regidx,
81f7f76b85SAndrew Jones 			   struct dt_pbus_reg *pbus_reg)
82f7f76b85SAndrew Jones {
83f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
8481d923cdSAndrew Jones 	u32 nac, nsc;
8581d923cdSAndrew Jones 	int parent, ret;
86f7f76b85SAndrew Jones 
8781d923cdSAndrew Jones 	parent = fdt_parent_offset(fdt, fdtnode);
8881d923cdSAndrew Jones 	if (parent < 0)
8981d923cdSAndrew Jones 		return parent;
9081d923cdSAndrew Jones 
9181d923cdSAndrew Jones 	ret = dt_get_nr_cells(parent, &nac, &nsc);
9281d923cdSAndrew Jones 	if (ret != 0)
9381d923cdSAndrew Jones 		return ret;
9481d923cdSAndrew Jones 
9581d923cdSAndrew Jones 	dt_reg_init(&raw_reg, nac, nsc);
96f7f76b85SAndrew Jones 
97f7f76b85SAndrew Jones 	ret = dt_get_reg(fdtnode, regidx, &raw_reg);
98f7f76b85SAndrew Jones 	if (ret < 0)
99f7f76b85SAndrew Jones 		return ret;
100f7f76b85SAndrew Jones 
101f7f76b85SAndrew Jones 	pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
102f7f76b85SAndrew Jones 					    raw_reg.address_cells);
103f7f76b85SAndrew Jones 	pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
104f7f76b85SAndrew Jones 					    raw_reg.size_cells);
105f7f76b85SAndrew Jones 
106f7f76b85SAndrew Jones 	return 0;
107f7f76b85SAndrew Jones }
108f7f76b85SAndrew Jones 
109f7f76b85SAndrew Jones int dt_pbus_translate(const struct dt_device *dev, int regidx,
110f7f76b85SAndrew Jones 		      void *reg)
111f7f76b85SAndrew Jones {
112f7f76b85SAndrew Jones 	return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
113f7f76b85SAndrew Jones }
114f7f76b85SAndrew Jones 
115f7f76b85SAndrew Jones int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
116f7f76b85SAndrew Jones {
117f7f76b85SAndrew Jones 	/* matches any device with a valid node */
118f7f76b85SAndrew Jones 	return fdtnode < 0 ? fdtnode : 1;
119f7f76b85SAndrew Jones }
120f7f76b85SAndrew Jones 
121f7f76b85SAndrew Jones static const struct dt_bus dt_default_bus = {
122f7f76b85SAndrew Jones 	.match = dt_bus_match_any,
123f7f76b85SAndrew Jones 	.translate = dt_pbus_translate,
124f7f76b85SAndrew Jones };
125f7f76b85SAndrew Jones 
126f7f76b85SAndrew Jones void dt_bus_init_defaults(struct dt_bus *bus)
127f7f76b85SAndrew Jones {
128f7f76b85SAndrew Jones 	memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
129f7f76b85SAndrew Jones }
130f7f76b85SAndrew Jones 
131f7f76b85SAndrew Jones void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
132f7f76b85SAndrew Jones 		    void *info)
133f7f76b85SAndrew Jones {
134f7f76b85SAndrew Jones 	memset(dev, 0, sizeof(struct dt_device));
135f7f76b85SAndrew Jones 	dev->bus = bus;
136f7f76b85SAndrew Jones 	dev->info = info;
137f7f76b85SAndrew Jones }
138f7f76b85SAndrew Jones 
139f7f76b85SAndrew Jones int dt_device_find_compatible(const struct dt_device *dev,
140f7f76b85SAndrew Jones 			      const char *compatible)
141f7f76b85SAndrew Jones {
142f7f76b85SAndrew Jones 	int node, ret;
143f7f76b85SAndrew Jones 
144f7f76b85SAndrew Jones 	node = fdt_node_offset_by_compatible(fdt, -1, compatible);
145f7f76b85SAndrew Jones 	while (node >= 0) {
146f7f76b85SAndrew Jones 		ret = dev->bus->match(dev, node);
147f7f76b85SAndrew Jones 		if (ret < 0)
148f7f76b85SAndrew Jones 			return ret;
149f7f76b85SAndrew Jones 		else if (ret)
150f7f76b85SAndrew Jones 			break;
151f7f76b85SAndrew Jones 		node = fdt_node_offset_by_compatible(fdt, node, compatible);
152f7f76b85SAndrew Jones 	}
153f7f76b85SAndrew Jones 	return node;
154f7f76b85SAndrew Jones }
155f7f76b85SAndrew Jones 
156f7f76b85SAndrew Jones int dt_pbus_get_base_compatible(const char *compatible,
157f7f76b85SAndrew Jones 				struct dt_pbus_reg *base)
158f7f76b85SAndrew Jones {
159f7f76b85SAndrew Jones 	struct dt_device dev;
160f7f76b85SAndrew Jones 	int node;
161f7f76b85SAndrew Jones 
162f7f76b85SAndrew Jones 	dt_device_init(&dev, &dt_default_bus, NULL);
163f7f76b85SAndrew Jones 
164f7f76b85SAndrew Jones 	node = dt_device_find_compatible(&dev, compatible);
165f7f76b85SAndrew Jones 	if (node < 0)
166f7f76b85SAndrew Jones 		return node;
167f7f76b85SAndrew Jones 
168f7f76b85SAndrew Jones 	dt_device_bind_node(&dev, node);
169f7f76b85SAndrew Jones 
170f7f76b85SAndrew Jones 	return dt_pbus_get_base(&dev, base);
171f7f76b85SAndrew Jones }
172f7f76b85SAndrew Jones 
173f7f76b85SAndrew Jones int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
174f7f76b85SAndrew Jones {
175f7f76b85SAndrew Jones 	const char *pn = "device_type", *pv = "memory";
176bb8ab2adSAndrew Jones 	int node, ret, reg_idx, pl = strlen(pv) + 1, nr = 0;
177f7f76b85SAndrew Jones 	struct dt_pbus_reg reg;
178f7f76b85SAndrew Jones 
179f7f76b85SAndrew Jones 	node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
180f7f76b85SAndrew Jones 
181f7f76b85SAndrew Jones 	while (node >= 0) {
182f7f76b85SAndrew Jones 
183bb8ab2adSAndrew Jones 		reg_idx = 0;
184bb8ab2adSAndrew Jones 
185f7f76b85SAndrew Jones 		while (nr < nr_regs) {
186bb8ab2adSAndrew Jones 			ret = dt_pbus_translate_node(node, reg_idx, &reg);
187f7f76b85SAndrew Jones 			if (ret == -FDT_ERR_NOTFOUND)
188f7f76b85SAndrew Jones 				break;
189f7f76b85SAndrew Jones 			if (ret < 0)
190f7f76b85SAndrew Jones 				return ret;
191f7f76b85SAndrew Jones 			regs[nr].addr = reg.addr;
192f7f76b85SAndrew Jones 			regs[nr].size = reg.size;
193bb8ab2adSAndrew Jones 			++nr, ++reg_idx;
194f7f76b85SAndrew Jones 		}
195f7f76b85SAndrew Jones 
196f7f76b85SAndrew Jones 		node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
197f7f76b85SAndrew Jones 	}
198f7f76b85SAndrew Jones 
199f7f76b85SAndrew Jones 	return node != -FDT_ERR_NOTFOUND ? node : nr;
200f7f76b85SAndrew Jones }
201f7f76b85SAndrew Jones 
202f7f76b85SAndrew Jones int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval, void *info),
203f7f76b85SAndrew Jones 			 void *info)
204f7f76b85SAndrew Jones {
205f7f76b85SAndrew Jones 	const struct fdt_property *prop;
206f7f76b85SAndrew Jones 	int cpus, cpu, ret, len;
207f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
208f7f76b85SAndrew Jones 	u32 nac, nsc;
209f7f76b85SAndrew Jones 
210f7f76b85SAndrew Jones 	cpus = fdt_path_offset(fdt, "/cpus");
211f7f76b85SAndrew Jones 	if (cpus < 0)
212f7f76b85SAndrew Jones 		return cpus;
213f7f76b85SAndrew Jones 
214f7f76b85SAndrew Jones 	ret = dt_get_nr_cells(cpus, &nac, &nsc);
215f7f76b85SAndrew Jones 	if (ret < 0)
216f7f76b85SAndrew Jones 		return ret;
217f7f76b85SAndrew Jones 
218f7f76b85SAndrew Jones 	dt_reg_init(&raw_reg, nac, nsc);
219f7f76b85SAndrew Jones 
220f7f76b85SAndrew Jones 	dt_for_each_subnode(cpus, cpu) {
221f7f76b85SAndrew Jones 
222f7f76b85SAndrew Jones 		prop = fdt_get_property(fdt, cpu, "device_type", &len);
223f7f76b85SAndrew Jones 		if (prop == NULL)
224f7f76b85SAndrew Jones 			return len;
225f7f76b85SAndrew Jones 
226f7f76b85SAndrew Jones 		if (len != 4 || strcmp((char *)prop->data, "cpu"))
227f7f76b85SAndrew Jones 			continue;
228f7f76b85SAndrew Jones 
229f7f76b85SAndrew Jones 		ret = dt_get_reg(cpu, 0, &raw_reg);
230f7f76b85SAndrew Jones 		if (ret < 0)
231f7f76b85SAndrew Jones 			return ret;
232f7f76b85SAndrew Jones 
233f7f76b85SAndrew Jones 		func(cpu, raw_reg.address_cells[0], info);
234f7f76b85SAndrew Jones 	}
235f7f76b85SAndrew Jones 
236f7f76b85SAndrew Jones 	return 0;
237f7f76b85SAndrew Jones }
238f7f76b85SAndrew Jones 
239f7f76b85SAndrew Jones int dt_get_bootargs(const char **bootargs)
240f7f76b85SAndrew Jones {
241f7f76b85SAndrew Jones 	const struct fdt_property *prop;
242f7f76b85SAndrew Jones 	int node, len;
243f7f76b85SAndrew Jones 
244f7f76b85SAndrew Jones 	*bootargs = NULL;
245f7f76b85SAndrew Jones 
246f7f76b85SAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
247f7f76b85SAndrew Jones 	if (node < 0)
248f7f76b85SAndrew Jones 		return node;
249f7f76b85SAndrew Jones 
250f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, node, "bootargs", &len);
251f7f76b85SAndrew Jones 	if (prop)
252f7f76b85SAndrew Jones 		*bootargs = prop->data;
253f7f76b85SAndrew Jones 	else if (len < 0 && len != -FDT_ERR_NOTFOUND)
254f7f76b85SAndrew Jones 		return len;
255f7f76b85SAndrew Jones 
256f7f76b85SAndrew Jones 	return 0;
257f7f76b85SAndrew Jones }
258f7f76b85SAndrew Jones 
2597e29db9fSAndrew Jones int dt_get_default_console_node(void)
2607e29db9fSAndrew Jones {
2617e29db9fSAndrew Jones 	const struct fdt_property *prop;
2627e29db9fSAndrew Jones 	int node, len;
2637e29db9fSAndrew Jones 
2647e29db9fSAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
2657e29db9fSAndrew Jones 	if (node < 0)
2667e29db9fSAndrew Jones 		return node;
2677e29db9fSAndrew Jones 
2687e29db9fSAndrew Jones 	prop = fdt_get_property(fdt, node, "stdout-path", &len);
2697e29db9fSAndrew Jones 	if (!prop) {
2707e29db9fSAndrew Jones 		prop = fdt_get_property(fdt, node, "linux,stdout-path", &len);
2717e29db9fSAndrew Jones 		if (!prop)
2727e29db9fSAndrew Jones 			return len;
2737e29db9fSAndrew Jones 	}
2747e29db9fSAndrew Jones 
2757e29db9fSAndrew Jones 	return fdt_path_offset(fdt, prop->data);
2767e29db9fSAndrew Jones }
2777e29db9fSAndrew Jones 
278f7f76b85SAndrew Jones int dt_init(const void *fdt_ptr)
279f7f76b85SAndrew Jones {
280*18407418SAndrew Jones 	int ret;
281f7f76b85SAndrew Jones 
282f7f76b85SAndrew Jones 	ret = fdt_check_header(fdt_ptr);
283f7f76b85SAndrew Jones 	if (ret < 0)
284f7f76b85SAndrew Jones 		return ret;
285f7f76b85SAndrew Jones 
286*18407418SAndrew Jones 	/* Sanity check the path.  */
287*18407418SAndrew Jones 	ret = fdt_path_offset(fdt_ptr, "/");
288f7f76b85SAndrew Jones 	if (ret < 0)
289f7f76b85SAndrew Jones 		return ret;
290f7f76b85SAndrew Jones 
291*18407418SAndrew Jones 	fdt = fdt_ptr;
292f7f76b85SAndrew Jones 	return 0;
293f7f76b85SAndrew Jones }
294