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