xref: /haiku/src/add-ons/kernel/bus_managers/fdt/fdt_module.cpp (revision 3d4afef9cba2f328e238089d4609d00d4b1524f3)
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 <fdt.h>
22 #include <libfdt.h>
23 #include <libfdt_env.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 
69 	const char *name = fdt_get_name(gFDT, node, NULL);
70 
71 	*attr++ = (device_attr) { B_DEVICE_BUS, B_STRING_TYPE, {string: "fdt"}};
72 	*attr++ = (device_attr) { B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
73 		{ string: (strcmp(name, "") != 0) ? name : "Root" } };
74 	*attr++ = (device_attr) { "fdt/node", B_UINT32_TYPE, {ui32: (uint32)node}};
75 	*attr++ = (device_attr) { "fdt/name", B_STRING_TYPE, {string: name}};
76 
77 	prop = fdt_getprop(gFDT, node, "device_type", &propLen);
78 	if (prop != NULL) {
79 		*attr++ = (device_attr) { "fdt/device_type", B_STRING_TYPE,
80 			{ string: (const char*)prop } };
81 	}
82 
83 	prop = fdt_getprop(gFDT, node, "compatible", &propLen);
84 
85 	if (prop != NULL) {
86 		*attr++ = (device_attr){ "fdt/compatible", B_STRING_TYPE,
87 			{ string: (const char*)prop } };
88 	}
89 
90 	*attr = {0};
91 
92 	status_t res = gDeviceManager->register_node(parentDev,
93 		"bus_managers/fdt/driver_v1", attrs, NULL, &curDev);
94 
95 	if (res < B_OK)
96 		return res;
97 
98 	prop = fdt_getprop(gFDT, node, "phandle", &propLen);
99 
100 	if (prop != NULL)
101 		bus->phandles.Put(fdt32_to_cpu(*(uint32_t*)prop), curDev);
102 
103 	return B_OK;
104 }
105 
106 
107 static void
108 fdt_traverse(fdt_bus* bus, int &node, int &depth, device_node* parentDev)
109 {
110 	int curDepth = depth;
111 #if 0
112 	for (int i = 0; i < depth; i++) dprintf("  ");
113 	dprintf("node('%s')\n", fdt_get_name(gFDT, node, NULL));
114 #endif
115 	device_node* curDev;
116 	fdt_register_node(bus, node, parentDev, curDev);
117 
118 	node = fdt_next_node(gFDT, node, &depth);
119 	while (node >= 0 && depth == curDepth + 1) {
120 		fdt_traverse(bus, node, depth, curDev);
121 	}
122 }
123 
124 
125 //#pragma mark bus
126 
127 static int32
128 fdt_bus_std_ops(int32 op, ...)
129 {
130 	switch (op) {
131 		case B_MODULE_INIT:
132 			TRACE("fdt root init\n");
133 			return B_OK;
134 
135 		case B_MODULE_UNINIT:
136 			TRACE("fdt root uninit\n");
137 			return B_OK;
138 	}
139 
140 	return B_BAD_VALUE;
141 }
142 
143 
144 static float
145 fdt_bus_supports_device(device_node* parent)
146 {
147 	TRACE("fdt_bus_supports_device\n");
148 
149 	// make sure parent is really device root
150 	const char* bus;
151 	if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
152 		return B_ERROR;
153 
154 	if (strcmp(bus, "root"))
155 		return 0.0;
156 
157 	return 1.0;
158 }
159 
160 
161 static status_t
162 fdt_bus_register_device(device_node* parent)
163 {
164 	TRACE("+fdt_bus_register_device\n");
165 	struct ScopeExit {
166 		ScopeExit() {TRACE("-fdt_bus_register_device\n");}
167 	} scopeExit;
168 
169 	device_attr attrs[] = {
170 		{B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {string: "FDT"}},
171 		{B_DEVICE_FLAGS, B_UINT32_TYPE, {ui32: B_KEEP_DRIVER_LOADED}},
172 		{}
173 	};
174 
175 	return gDeviceManager->register_node(
176 		parent, "bus_managers/fdt/root/driver_v1", attrs, NULL, NULL);
177 }
178 
179 
180 static status_t
181 fdt_bus_init(device_node* node, void** cookie)
182 {
183 	TRACE("fdt_bus_init\n");
184 
185 	ObjectDeleter<fdt_bus> bus(new(std::nothrow) fdt_bus());
186 	if (!bus.IsSet())
187 		return B_NO_MEMORY;
188 
189 	bus->node = node;
190 	*cookie = bus.Detach();
191 	return B_OK;
192 }
193 
194 
195 static void
196 fdt_bus_uninit(void* cookie)
197 {
198 	TRACE("fdt_bus_uninit\n");
199 
200 	ObjectDeleter<fdt_bus> bus((fdt_bus*)cookie);
201 }
202 
203 
204 static status_t
205 fdt_bus_register_child_devices(void* cookie)
206 {
207 	TRACE("fdt_bus_register_child_devices\n");
208 
209 	fdt_bus* bus = (fdt_bus*)cookie;
210 
211 	int node = -1, depth = -1;
212 	node = fdt_next_node(gFDT, node, &depth);
213 	fdt_traverse(bus, node, depth, bus->node);
214 
215 	return B_OK;
216 }
217 
218 
219 device_node*
220 fdt_bus_node_by_phandle(fdt_bus* bus, int phandle)
221 {
222 	ASSERT(bus != NULL);
223 
224 	device_node** devNode;
225 	if (!bus->phandles.Get(phandle, devNode))
226 		return NULL;
227 
228 	return *devNode;
229 }
230 
231 
232 //#pragma mark device
233 
234 
235 static status_t
236 fdt_device_std_ops(int32 op, ...)
237 {
238 	switch (op) {
239 		case B_MODULE_INIT:
240 		case B_MODULE_UNINIT:
241 			return B_OK;
242 	}
243 
244 	return B_BAD_VALUE;
245 }
246 
247 
248 static status_t
249 fdt_device_init_driver(device_node* node, void** cookie)
250 {
251 	TRACE("fdt_device_init_driver()\n");
252 
253 	ObjectDeleter<fdt_device> dev(new(std::nothrow) fdt_device());
254 	if (!dev.IsSet())
255 		return B_NO_MEMORY;
256 
257 	dev->node = node;
258 
259 	// get bus from parent node
260 	DeviceNodePutter<&gDeviceManager> parent(
261 		gDeviceManager->get_parent_node(node));
262 	driver_module_info* parentModule;
263 	void* parentDev;
264 	ASSERT(gDeviceManager->get_driver(
265 		parent.Get(), &parentModule, &parentDev) >= B_OK);
266 	if (parentModule == (driver_module_info*)&gDeviceModule)
267 		dev->bus = ((fdt_device*)parentDev)->bus;
268 	else if (parentModule == (driver_module_info*)&gBusModule)
269 		dev->bus = parent.Get();
270 	else
271 		panic("bad parent node");
272 
273 	*cookie = dev.Detach();
274 	return B_OK;
275 }
276 
277 
278 static void
279 fdt_device_uninit_driver(void* cookie)
280 {
281 	TRACE("fdt_device_uninit_driver()\n");
282 	ObjectDeleter<fdt_device> dev((fdt_device*)cookie);
283 }
284 
285 
286 static status_t
287 fdt_device_register_child_devices(void* cookie)
288 {
289 	TRACE("fdt_device_register_child_devices()\n");
290 	return B_OK;
291 }
292 
293 
294 static device_node*
295 fdt_device_get_bus(fdt_device* dev)
296 {
297 	ASSERT(dev != NULL);
298 	return dev->bus;
299 }
300 
301 
302 static const char*
303 fdt_device_get_name(fdt_device* dev)
304 {
305 	ASSERT(dev != NULL);
306 
307 	uint32 fdtNode;
308 	ASSERT(gDeviceManager->get_attr_uint32(
309 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
310 
311 	return fdt_get_name(gFDT, (int)fdtNode, NULL);
312 }
313 
314 
315 static const void*
316 fdt_device_get_prop(fdt_device* dev, const char* name, int* len)
317 {
318 	ASSERT(dev != NULL);
319 
320 	uint32 fdtNode;
321 	ASSERT(gDeviceManager->get_attr_uint32(
322 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
323 
324 	return fdt_getprop(gFDT, (int)fdtNode, name, len);
325 }
326 
327 
328 static bool
329 fdt_device_get_reg(fdt_device* dev, uint32 ord, uint64* regs, uint64* len)
330 {
331 	ASSERT(dev != NULL);
332 
333 	uint32 fdtNode;
334 	ASSERT(gDeviceManager->get_attr_uint32(
335 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
336 
337 	int propLen;
338 	const void* prop = fdt_getprop(gFDT, (int)fdtNode, "reg", &propLen);
339 	if (prop == NULL)
340 		return false;
341 
342 	// TODO: use '#address-cells', '#size-cells' in parent node to identify
343 	// field sizes
344 
345 	if ((ord + 1)*16 > (uint32)propLen)
346 		return false;
347 
348 	if (regs != NULL)
349 		*regs = fdt64_to_cpu(*(((uint64*)prop) + 2*ord));
350 
351 	if (len != NULL)
352 		*len = fdt64_to_cpu(*(((uint64*)prop) + 2*ord + 1));
353 
354 	return true;
355 }
356 
357 
358 static bool
359 fdt_device_get_interrupt(fdt_device* dev, uint32 ord,
360 	device_node** interruptController, uint64* interrupt)
361 {
362 	ASSERT(dev != NULL);
363 
364 	uint32 fdtNode;
365 	ASSERT(gDeviceManager->get_attr_uint32(
366 		dev->node, "fdt/node", &fdtNode, false) >= B_OK);
367 
368 	// TODO: handle other interrupt encodings
369 	int propLen;
370 	const void* prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts-extended",
371 		&propLen);
372 	if (prop == NULL) {
373 		prop = fdt_getprop(gFDT, (int)fdtNode, "interrupts",
374 			&propLen);
375 		if (prop == NULL)
376 			return false;
377 
378 		if ((ord + 1)*4 > (uint32)propLen)
379 			return false;
380 
381 		if (interrupt != NULL)
382 			*interrupt = fdt32_to_cpu(*(((uint32*)prop) + ord));
383 
384 		if (interruptController != NULL) {
385 			prop = fdt_getprop(gFDT, (int)fdtNode, "interrupt-parent",
386 				&propLen);
387 			if (prop != NULL && propLen == 4) {
388 				uint32 phandle = fdt32_to_cpu(*(uint32*)prop);
389 
390 				fdt_bus* bus;
391 				ASSERT(gDeviceManager->get_driver(
392 					dev->bus, NULL, (void**)&bus) >= B_OK);
393 
394 				*interruptController = fdt_bus_node_by_phandle(bus, phandle);
395 			}
396 		}
397 		return true;
398 	}
399 
400 	// TODO: use '#interrupt-cells' to identify field sizes
401 
402 	if ((ord + 1)*8 > (uint32)propLen)
403 		return false;
404 
405 	if (interruptController != NULL) {
406 		uint32 phandle = fdt32_to_cpu(*(((uint32*)prop) + 2*ord));
407 
408 		fdt_bus* bus;
409 		ASSERT(gDeviceManager->get_driver(
410 			dev->bus, NULL, (void**)&bus) >= B_OK);
411 
412 		*interruptController = fdt_bus_node_by_phandle(bus, phandle);
413 	}
414 
415 	if (interrupt != NULL)
416 		*interrupt = fdt32_to_cpu(*(((uint32*)prop) + 2*ord + 1));
417 
418 	return true;
419 }
420 
421 
422 //#pragma mark -
423 
424 fdt_bus_module_info gBusModule = {
425 	{
426 		{
427 			"bus_managers/fdt/root/driver_v1",
428 			0,
429 			fdt_bus_std_ops
430 		},
431 		fdt_bus_supports_device,
432 		fdt_bus_register_device,
433 		fdt_bus_init,
434 		fdt_bus_uninit,
435 		fdt_bus_register_child_devices,
436 		NULL,	// rescan devices
437 		NULL,	// device removed
438 	},
439 	fdt_bus_node_by_phandle,
440 };
441 
442 
443 fdt_device_module_info gDeviceModule = {
444 	{
445 		{
446 			"bus_managers/fdt/driver_v1",
447 			0,
448 			fdt_device_std_ops
449 		},
450 
451 		NULL,		// supports device
452 		NULL,		// register device (our parent registered us)
453 		fdt_device_init_driver,
454 		fdt_device_uninit_driver,
455 		fdt_device_register_child_devices,
456 		NULL,		// rescan devices
457 		NULL,		// device removed
458 	},
459 	fdt_device_get_bus,
460 	fdt_device_get_name,
461 	fdt_device_get_prop,
462 	fdt_device_get_reg,
463 	fdt_device_get_interrupt,
464 };
465 
466 
467 module_info* modules[] = {
468 	(module_info*)&gBusModule,
469 	(module_info*)&gDeviceModule,
470 	NULL
471 };
472 
473 module_dependency module_dependencies[] = {
474 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager },
475 	{ NULL }
476 };
477