xref: /kvm-unit-tests/lib/devicetree.c (revision 7e29db9f3a006319fcfde7d5514cbb0ed5b4d97e)
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 static u32 root_nr_address_cells, root_nr_size_cells;
12f7f76b85SAndrew Jones 
13f7f76b85SAndrew Jones const void *dt_fdt(void)
14f7f76b85SAndrew Jones {
15f7f76b85SAndrew Jones 	return fdt;
16f7f76b85SAndrew Jones }
17f7f76b85SAndrew Jones 
18f7f76b85SAndrew Jones bool dt_available(void)
19f7f76b85SAndrew Jones {
20f7f76b85SAndrew Jones 	return fdt_check_header(fdt) == 0;
21f7f76b85SAndrew Jones }
22f7f76b85SAndrew Jones 
23f7f76b85SAndrew Jones int dt_get_nr_cells(int fdtnode, u32 *nr_address_cells, u32 *nr_size_cells)
24f7f76b85SAndrew Jones {
25f7f76b85SAndrew Jones 	const struct fdt_property *prop;
26f7f76b85SAndrew Jones 	u32 *nr_cells;
27f7f76b85SAndrew Jones 	int len;
28f7f76b85SAndrew Jones 
29f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "#address-cells", &len);
30f7f76b85SAndrew Jones 	if (prop == NULL)
31f7f76b85SAndrew Jones 		return len;
32f7f76b85SAndrew Jones 
33f7f76b85SAndrew Jones 	nr_cells = (u32 *)prop->data;
34f7f76b85SAndrew Jones 	*nr_address_cells = fdt32_to_cpu(*nr_cells);
35f7f76b85SAndrew Jones 
36f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "#size-cells", &len);
37f7f76b85SAndrew Jones 	if (prop == NULL)
38f7f76b85SAndrew Jones 		return len;
39f7f76b85SAndrew Jones 
40f7f76b85SAndrew Jones 	nr_cells = (u32 *)prop->data;
41f7f76b85SAndrew Jones 	*nr_size_cells = fdt32_to_cpu(*nr_cells);
42f7f76b85SAndrew Jones 
43f7f76b85SAndrew Jones 	return 0;
44f7f76b85SAndrew Jones }
45f7f76b85SAndrew Jones 
46f7f76b85SAndrew Jones void dt_reg_init(struct dt_reg *reg, u32 nr_address_cells, u32 nr_size_cells)
47f7f76b85SAndrew Jones {
48f7f76b85SAndrew Jones 	memset(reg, 0, sizeof(struct dt_reg));
49f7f76b85SAndrew Jones 	reg->nr_address_cells = nr_address_cells;
50f7f76b85SAndrew Jones 	reg->nr_size_cells = nr_size_cells;
51f7f76b85SAndrew Jones }
52f7f76b85SAndrew Jones 
53f7f76b85SAndrew Jones int dt_get_reg(int fdtnode, int regidx, struct dt_reg *reg)
54f7f76b85SAndrew Jones {
55f7f76b85SAndrew Jones 	const struct fdt_property *prop;
56f7f76b85SAndrew Jones 	u32 *cells, i;
57f7f76b85SAndrew Jones 	unsigned nr_tuple_cells;
58f7f76b85SAndrew Jones 	int len;
59f7f76b85SAndrew Jones 
60f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, fdtnode, "reg", &len);
61f7f76b85SAndrew Jones 	if (prop == NULL)
62f7f76b85SAndrew Jones 		return len;
63f7f76b85SAndrew Jones 
64f7f76b85SAndrew Jones 	cells = (u32 *)prop->data;
65f7f76b85SAndrew Jones 	nr_tuple_cells = reg->nr_address_cells + reg->nr_size_cells;
66f7f76b85SAndrew Jones 	regidx *= nr_tuple_cells;
67f7f76b85SAndrew Jones 
68f7f76b85SAndrew Jones 	if (regidx + nr_tuple_cells > len/sizeof(u32))
69f7f76b85SAndrew Jones 		return -FDT_ERR_NOTFOUND;
70f7f76b85SAndrew Jones 
71f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_address_cells; ++i)
72f7f76b85SAndrew Jones 		reg->address_cells[i] = fdt32_to_cpu(cells[regidx + i]);
73f7f76b85SAndrew Jones 
74f7f76b85SAndrew Jones 	regidx += reg->nr_address_cells;
75f7f76b85SAndrew Jones 	for (i = 0; i < reg->nr_size_cells; ++i)
76f7f76b85SAndrew Jones 		reg->size_cells[i] = fdt32_to_cpu(cells[regidx + i]);
77f7f76b85SAndrew Jones 
78f7f76b85SAndrew Jones 	return 0;
79f7f76b85SAndrew Jones }
80f7f76b85SAndrew Jones 
81f7f76b85SAndrew Jones int dt_pbus_translate_node(int fdtnode, int regidx,
82f7f76b85SAndrew Jones 			   struct dt_pbus_reg *pbus_reg)
83f7f76b85SAndrew Jones {
84f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
85f7f76b85SAndrew Jones 	int ret;
86f7f76b85SAndrew Jones 
87f7f76b85SAndrew Jones 	dt_reg_init(&raw_reg, root_nr_address_cells, root_nr_size_cells);
88f7f76b85SAndrew Jones 
89f7f76b85SAndrew Jones 	ret = dt_get_reg(fdtnode, regidx, &raw_reg);
90f7f76b85SAndrew Jones 	if (ret < 0)
91f7f76b85SAndrew Jones 		return ret;
92f7f76b85SAndrew Jones 
93f7f76b85SAndrew Jones 	pbus_reg->addr = dt_pbus_read_cells(raw_reg.nr_address_cells,
94f7f76b85SAndrew Jones 					    raw_reg.address_cells);
95f7f76b85SAndrew Jones 	pbus_reg->size = dt_pbus_read_cells(raw_reg.nr_size_cells,
96f7f76b85SAndrew Jones 					    raw_reg.size_cells);
97f7f76b85SAndrew Jones 
98f7f76b85SAndrew Jones 	return 0;
99f7f76b85SAndrew Jones }
100f7f76b85SAndrew Jones 
101f7f76b85SAndrew Jones int dt_pbus_translate(const struct dt_device *dev, int regidx,
102f7f76b85SAndrew Jones 		      void *reg)
103f7f76b85SAndrew Jones {
104f7f76b85SAndrew Jones 	return dt_pbus_translate_node(dev->fdtnode, regidx, reg);
105f7f76b85SAndrew Jones }
106f7f76b85SAndrew Jones 
107f7f76b85SAndrew Jones int dt_bus_match_any(const struct dt_device *dev __unused, int fdtnode)
108f7f76b85SAndrew Jones {
109f7f76b85SAndrew Jones 	/* matches any device with a valid node */
110f7f76b85SAndrew Jones 	return fdtnode < 0 ? fdtnode : 1;
111f7f76b85SAndrew Jones }
112f7f76b85SAndrew Jones 
113f7f76b85SAndrew Jones static const struct dt_bus dt_default_bus = {
114f7f76b85SAndrew Jones 	.match = dt_bus_match_any,
115f7f76b85SAndrew Jones 	.translate = dt_pbus_translate,
116f7f76b85SAndrew Jones };
117f7f76b85SAndrew Jones 
118f7f76b85SAndrew Jones void dt_bus_init_defaults(struct dt_bus *bus)
119f7f76b85SAndrew Jones {
120f7f76b85SAndrew Jones 	memcpy(bus, &dt_default_bus, sizeof(struct dt_bus));
121f7f76b85SAndrew Jones }
122f7f76b85SAndrew Jones 
123f7f76b85SAndrew Jones void dt_device_init(struct dt_device *dev, const struct dt_bus *bus,
124f7f76b85SAndrew Jones 		    void *info)
125f7f76b85SAndrew Jones {
126f7f76b85SAndrew Jones 	memset(dev, 0, sizeof(struct dt_device));
127f7f76b85SAndrew Jones 	dev->bus = bus;
128f7f76b85SAndrew Jones 	dev->info = info;
129f7f76b85SAndrew Jones }
130f7f76b85SAndrew Jones 
131f7f76b85SAndrew Jones int dt_device_find_compatible(const struct dt_device *dev,
132f7f76b85SAndrew Jones 			      const char *compatible)
133f7f76b85SAndrew Jones {
134f7f76b85SAndrew Jones 	int node, ret;
135f7f76b85SAndrew Jones 
136f7f76b85SAndrew Jones 	node = fdt_node_offset_by_compatible(fdt, -1, compatible);
137f7f76b85SAndrew Jones 	while (node >= 0) {
138f7f76b85SAndrew Jones 		ret = dev->bus->match(dev, node);
139f7f76b85SAndrew Jones 		if (ret < 0)
140f7f76b85SAndrew Jones 			return ret;
141f7f76b85SAndrew Jones 		else if (ret)
142f7f76b85SAndrew Jones 			break;
143f7f76b85SAndrew Jones 		node = fdt_node_offset_by_compatible(fdt, node, compatible);
144f7f76b85SAndrew Jones 	}
145f7f76b85SAndrew Jones 	return node;
146f7f76b85SAndrew Jones }
147f7f76b85SAndrew Jones 
148f7f76b85SAndrew Jones int dt_pbus_get_base_compatible(const char *compatible,
149f7f76b85SAndrew Jones 				struct dt_pbus_reg *base)
150f7f76b85SAndrew Jones {
151f7f76b85SAndrew Jones 	struct dt_device dev;
152f7f76b85SAndrew Jones 	int node;
153f7f76b85SAndrew Jones 
154f7f76b85SAndrew Jones 	dt_device_init(&dev, &dt_default_bus, NULL);
155f7f76b85SAndrew Jones 
156f7f76b85SAndrew Jones 	node = dt_device_find_compatible(&dev, compatible);
157f7f76b85SAndrew Jones 	if (node < 0)
158f7f76b85SAndrew Jones 		return node;
159f7f76b85SAndrew Jones 
160f7f76b85SAndrew Jones 	dt_device_bind_node(&dev, node);
161f7f76b85SAndrew Jones 
162f7f76b85SAndrew Jones 	return dt_pbus_get_base(&dev, base);
163f7f76b85SAndrew Jones }
164f7f76b85SAndrew Jones 
165f7f76b85SAndrew Jones int dt_get_memory_params(struct dt_pbus_reg *regs, int nr_regs)
166f7f76b85SAndrew Jones {
167f7f76b85SAndrew Jones 	const char *pn = "device_type", *pv = "memory";
168f7f76b85SAndrew Jones 	int node, ret, pl = strlen(pv) + 1, nr = 0;
169f7f76b85SAndrew Jones 	struct dt_pbus_reg reg;
170f7f76b85SAndrew Jones 
171f7f76b85SAndrew Jones 	node = fdt_node_offset_by_prop_value(fdt, -1, pn, pv, pl);
172f7f76b85SAndrew Jones 
173f7f76b85SAndrew Jones 	while (node >= 0) {
174f7f76b85SAndrew Jones 
175f7f76b85SAndrew Jones 		while (nr < nr_regs) {
176f7f76b85SAndrew Jones 			ret = dt_pbus_translate_node(node, nr, &reg);
177f7f76b85SAndrew Jones 			if (ret == -FDT_ERR_NOTFOUND)
178f7f76b85SAndrew Jones 				break;
179f7f76b85SAndrew Jones 			if (ret < 0)
180f7f76b85SAndrew Jones 				return ret;
181f7f76b85SAndrew Jones 			regs[nr].addr = reg.addr;
182f7f76b85SAndrew Jones 			regs[nr].size = reg.size;
183f7f76b85SAndrew Jones 			++nr;
184f7f76b85SAndrew Jones 		}
185f7f76b85SAndrew Jones 
186f7f76b85SAndrew Jones 		node = fdt_node_offset_by_prop_value(fdt, node, pn, pv, pl);
187f7f76b85SAndrew Jones 	}
188f7f76b85SAndrew Jones 
189f7f76b85SAndrew Jones 	return node != -FDT_ERR_NOTFOUND ? node : nr;
190f7f76b85SAndrew Jones }
191f7f76b85SAndrew Jones 
192f7f76b85SAndrew Jones int dt_for_each_cpu_node(void (*func)(int fdtnode, u32 regval, void *info),
193f7f76b85SAndrew Jones 			 void *info)
194f7f76b85SAndrew Jones {
195f7f76b85SAndrew Jones 	const struct fdt_property *prop;
196f7f76b85SAndrew Jones 	int cpus, cpu, ret, len;
197f7f76b85SAndrew Jones 	struct dt_reg raw_reg;
198f7f76b85SAndrew Jones 	u32 nac, nsc;
199f7f76b85SAndrew Jones 
200f7f76b85SAndrew Jones 	cpus = fdt_path_offset(fdt, "/cpus");
201f7f76b85SAndrew Jones 	if (cpus < 0)
202f7f76b85SAndrew Jones 		return cpus;
203f7f76b85SAndrew Jones 
204f7f76b85SAndrew Jones 	ret = dt_get_nr_cells(cpus, &nac, &nsc);
205f7f76b85SAndrew Jones 	if (ret < 0)
206f7f76b85SAndrew Jones 		return ret;
207f7f76b85SAndrew Jones 
208f7f76b85SAndrew Jones 	dt_reg_init(&raw_reg, nac, nsc);
209f7f76b85SAndrew Jones 
210f7f76b85SAndrew Jones 	dt_for_each_subnode(cpus, cpu) {
211f7f76b85SAndrew Jones 
212f7f76b85SAndrew Jones 		prop = fdt_get_property(fdt, cpu, "device_type", &len);
213f7f76b85SAndrew Jones 		if (prop == NULL)
214f7f76b85SAndrew Jones 			return len;
215f7f76b85SAndrew Jones 
216f7f76b85SAndrew Jones 		if (len != 4 || strcmp((char *)prop->data, "cpu"))
217f7f76b85SAndrew Jones 			continue;
218f7f76b85SAndrew Jones 
219f7f76b85SAndrew Jones 		ret = dt_get_reg(cpu, 0, &raw_reg);
220f7f76b85SAndrew Jones 		if (ret < 0)
221f7f76b85SAndrew Jones 			return ret;
222f7f76b85SAndrew Jones 
223f7f76b85SAndrew Jones 		func(cpu, raw_reg.address_cells[0], info);
224f7f76b85SAndrew Jones 	}
225f7f76b85SAndrew Jones 
226f7f76b85SAndrew Jones 	return 0;
227f7f76b85SAndrew Jones }
228f7f76b85SAndrew Jones 
229f7f76b85SAndrew Jones int dt_get_bootargs(const char **bootargs)
230f7f76b85SAndrew Jones {
231f7f76b85SAndrew Jones 	const struct fdt_property *prop;
232f7f76b85SAndrew Jones 	int node, len;
233f7f76b85SAndrew Jones 
234f7f76b85SAndrew Jones 	*bootargs = NULL;
235f7f76b85SAndrew Jones 
236f7f76b85SAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
237f7f76b85SAndrew Jones 	if (node < 0)
238f7f76b85SAndrew Jones 		return node;
239f7f76b85SAndrew Jones 
240f7f76b85SAndrew Jones 	prop = fdt_get_property(fdt, node, "bootargs", &len);
241f7f76b85SAndrew Jones 	if (prop)
242f7f76b85SAndrew Jones 		*bootargs = prop->data;
243f7f76b85SAndrew Jones 	else if (len < 0 && len != -FDT_ERR_NOTFOUND)
244f7f76b85SAndrew Jones 		return len;
245f7f76b85SAndrew Jones 
246f7f76b85SAndrew Jones 	return 0;
247f7f76b85SAndrew Jones }
248f7f76b85SAndrew Jones 
249*7e29db9fSAndrew Jones int dt_get_default_console_node(void)
250*7e29db9fSAndrew Jones {
251*7e29db9fSAndrew Jones 	const struct fdt_property *prop;
252*7e29db9fSAndrew Jones 	int node, len;
253*7e29db9fSAndrew Jones 
254*7e29db9fSAndrew Jones 	node = fdt_path_offset(fdt, "/chosen");
255*7e29db9fSAndrew Jones 	if (node < 0)
256*7e29db9fSAndrew Jones 		return node;
257*7e29db9fSAndrew Jones 
258*7e29db9fSAndrew Jones 	prop = fdt_get_property(fdt, node, "stdout-path", &len);
259*7e29db9fSAndrew Jones 	if (!prop) {
260*7e29db9fSAndrew Jones 		prop = fdt_get_property(fdt, node, "linux,stdout-path", &len);
261*7e29db9fSAndrew Jones 		if (!prop)
262*7e29db9fSAndrew Jones 			return len;
263*7e29db9fSAndrew Jones 	}
264*7e29db9fSAndrew Jones 
265*7e29db9fSAndrew Jones 	return fdt_path_offset(fdt, prop->data);
266*7e29db9fSAndrew Jones }
267*7e29db9fSAndrew Jones 
268f7f76b85SAndrew Jones int dt_init(const void *fdt_ptr)
269f7f76b85SAndrew Jones {
270f7f76b85SAndrew Jones 	struct dt_bus *defbus = (struct dt_bus *)&dt_default_bus;
271f7f76b85SAndrew Jones 	int root, ret;
272f7f76b85SAndrew Jones 
273f7f76b85SAndrew Jones 	ret = fdt_check_header(fdt_ptr);
274f7f76b85SAndrew Jones 	if (ret < 0)
275f7f76b85SAndrew Jones 		return ret;
276f7f76b85SAndrew Jones 	fdt = fdt_ptr;
277f7f76b85SAndrew Jones 
278f7f76b85SAndrew Jones 	root = fdt_path_offset(fdt, "/");
279f7f76b85SAndrew Jones 	if (root < 0)
280f7f76b85SAndrew Jones 		return root;
281f7f76b85SAndrew Jones 
282f7f76b85SAndrew Jones 	ret = dt_get_nr_cells(root, &root_nr_address_cells,
283f7f76b85SAndrew Jones 				    &root_nr_size_cells);
284f7f76b85SAndrew Jones 	if (ret < 0)
285f7f76b85SAndrew Jones 		return ret;
286f7f76b85SAndrew Jones 
287f7f76b85SAndrew Jones 	defbus->nr_address_cells = root_nr_address_cells;
288f7f76b85SAndrew Jones 	defbus->nr_size_cells = root_nr_size_cells;
289f7f76b85SAndrew Jones 
290f7f76b85SAndrew Jones 	return 0;
291f7f76b85SAndrew Jones }
292