xref: /kvm-unit-tests/lib/devicetree.c (revision e526bc786e9878c3880ae4b09b01a4572756e492)
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 
dt_fdt(void)12f7f76b85SAndrew Jones const void *dt_fdt(void)
13f7f76b85SAndrew Jones {
14f7f76b85SAndrew Jones 	return fdt;
15f7f76b85SAndrew Jones }
16f7f76b85SAndrew Jones 
dt_available(void)17f7f76b85SAndrew Jones bool dt_available(void)
18f7f76b85SAndrew Jones {
19*040d725eSNikos Nikoleris 	return fdt && fdt_check_header(fdt) == 0;
20f7f76b85SAndrew Jones }
21f7f76b85SAndrew Jones 
dt_get_nr_cells(int fdtnode,u32 * nr_address_cells,u32 * nr_size_cells)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;
26db07bff0SAndrew Jones 	int len, nac, nsc;
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;
33db07bff0SAndrew Jones 	nac = 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;
40db07bff0SAndrew Jones 	nsc = fdt32_to_cpu(*nr_cells);
41db07bff0SAndrew Jones 
42db07bff0SAndrew Jones 	*nr_address_cells = nac;
43db07bff0SAndrew Jones 	*nr_size_cells = nsc;
44f7f76b85SAndrew Jones 
45f7f76b85SAndrew Jones 	return 0;
46f7f76b85SAndrew Jones }
47f7f76b85SAndrew Jones 
dt_reg_init(struct dt_reg * reg,u32 nr_address_cells,u32 nr_size_cells)48f7f76b85SAndrew Jones void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
49f7f76b85SAndrew Jones {
50f7f76b85SAndrew Jones 	memset(reg, 0, sizeof(struct dt_reg));
51f7f76b85SAndrew Jones 	reg->nr_address_cells = nr_address_cells;
52f7f76b85SAndrew Jones 	reg->nr_size_cells = nr_size_cells;
53f7f76b85SAndrew Jones }
54f7f76b85SAndrew Jones 
dt_get_reg(int fdtnode,int regidx,struct dt_reg * reg)55f7f76b85SAndrew Jones int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
56f7f76b85SAndrew Jones {
57f7f76b85SAndrew Jones 	const struct fdt_property *prop;
58f7f76b85SAndrew Jones 	u32 *cells, i;
59f7f76b85SAndrew Jones 	unsigned nr_tuple_cells;
60f7f76b85SAndrew Jones 	int len;
61f7f76b85SAndrew Jones 
62f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "reg", &len);
63f7f76b85SAndrew Jones 	if (prop == NULL)
64f7f76b85SAndrew Jones 		return len;
65f7f76b85SAndrew Jones 
66f7f76b85SAndrew Jones 	cells = (u32 *)prop->data;
67f7f76b85SAndrew Jones 	nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
68f7f76b85SAndrew Jones 	regidx *= nr_tuple_cells;
69f7f76b85SAndrew Jones 
70f7f76b85SAndrew Jones 	if (regidx + nr_tuple_cells > len/sizeof(u32))
71f7f76b85SAndrew Jones 		return -FDT_ERR_NOTFOUND;
72f7f76b85SAndrew Jones 
73f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_address_cells; ++i)
74f7f76b85SAndrew Jones 		reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
75f7f76b85SAndrew Jones 
76f7f76b85SAndrew Jones 	regidx += reg->nr_address_cells;
77f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_size_cells; ++i)
78f7f76b85SAndrew Jones 		reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
79f7f76b85SAndrew Jones 
80f7f76b85SAndrew Jones 	return 0;
81f7f76b85SAndrew Jones }
82f7f76b85SAndrew Jones 
dt_pbus_translate_node(int fdtnode,int regidx,struct dt_pbus_reg * pbus_reg)83f7f76b85SAndrew Jones int dt_pbus_translate_node(int fdtnode, int regidx,
84f7f76b85SAndrew Jones 			   struct dt_pbus_reg *pbus_reg)
85f7f76b85SAndrew Jones {
86f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
8781d923cdSAndrew Jones 	u32 nac, nsc;
8881d923cdSAndrew Jones 	int parent, ret;
89f7f76b85SAndrew Jones 
9081d923cdSAndrew Jones 	parent = fdt_parent_offset(fdt, fdtnode);
9181d923cdSAndrew Jones 	if (parent < 0)
9281d923cdSAndrew Jones 		return parent;
9381d923cdSAndrew Jones 
9481d923cdSAndrew Jones 	ret = dt_get_nr_cells(parent, &nac, &nsc);
9581d923cdSAndrew Jones 	if (ret != 0)
9681d923cdSAndrew Jones 		return ret;
9781d923cdSAndrew Jones 
9881d923cdSAndrew Jones 	dt_reg_init(&raw_reg, nac, nsc);
99f7f76b85SAndrew Jones 
100f7f76b85SAndrew Jones 	ret = dt_get_reg(fdtnode, regidx, &raw_reg);
101f7f76b85SAndrew Jones 	if (ret < 0)
102f7f76b85SAndrew Jones 		return ret;
103f7f76b85SAndrew Jones 
104f7f76b85SAndrew Jones 	pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
105f7f76b85SAndrew Jones 					    raw_reg.address_cells);
106f7f76b85SAndrew Jones 	pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
107f7f76b85SAndrew Jones 					    raw_reg.size_cells);
108f7f76b85SAndrew Jones 
109f7f76b85SAndrew Jones 	return 0;
110f7f76b85SAndrew Jones }
111f7f76b85SAndrew Jones 
dt_pbus_translate(const struct dt_device * dev,int regidx,void * reg)112f7f76b85SAndrew Jones int dt_pbus_translate(const struct dt_device *dev, int regidx,
113f7f76b85SAndrew Jones 		      void *reg)
114f7f76b85SAndrew Jones {
115f7f76b85SAndrew Jones 	return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
116f7f76b85SAndrew Jones }
117f7f76b85SAndrew Jones 
dt_bus_match_any(const struct dt_device * dev __unused,int fdtnode)118f7f76b85SAndrew Jones int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
119f7f76b85SAndrew Jones {
120f7f76b85SAndrew Jones 	/* matches any device with a valid node */
121f7f76b85SAndrew Jones 	return fdtnode < 0 ? fdtnode : 1;
122f7f76b85SAndrew Jones }
123f7f76b85SAndrew Jones 
124f7f76b85SAndrew Jones static const struct dt_bus dt_default_bus = {
125f7f76b85SAndrew Jones 	.match = dt_bus_match_any,
126f7f76b85SAndrew Jones 	.translate = dt_pbus_translate,
127f7f76b85SAndrew Jones };
128f7f76b85SAndrew Jones 
dt_bus_init_defaults(struct dt_bus * bus)129f7f76b85SAndrew Jones void dt_bus_init_defaults(struct dt_bus *bus)
130f7f76b85SAndrew Jones {
131f7f76b85SAndrew Jones 	memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
132f7f76b85SAndrew Jones }
133f7f76b85SAndrew Jones 
dt_device_init(struct dt_device * dev,const struct dt_bus * bus,void * info)134f7f76b85SAndrew Jones void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
135f7f76b85SAndrew Jones 		    void *info)
136f7f76b85SAndrew Jones {
137f7f76b85SAndrew Jones 	memset(dev, 0, sizeof(struct dt_device));
138f7f76b85SAndrew Jones 	dev->bus = bus;
139f7f76b85SAndrew Jones 	dev->info = info;
140f7f76b85SAndrew Jones }
141f7f76b85SAndrew Jones 
dt_device_find_compatible(const struct dt_device * dev,const char * compatible)142f7f76b85SAndrew Jones int dt_device_find_compatible(const struct dt_device *dev,
143f7f76b85SAndrew Jones 			      const char *compatible)
144f7f76b85SAndrew Jones {
145f7f76b85SAndrew Jones 	int node, ret;
146f7f76b85SAndrew Jones 
147f7f76b85SAndrew Jones 	node = fdt_node_offset_by_compatible(fdt, -1, compatible);
148f7f76b85SAndrew Jones 	while (node >= 0) {
149f7f76b85SAndrew Jones 		ret = dev->bus->match(dev, node);
150f7f76b85SAndrew Jones 		if (ret < 0)
151f7f76b85SAndrew Jones 			return ret;
152f7f76b85SAndrew Jones 		else if (ret)
153f7f76b85SAndrew Jones 			break;
154f7f76b85SAndrew Jones 		node = fdt_node_offset_by_compatible(fdt, node, compatible);
155f7f76b85SAndrew Jones 	}
156f7f76b85SAndrew Jones 	return node;
157f7f76b85SAndrew Jones }
158f7f76b85SAndrew Jones 
dt_pbus_get_base_compatible(const char * compatible,struct dt_pbus_reg * base)159f7f76b85SAndrew Jones int dt_pbus_get_base_compatible(const char *compatible,
160f7f76b85SAndrew Jones 				struct dt_pbus_reg *base)
161f7f76b85SAndrew Jones {
162f7f76b85SAndrew Jones 	struct dt_device dev;
163f7f76b85SAndrew Jones 	int node;
164f7f76b85SAndrew Jones 
165f7f76b85SAndrew Jones 	dt_device_init(&dev, &dt_default_bus, NULL);
166f7f76b85SAndrew Jones 
167f7f76b85SAndrew Jones 	node = dt_device_find_compatible(&dev, compatible);
168f7f76b85SAndrew Jones 	if (node < 0)
169f7f76b85SAndrew Jones 		return node;
170f7f76b85SAndrew Jones 
171f7f76b85SAndrew Jones 	dt_device_bind_node(&dev, node);
172f7f76b85SAndrew Jones 
173f7f76b85SAndrew Jones 	return dt_pbus_get_base(&dev, base);
174f7f76b85SAndrew Jones }
175f7f76b85SAndrew Jones 
dt_get_memory_params(struct dt_pbus_reg * regs,int nr_regs)176f7f76b85SAndrew Jones int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
177f7f76b85SAndrew Jones {
178f7f76b85SAndrew Jones 	const char *pn = "device_type", *pv = "memory";
179bb8ab2adSAndrew Jones 	int node, ret, reg_idx, pl = strlen(pv) + 1, nr = 0;
180f7f76b85SAndrew Jones 	struct dt_pbus_reg reg;
181f7f76b85SAndrew Jones 
182f7f76b85SAndrew Jones 	node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
183f7f76b85SAndrew Jones 
184f7f76b85SAndrew Jones 	while (node >= 0) {
185f7f76b85SAndrew Jones 
186bb8ab2adSAndrew Jones 		reg_idx = 0;
187bb8ab2adSAndrew Jones 
188f7f76b85SAndrew Jones 		while (nr < nr_regs) {
189bb8ab2adSAndrew Jones 			ret = dt_pbus_translate_node(node, reg_idx, &reg);
190f7f76b85SAndrew Jones 			if (ret == -FDT_ERR_NOTFOUND)
191f7f76b85SAndrew Jones 				break;
192f7f76b85SAndrew Jones 			if (ret < 0)
193f7f76b85SAndrew Jones 				return ret;
194f7f76b85SAndrew Jones 			regs[nr].addr = reg.addr;
195f7f76b85SAndrew Jones 			regs[nr].size = reg.size;
196bb8ab2adSAndrew Jones 			++nr, ++reg_idx;
197f7f76b85SAndrew Jones 		}
198f7f76b85SAndrew Jones 
199f7f76b85SAndrew Jones 		node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
200f7f76b85SAndrew Jones 	}
201f7f76b85SAndrew Jones 
202f7f76b85SAndrew Jones 	return node != -FDT_ERR_NOTFOUND ? node : nr;
203f7f76b85SAndrew Jones }
204f7f76b85SAndrew Jones 
dt_for_each_cpu_node(void (* func)(int fdtnode,u64 regval,void * info),void * info)2057a20b74eSAndrew Jones int dt_for_each_cpu_node(void (*func)(int fdtnode, u64 regval, void *info),
206f7f76b85SAndrew Jones 			 void *info)
207f7f76b85SAndrew Jones {
208f7f76b85SAndrew Jones 	const struct fdt_property *prop;
209f7f76b85SAndrew Jones 	int cpus, cpu, ret, len;
210f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
211f7f76b85SAndrew Jones 	u32 nac, nsc;
2127a20b74eSAndrew Jones 	u64 regval;
213f7f76b85SAndrew Jones 
214f7f76b85SAndrew Jones 	cpus = fdt_path_offset(fdt, "/cpus");
215f7f76b85SAndrew Jones 	if (cpus < 0)
216f7f76b85SAndrew Jones 		return cpus;
217f7f76b85SAndrew Jones 
218f7f76b85SAndrew Jones 	ret = dt_get_nr_cells(cpus, &nac, &nsc);
219f7f76b85SAndrew Jones 	if (ret < 0)
220f7f76b85SAndrew Jones 		return ret;
221f7f76b85SAndrew Jones 
222f7f76b85SAndrew Jones 	dt_reg_init(&raw_reg, nac, nsc);
223f7f76b85SAndrew Jones 
224f7f76b85SAndrew Jones 	dt_for_each_subnode(cpus, cpu) {
225f7f76b85SAndrew Jones 
226f7f76b85SAndrew Jones 		prop = fdt_get_property(fdt, cpu, "device_type", &len);
227f7f76b85SAndrew Jones 		if (prop == NULL)
22857a11d8dSZeng Tao 			continue;
229f7f76b85SAndrew Jones 
230f7f76b85SAndrew Jones 		if (len != 4 || strcmp((char *)prop->data, "cpu"))
231f7f76b85SAndrew Jones 			continue;
232f7f76b85SAndrew Jones 
233f7f76b85SAndrew Jones 		ret = dt_get_reg(cpu, 0, &raw_reg);
234f7f76b85SAndrew Jones 		if (ret < 0)
235f7f76b85SAndrew Jones 			return ret;
236f7f76b85SAndrew Jones 
2377a20b74eSAndrew Jones 		regval = raw_reg.address_cells[0];
2387a20b74eSAndrew Jones 		if (nac == 2)
2397a20b74eSAndrew Jones 			regval = (regval << 32) | raw_reg.address_cells[1];
2407a20b74eSAndrew Jones 
2417a20b74eSAndrew Jones 		func(cpu, regval, info);
242f7f76b85SAndrew Jones 	}
243f7f76b85SAndrew Jones 
244f7f76b85SAndrew Jones 	return 0;
245f7f76b85SAndrew Jones }
246f7f76b85SAndrew Jones 
dt_get_bootargs(const char ** bootargs)247f7f76b85SAndrew Jones int dt_get_bootargs(const char **bootargs)
248f7f76b85SAndrew Jones {
249f7f76b85SAndrew Jones 	const struct fdt_property *prop;
250f7f76b85SAndrew Jones 	int node, len;
251f7f76b85SAndrew Jones 
252f7f76b85SAndrew Jones 	*bootargs = NULL;
253f7f76b85SAndrew Jones 
254f7f76b85SAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
255f7f76b85SAndrew Jones 	if (node < 0)
256f7f76b85SAndrew Jones 		return node;
257f7f76b85SAndrew Jones 
258f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, node, "bootargs", &len);
259d81b83fdSAndrew Jones 	if (!prop)
260f7f76b85SAndrew Jones 		return len;
261f7f76b85SAndrew Jones 
262d81b83fdSAndrew Jones 	*bootargs = prop->data;
263f7f76b85SAndrew Jones 	return 0;
264f7f76b85SAndrew Jones }
265f7f76b85SAndrew Jones 
dt_get_default_console_node(void)2667e29db9fSAndrew Jones int dt_get_default_console_node(void)
2677e29db9fSAndrew Jones {
268546914b4SNikos Nikoleris 	const char *p, *q;
2697e29db9fSAndrew Jones 	int node, len;
2707e29db9fSAndrew Jones 
2717e29db9fSAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
2727e29db9fSAndrew Jones 	if (node < 0)
2737e29db9fSAndrew Jones 		return node;
2747e29db9fSAndrew Jones 
275546914b4SNikos Nikoleris 	p = fdt_getprop(fdt, node, "stdout-path", &len);
276546914b4SNikos Nikoleris 	if (!p) {
277546914b4SNikos Nikoleris 		p = fdt_getprop(fdt, node, "linux,stdout-path", &len);
278546914b4SNikos Nikoleris 		if (!p)
2797e29db9fSAndrew Jones 			return len;
2807e29db9fSAndrew Jones 	}
2817e29db9fSAndrew Jones 
282546914b4SNikos Nikoleris 	q = strchrnul(p, ':');
283546914b4SNikos Nikoleris 	len = q - p;
284546914b4SNikos Nikoleris 
285546914b4SNikos Nikoleris 	return fdt_path_offset_namelen(fdt, p, len);
2867e29db9fSAndrew Jones }
2877e29db9fSAndrew Jones 
dt_get_initrd(const char ** initrd,u32 * size)288dd3f102fSAndrew Jones int dt_get_initrd(const char **initrd, u32 *size)
289dd3f102fSAndrew Jones {
290dd3f102fSAndrew Jones 	const struct fdt_property *prop;
291c6f24b86SAlexandru Elisei 	u64 start, end;
292dd3f102fSAndrew Jones 	int node, len;
293dd3f102fSAndrew Jones 	u32 *data;
294dd3f102fSAndrew Jones 
295dd3f102fSAndrew Jones 	*initrd = NULL;
296dd3f102fSAndrew Jones 	*size = 0;
297dd3f102fSAndrew Jones 
298dd3f102fSAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
299dd3f102fSAndrew Jones 	if (node < 0)
300dd3f102fSAndrew Jones 		return node;
301dd3f102fSAndrew Jones 
302dd3f102fSAndrew Jones 	prop = fdt_get_property(fdt, node, "linux,initrd-start", &len);
303dd3f102fSAndrew Jones 	if (!prop)
304dd3f102fSAndrew Jones 		return len;
305dd3f102fSAndrew Jones 	data = (u32 *)prop->data;
306c6f24b86SAlexandru Elisei 	start = fdt32_to_cpu(*data);
307c6f24b86SAlexandru Elisei 	if (len == 8) {
308c6f24b86SAlexandru Elisei 		data++;
309c6f24b86SAlexandru Elisei 		start = (start << 32) | fdt32_to_cpu(*data);
310c6f24b86SAlexandru Elisei 	}
311dd3f102fSAndrew Jones 
312dd3f102fSAndrew Jones 	prop = fdt_get_property(fdt, node, "linux,initrd-end", &len);
313dd3f102fSAndrew Jones 	if (!prop) {
314dd3f102fSAndrew Jones 		assert(len != -FDT_ERR_NOTFOUND);
315dd3f102fSAndrew Jones 		return len;
316dd3f102fSAndrew Jones 	}
317dd3f102fSAndrew Jones 	data = (u32 *)prop->data;
318c6f24b86SAlexandru Elisei 	end = fdt32_to_cpu(*data);
319c6f24b86SAlexandru Elisei 	if (len == 8) {
320c6f24b86SAlexandru Elisei 		data++;
321c6f24b86SAlexandru Elisei 		end = (end << 32) | fdt32_to_cpu(*data);
322c6f24b86SAlexandru Elisei 	}
323dd3f102fSAndrew Jones 
324e9e4214aSAndrew Jones 	assert(start < end);
325e9e4214aSAndrew Jones 	assert(sizeof(long) == 8 || !(end >> 32));
326e9e4214aSAndrew Jones 
327c6f24b86SAlexandru Elisei 	*initrd = (char *)(unsigned long)start;
328c6f24b86SAlexandru Elisei 	*size = end - start;
329dd3f102fSAndrew Jones 
330dd3f102fSAndrew Jones 	return 0;
331dd3f102fSAndrew Jones }
332dd3f102fSAndrew Jones 
dt_init(const void * fdt_ptr)333f7f76b85SAndrew Jones int dt_init(const void *fdt_ptr)
334f7f76b85SAndrew Jones {
33518407418SAndrew Jones 	int ret;
336f7f76b85SAndrew Jones 
337f7f76b85SAndrew Jones 	ret = fdt_check_header(fdt_ptr);
338f7f76b85SAndrew Jones 	if (ret < 0)
339f7f76b85SAndrew Jones 		return ret;
340f7f76b85SAndrew Jones 
34118407418SAndrew Jones 	/* Sanity check the path.  */
34218407418SAndrew Jones 	ret = fdt_path_offset(fdt_ptr, "/");
343f7f76b85SAndrew Jones 	if (ret < 0)
344f7f76b85SAndrew Jones 		return ret;
345f7f76b85SAndrew Jones 
34618407418SAndrew Jones 	fdt = fdt_ptr;
347f7f76b85SAndrew Jones 	return 0;
348f7f76b85SAndrew Jones }
349