xref: /haiku/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp (revision 7bbfd0ff4956fd50e18cfb6f6f994f3f9446d77d)
1 /*
2  * Copyright 2014, Ithamar R. Adema <ithamar@upgrade-android.com>
3  * All rights reserved. Distributed under the terms of the MIT License.
4  *
5  * Copyright 2015-2021, Haiku, Inc. All rights reserved.
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include <drivers/bus/FDT.h>
11 #include <KernelExport.h>
12 #include <util/kernel_cpp.h>
13 #include <device_manager.h>
14 
15 #include <AutoDeleter.h>
16 #include <AutoDeleterDrivers.h>
17 #include <HashMap.h>
18 #include <debug.h>
19 
20 extern "C" {
21 #include <libfdt_env.h>
22 #include <fdt.h>
23 #include <libfdt.h>
24 };
25 
26 
27 //#define TRACE_FDT
28 #ifdef TRACE_FDT
29 #define TRACE(x...) dprintf(x)
30 #else
31 #define TRACE(x...)
32 #endif
33 
34 
35 extern void* gFDT;
36 
37 device_manager_info* gDeviceManager;
38 
39 extern fdt_bus_module_info gBusModule;
40 extern fdt_device_module_info gDeviceModule;
41 
42 
43 //#pragma mark -
44 
45 
46 struct fdt_bus {
47 	device_node* node;
48 	HashMap<HashKey32<int32>, device_node*> phandles;
49 };
50 
51 
52 struct fdt_device {
53 	device_node* node;
54 	device_node* bus;
55 };
56 
57 
58 static status_t
59 fdt_register_node(fdt_bus* bus, int node, device_node* parentDev,
60 	device_node*& curDev)
61 {
62 	TRACE("%s('%s', %p)\n", __func__, fdt_get_name(gFDT, node, NULL),
63 		parentDev);
64 
65 	const void* prop; int propLen;
66 	device_attr attrs[8];
67 	device_attr* attr = attrs;
68 	int nameLen = 0;
69 	const char *name = fdt_get_name(gFDT, node, &nameLen);
70 
71 	if (name == NULL) {
72 		dprintf("%s ERROR: fdt_get_name: %s\n", __func__,
73 			fdt_strerror(nameLen));
74 		return B_ERROR;
75 	}
76 
77 	*attr++ = (device_attr) { B_DEVICE_BUS, B_STRING_TYPE, {string: "fdt"}};
78 	*attr++ = (device_attr) { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
79 		{ string: (strcmp(name, "") != 0) ? name : "Root" } };
80 	*attr++ = (device_attr) { "fdt/node", B_UINT32_TYPE, {ui32: (uint32)node}};
81 	*attr++ = (device_attr) { "fdt/name", B_STRING_TYPE, {string: name}};
82 
83 	prop = fdt_getprop(gFDT, node, "device_type", &propLen);
84 	if (prop != NULL) {
85 		*attr++ = (device_attr) { "fdt/device_type", B_STRING_TYPE,
86 			{ string: (const char*)prop } };
87 	}
88 
89 	prop = fdt_getprop(gFDT, node, "compatible", &propLen);
90 
91 	if (prop != NULL) {
92 		*attr++ = (device_attr){ "fdt/compatible", B_STRING_TYPE,
93 			{ string: (const char*)prop } };
94 	}
95 
96 	*attr = {0};
97 
98 	status_t res = gDeviceManager->register_node(parentDev,
99 		"bus_managers/fdt/driver_v1", attrs, NULL, &curDev);
100 
101 	if (res < B_OK)
102 		return res;
103 
104 	prop = fdt_getprop(gFDT, node, "phandle", &propLen);
105 
106 	if (prop != NULL)
107 		bus->phandles.Put(fdt32_to_cpu(*(uint32_t*)prop), curDev);
108 
109 	return B_OK;
110 }
111 
112 
113 static void
114 fdt_traverse(fdt_bus* bus, int &node, int &depth, device_node* parentDev)
115 {
116 	int curDepth = depth;
117 #if 0
118 	for (int i = 0; i < depth; i++) dprintf("  ");
119 	dprintf("node('%s')\n", fdt_get_name(gFDT, node, NULL));
120 #endif
121 	device_node* curDev;
122 	fdt_register_node(bus, node, parentDev, curDev);
123 
124 	node = fdt_next_node(gFDT, node, &depth);
125 	while (node >= 0 && depth == curDepth + 1) {
126 		fdt_traverse(bus, node, depth, curDev);
127 	}
128 }
129 
130 
131 //#pragma mark bus
132 
133 static int32
134 fdt_bus_std_ops(int32 op, ...)
135 {
136 	switch (op) {
137 		case B_MODULE_INIT:
138 			TRACE("fdt root init\n");
139 			return B_OK;
140 
141 		case B_MODULE_UNINIT:
142 			TRACE("fdt root uninit\n");
143 			return B_OK;
144 	}
145 
146 	return B_BAD_VALUE;
147 }
148 
149 
150 static float
151 fdt_bus_supports_device(device_node* parent)
152 {
153 	TRACE("fdt_bus_supports_device\n");
154 
155 	// make sure parent is really device root
156 	const char* bus;
157 	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
158 		return B_ERROR;
159 
160 	if (strcmp(bus, "root"))
161 		return 0.0;
162 
163 	return 1.0;
164 }
165 
166 
167 static status_t
168 fdt_bus_register_device(device_node* parent)
169 {
170 	TRACE("+fdt_bus_register_device\n");
171 	struct ScopeExit {
172 		ScopeExit() {TRACE("-fdt_bus_register_device\n");}
173 	} scopeExit;
174 
175 	device_attr attrs[] = {
176 		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "FDT"}},
177 		{B_DEVICE_FLAGS, B_UINT32_TYPE, {ui32: B_KEEP_DRIVER_LOADED}},
178 		{}
179 	};
180 
181 	return gDeviceManager->register_node(
182 		parent, "bus_managers/fdt/root/driver_v1", attrs, NULL, NULL);
183 }
184 
185 
186 static status_t
187 fdt_bus_init(device_node* node, void** cookie)
188 {
189 	TRACE("fdt_bus_init\n");
190 
191 	if (gFDT == NULL) {
192 		TRACE("FDT is NULL!\n");
193 		return B_DEVICE_NOT_FOUND;
194 	}
195 
196 	ObjectDeleter<fdt_bus> bus(new(std::nothrow) fdt_bus());
197 	if (!bus.IsSet())
198 		return B_NO_MEMORY;
199 
200 	bus->node = node;
201 	*cookie = bus.Detach();
202 	return B_OK;
203 }
204 
205 
206 static void
207 fdt_bus_uninit(void* cookie)
208 {
209 	TRACE("fdt_bus_uninit\n");
210 
211 	ObjectDeleter<fdt_bus> bus((fdt_bus*)cookie);
212 }
213 
214 
215 static status_t
216 fdt_bus_register_child_devices(void* cookie)
217 {
218 	TRACE("fdt_bus_register_child_devices\n");
219 
220 	fdt_bus* bus = (fdt_bus*)cookie;
221 
222 	int node = -1, depth = -1;
223 	node = fdt_next_node(gFDT, node, &depth);
224 	fdt_traverse(bus, node, depth, bus->node);
225 
226 	return B_OK;
227 }
228 
229 
230 device_node*
231 fdt_bus_node_by_phandle(fdt_bus* bus, int phandle)
232 {
233 	ASSERT(bus != NULL);
234 
235 	device_node** devNode;
236 	if (!bus->phandles.Get(phandle, devNode))
237 		return NULL;
238 
239 	return *devNode;
240 }
241 
242 
243 //#pragma mark device
244 
245 
246 static status_t
247 fdt_device_std_ops(int32 op, ...)
248 {
249 	switch (op) {
250 		case B_MODULE_INIT:
251 		case B_MODULE_UNINIT:
252 			return B_OK;
253 	}
254 
255 	return B_BAD_VALUE;
256 }
257 
258 
259 static status_t
260 fdt_device_init_driver(device_node* node, void** cookie)
261 {
262 	TRACE("fdt_device_init_driver()\n");
263 
264 	ObjectDeleter<fdt_device> dev(new(std::nothrow) fdt_device());
265 	if (!dev.IsSet())
266 		return B_NO_MEMORY;
267 
268 	dev->node = node;
269 
270 	// get bus from parent node
271 	DeviceNodePutter<&gDeviceManager> parent(
272 		gDeviceManager->get_parent_node(node));
273 	driver_module_info* parentModule;
274 	void* parentDev;
275 	ASSERT(gDeviceManager->get_driver(
276 		parent.Get(), &parentModule, &parentDev) >= B_OK);
277 	if (parentModule == (driver_module_info*)&gDeviceModule)
278 		dev->bus = ((fdt_device*)parentDev)->bus;
279 	else if (parentModule == (driver_module_info*)&gBusModule)
280 		dev->bus = parent.Get();
281 	else
282 		panic("bad parent node");
283 
284 	*cookie = dev.Detach();
285 	return B_OK;
286 }
287 
288 
289 static void
290 fdt_device_uninit_driver(void* cookie)
291 {
292 	TRACE("fdt_device_uninit_driver()\n");
293 	ObjectDeleter<fdt_device> dev((fdt_device*)cookie);
294 }
295 
296 
297 static status_t
298 fdt_device_register_child_devices(void* cookie)
299 {
300 	TRACE("fdt_device_register_child_devices()\n");
301 	return B_OK;
302 }
303 
304 
305 static device_node*
306 fdt_device_get_bus(fdt_device* dev)
307 {
308 	ASSERT(dev != NULL);
309 	return dev->bus;
310 }
311 
312 
313 static const char*
314 fdt_device_get_name(fdt_device* dev)
315 {
316 	ASSERT(dev != NULL);
317 
318 	uint32 fdtNode;
319 	ASSERT(gDeviceManager->get_attr_uint32(
320 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
321 
322 	return fdt_get_name(gFDT, (int)fdtNode, NULL);
323 }
324 
325 
326 static const void*
327 fdt_device_get_prop(fdt_device* dev, const char* name, int* len)
328 {
329 	ASSERT(dev != NULL);
330 
331 	uint32 fdtNode;
332 	ASSERT(gDeviceManager->get_attr_uint32(
333 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
334 
335 	return fdt_getprop(gFDT, (int)fdtNode, name, len);
336 }
337 
338 
339 static bool
340 fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len)
341 {
342 	ASSERT(dev != NULL);
343 
344 	uint32 fdtNode;
345 	ASSERT(gDeviceManager->get_attr_uint32(
346 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
347 
348 	int propLen;
349 	const void* prop = fdt_getprop(gFDT, (int)fdtNode, "reg", &propLen);
350 	if (prop == NULL)
351 		return false;
352 
353 	// TODO: use '#address-cells', '#size-cells' in parent node to identify
354 	// field sizes
355 
356 	if ((ord + 1)*16 > (uint32)propLen)
357 		return false;
358 
359 	if (regs != NULL)
360 		*regs = fdt64_to_cpu(*(((uint64*)prop) + 2*ord));
361 
362 	if (len != NULL)
363 		*len = fdt64_to_cpu(*(((uint64*)prop) + 2*ord + 1));
364 
365 	return true;
366 }
367 
368 
369 static uint32
370 fdt_get_interrupt_parent(fdt_device* dev, int node)
371 {
372 	while (node >= 0) {
373 		uint32* prop;
374 		int propLen;
375 		prop = (uint32*)fdt_getprop(gFDT, node, "interrupt-parent", &propLen);
376 		if (prop != NULL && propLen == 4) {
377 			return fdt32_to_cpu(*prop);
378 		}
379 
380 		node = fdt_parent_offset(gFDT, node);
381 	}
382 
383 	return 0;
384 }
385 
386 
387 static uint32
388 fdt_get_interrupt_cells(uint32 interrupt_parent_phandle)
389 {
390 	if (interrupt_parent_phandle > 0) {
391 		int node = fdt_node_offset_by_phandle(gFDT, interrupt_parent_phandle);
392 		if (node >= 0) {
393 			uint32* prop;
394 			int propLen;
395 			prop  = (uint32*)fdt_getprop(gFDT, node, "#interrupt-cells", &propLen);
396 			if (prop != NULL && propLen == 4) {
397 				return fdt32_to_cpu(*prop);
398 			}
399 		}
400 	}
401 	return 1;
402 }
403 
404 
405 static bool
406 fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
407 	device_node** interruptController, uint64* interrupt)
408 {
409 	ASSERT(dev != NULL);
410 
411 	uint32 fdtNode;
412 	ASSERT(gDeviceManager->get_attr_uint32(
413 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
414 
415 	int propLen;
416 	const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
417 		&propLen);
418 	if (prop == NULL) {
419 		uint32 interruptParent = fdt_get_interrupt_parent(dev, fdtNode);
420 		uint32 interruptCells = fdt_get_interrupt_cells(interruptParent);
421 
422 		prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
423 			&propLen);
424 		if (prop == NULL)
425 			return false;
426 
427 		if ((ord + 1) * interruptCells * sizeof(uint32) > (uint32)propLen)
428 			return false;
429 
430 		uint32 offs;
431 		if (interruptCells == 3) {
432 			offs = 3 * ord + 1;
433 		} else {
434 			offs = interruptCells * ord;
435 		}
436 
437 		if (interrupt != NULL)
438 			*interrupt = fdt32_to_cpu(*(((uint32*)prop) + offs));
439 
440 		if (interruptController != NULL && interruptParent != 0) {
441 			fdt_bus* bus;
442 			ASSERT(gDeviceManager->get_driver(dev->bus, NULL, (void**)&bus) >= B_OK);
443 			*interruptController = fdt_bus_node_by_phandle(bus, interruptParent);
444 		}
445 
446 		return true;
447 	}
448 
449 	if ((ord + 1) * 8 > (uint32)propLen)
450 		return false;
451 
452 	if (interruptController != NULL) {
453 		uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2 * ord));
454 
455 		fdt_bus* bus;
456 		ASSERT(gDeviceManager->get_driver(
457 			dev->bus, NULL, (void**)&bus) >= B_OK);
458 
459 		*interruptController = fdt_bus_node_by_phandle(bus, phandle);
460 	}
461 
462 	if (interrupt != NULL)
463 		*interrupt = fdt32_to_cpu(*(((uint32*)prop) + 2*ord + 1));
464 
465 	return true;
466 }
467 
468 
469 //#pragma mark -
470 
471 fdt_bus_module_info gBusModule = {
472 	{
473 		{
474 			"bus_managers/fdt/root/driver_v1",
475 			0,
476 			fdt_bus_std_ops
477 		},
478 		fdt_bus_supports_device,
479 		fdt_bus_register_device,
480 		fdt_bus_init,
481 		fdt_bus_uninit,
482 		fdt_bus_register_child_devices,
483 		NULL,	// rescan devices
484 		NULL,	// device removed
485 	},
486 	fdt_bus_node_by_phandle,
487 };
488 
489 
490 fdt_device_module_info gDeviceModule = {
491 	{
492 		{
493 			"bus_managers/fdt/driver_v1",
494 			0,
495 			fdt_device_std_ops
496 		},
497 
498 		NULL,		// supports device
499 		NULL,		// register device (our parent registered us)
500 		fdt_device_init_driver,
501 		fdt_device_uninit_driver,
502 		fdt_device_register_child_devices,
503 		NULL,		// rescan devices
504 		NULL,		// device removed
505 	},
506 	fdt_device_get_bus,
507 	fdt_device_get_name,
508 	fdt_device_get_prop,
509 	fdt_device_get_reg,
510 	fdt_device_get_interrupt,
511 };
512 
513 
514 module_info* modules[] = {
515 	(module_info*)&gBusModule,
516 	(module_info*)&gDeviceModule,
517 	NULL
518 };
519 
520 module_dependency module_dependencies[] = {
521 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
522 	{ NULL }
523 };
524