xref: /haiku/src/libs/compat/freebsd_network/bus.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2007, Hugo Santos. All Rights Reserved.
3  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 extern "C" {
9 #include "device.h"
10 }
11 
12 #include <cstdlib>
13 #include <PCI_x86.h>
14 
15 #include <arch/cpu.h>
16 #include <int.h>
17 
18 extern "C" {
19 #include <compat/dev/pci/pcireg.h>
20 #include <compat/dev/pci/pcivar.h>
21 #include <compat/machine/resource.h>
22 #include <compat/sys/mutex.h>
23 #include <compat/machine/bus.h>
24 #include <compat/sys/rman.h>
25 #include <compat/sys/bus.h>
26 }
27 
28 
29 //#define DEBUG_BUS_SPACE_RW
30 #ifdef DEBUG_BUS_SPACE_RW
31 #	define TRACE_BUS_SPACE_RW(x) driver_printf x
32 #else
33 #	define TRACE_BUS_SPACE_RW(x)
34 #endif
35 
36 
37 struct internal_intr {
38 	device_t		dev;
39 	driver_filter_t* filter;
40 	driver_intr_t	*handler;
41 	void			*arg;
42 	int				irq;
43 	uint32			flags;
44 
45 	thread_id		thread;
46 	sem_id			sem;
47 	int32			handling;
48 };
49 
50 static int32 intr_wrapper(void *data);
51 
52 
53 static area_id
54 map_mem(void **virtualAddr, phys_addr_t _phy, size_t size, uint32 protection,
55 	const char *name)
56 {
57 	uint32 offset = _phy & (B_PAGE_SIZE - 1);
58 	phys_addr_t physicalAddr = _phy - offset;
59 	area_id area;
60 
61 	size = roundup(size + offset, B_PAGE_SIZE);
62 	area = map_physical_memory(name, physicalAddr, size, B_ANY_KERNEL_ADDRESS,
63 		protection, virtualAddr);
64 	if (area < B_OK)
65 		return area;
66 
67 	*virtualAddr = (uint8 *)(*virtualAddr) + offset;
68 
69 	return area;
70 }
71 
72 
73 static int
74 bus_alloc_irq_resource(device_t dev, struct resource *res)
75 {
76 	uint8 irq = pci_read_config(dev, PCI_interrupt_line, 1);
77 	if (irq == 0 || irq == 0xff)
78 		return -1;
79 
80 	res->r_bustag = BUS_SPACE_TAG_IRQ;
81 	res->r_bushandle = irq;
82 	return 0;
83 }
84 
85 
86 static int
87 bus_alloc_mem_resource(device_t dev, struct resource *res, pci_info *info,
88 	int bar_index)
89 {
90 	phys_addr_t addr = info->u.h0.base_registers[bar_index];
91 	uint64 size = info->u.h0.base_register_sizes[bar_index];
92 	uchar flags = info->u.h0.base_register_flags[bar_index];
93 
94 	// reject empty regions
95 	if (size == 0)
96 		return -1;
97 
98 	// reject I/O space
99 	if ((flags & PCI_address_space) != 0)
100 		return -1;
101 
102 	// TODO: check flags & PCI_address_prefetchable ?
103 
104 	if ((flags & PCI_address_type) == PCI_address_type_64) {
105 		addr |= (uint64)info->u.h0.base_registers[bar_index + 1] << 32;
106 		size |= (uint64)info->u.h0.base_register_sizes[bar_index + 1] << 32;
107 	}
108 
109 	// enable this I/O resource
110 	if (pci_enable_io(dev, SYS_RES_MEMORY) != 0)
111 		return -1;
112 
113 	void *virtualAddr;
114 
115 	res->r_mapped_area = map_mem(&virtualAddr, addr, size,
116 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, "bus_alloc_resource(MEMORY)");
117 	if (res->r_mapped_area < B_OK)
118 		return -1;
119 
120 	res->r_bustag = BUS_SPACE_TAG_MEM;
121 	res->r_bushandle = (bus_space_handle_t)virtualAddr;
122 	return 0;
123 }
124 
125 
126 static int
127 bus_alloc_ioport_resource(device_t dev, struct resource *res, pci_info *info,
128 	int bar_index)
129 {
130 	uint32 size = info->u.h0.base_register_sizes[bar_index];
131 	uchar flags = info->u.h0.base_register_flags[bar_index];
132 
133 	// reject empty regions
134 	if (size == 0)
135 		return -1;
136 
137 	// reject memory space
138 	if ((flags & PCI_address_space) == 0)
139 		return -1;
140 
141 	// enable this I/O resource
142 	if (pci_enable_io(dev, SYS_RES_IOPORT) != 0)
143 		return -1;
144 
145 	res->r_bustag = BUS_SPACE_TAG_IO;
146 	res->r_bushandle = info->u.h0.base_registers[bar_index];
147 	return 0;
148 }
149 
150 
151 static int
152 bus_register_to_bar_index(pci_info *info, int regid)
153 {
154 	// check the offset really is of a BAR
155 	if (regid < PCI_base_registers || (regid % sizeof(uint32) != 0)
156 		|| (regid >= PCI_base_registers + 6 * (int)sizeof(uint32))) {
157 		return -1;
158 	}
159 
160 	// turn offset into array index
161 	regid -= PCI_base_registers;
162 	regid /= sizeof(uint32);
163 	return regid;
164 }
165 
166 
167 struct resource *
168 bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start,
169 	unsigned long end, unsigned long count, uint32 flags)
170 {
171 	struct resource *res;
172 	int result = -1;
173 
174 	if (type != SYS_RES_IRQ && type != SYS_RES_MEMORY
175 		&& type != SYS_RES_IOPORT)
176 		return NULL;
177 
178 	device_printf(dev, "bus_alloc_resource(%i, [%i], 0x%lx, 0x%lx, 0x%lx,"
179 		"0x%" B_PRIx32 ")\n", type, *rid, start, end, count, flags);
180 
181 	// maybe a local array of resources is enough
182 	res = (struct resource *)malloc(sizeof(struct resource));
183 	if (res == NULL)
184 		return NULL;
185 
186 	if (type == SYS_RES_IRQ) {
187 		if (*rid == 0) {
188 			// pinned interrupt
189 			result = bus_alloc_irq_resource(dev, res);
190 		} else {
191 			// msi or msi-x interrupt at index *rid - 1
192 			pci_info* info = get_device_pci_info(dev);
193 			res->r_bustag = BUS_SPACE_TAG_MSI;
194 			res->r_bushandle = info->u.h0.interrupt_line + *rid - 1;
195 			result = 0;
196 		}
197 	} else if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
198 		pci_info* info = get_device_pci_info(dev);
199 		int bar_index = bus_register_to_bar_index(info, *rid);
200 		if (bar_index >= 0) {
201 			if (type == SYS_RES_MEMORY)
202 				result = bus_alloc_mem_resource(dev, res, info, bar_index);
203 			else
204 				result = bus_alloc_ioport_resource(dev, res, info, bar_index);
205 		}
206 	}
207 
208 	if (result < 0) {
209 		free(res);
210 		return NULL;
211 	}
212 
213 	res->r_type = type;
214 	return res;
215 }
216 
217 
218 int
219 bus_release_resource(device_t dev, int type, int rid, struct resource *res)
220 {
221 	if (res->r_type != type)
222 		panic("bus_release_resource: mismatch");
223 
224 	if (type == SYS_RES_MEMORY)
225 		delete_area(res->r_mapped_area);
226 
227 	free(res);
228 	return 0;
229 }
230 
231 
232 int
233 bus_alloc_resources(device_t dev, struct resource_spec *resourceSpec,
234 	struct resource **resources)
235 {
236 	int i;
237 
238 	for (i = 0; resourceSpec[i].type != -1; i++) {
239 		resources[i] = bus_alloc_resource_any(dev,
240 			resourceSpec[i].type, &resourceSpec[i].rid, resourceSpec[i].flags);
241 		if (resources[i] == NULL
242 			&& (resourceSpec[i].flags & RF_OPTIONAL) == 0) {
243 			for (++i; resourceSpec[i].type != -1; i++) {
244 				resources[i] = NULL;
245 			}
246 
247 			bus_release_resources(dev, resourceSpec, resources);
248 			return ENXIO;
249 		}
250 	}
251 	return 0;
252 }
253 
254 
255 void
256 bus_release_resources(device_t dev, const struct resource_spec *resourceSpec,
257 	struct resource **resources)
258 {
259 	int i;
260 
261 	for (i = 0; resourceSpec[i].type != -1; i++) {
262 		if (resources[i] == NULL)
263 			continue;
264 
265 		bus_release_resource(dev, resourceSpec[i].type, resourceSpec[i].rid,
266 			resources[i]);
267 		resources[i] = NULL;
268 	}
269 }
270 
271 
272 bus_space_handle_t
273 rman_get_bushandle(struct resource *res)
274 {
275 	return res->r_bushandle;
276 }
277 
278 
279 bus_space_tag_t
280 rman_get_bustag(struct resource *res)
281 {
282 	return res->r_bustag;
283 }
284 
285 
286 int
287 rman_get_rid(struct resource *res)
288 {
289 	return 0;
290 }
291 
292 
293 void*
294 rman_get_virtual(struct resource *res)
295 {
296 	return NULL;
297 }
298 
299 
300 bus_addr_t
301 rman_get_start(struct resource *res)
302 {
303 	return res->r_bushandle;
304 }
305 
306 
307 bus_size_t
308 rman_get_size(struct resource *res)
309 {
310 	area_info info;
311 	if (get_area_info(res->r_mapped_area, &info) != B_OK)
312 		return 0;
313 	return info.size;
314 }
315 
316 
317 //	#pragma mark - Interrupt handling
318 
319 
320 static int32
321 intr_wrapper(void *data)
322 {
323 	struct internal_intr *intr = (struct internal_intr *)data;
324 
325 	//device_printf(intr->dev, "in interrupt handler.\n");
326 
327 	if (!HAIKU_CHECK_DISABLE_INTERRUPTS(intr->dev))
328 		return B_UNHANDLED_INTERRUPT;
329 
330 	release_sem_etc(intr->sem, 1, B_DO_NOT_RESCHEDULE);
331 	return intr->handling ? B_HANDLED_INTERRUPT : B_INVOKE_SCHEDULER;
332 }
333 
334 
335 static int32
336 intr_handler(void *data)
337 {
338 	struct internal_intr *intr = (struct internal_intr *)data;
339 	status_t status;
340 
341 	while (1) {
342 		status = acquire_sem(intr->sem);
343 		if (status < B_OK)
344 			break;
345 
346 		//device_printf(intr->dev, "in soft interrupt handler.\n");
347 
348 		atomic_or(&intr->handling, 1);
349 		if ((intr->flags & INTR_MPSAFE) == 0)
350 			mtx_lock(&Giant);
351 
352 		intr->handler(intr->arg);
353 
354 		if ((intr->flags & INTR_MPSAFE) == 0)
355 			mtx_unlock(&Giant);
356 		atomic_and(&intr->handling, 0);
357 		HAIKU_REENABLE_INTERRUPTS(intr->dev);
358 	}
359 
360 	return 0;
361 }
362 
363 
364 static void
365 free_internal_intr(struct internal_intr *intr)
366 {
367 	if (intr->sem >= B_OK) {
368 		status_t status;
369 		delete_sem(intr->sem);
370 		wait_for_thread(intr->thread, &status);
371 	}
372 
373 	free(intr);
374 }
375 
376 
377 int
378 bus_setup_intr(device_t dev, struct resource *res, int flags,
379 	driver_filter_t* filter, driver_intr_t handler, void *arg, void **_cookie)
380 {
381 	struct internal_intr *intr = (struct internal_intr *)malloc(
382 		sizeof(struct internal_intr));
383 	char semName[64];
384 	status_t status;
385 
386 	if (intr == NULL)
387 		return B_NO_MEMORY;
388 
389 	intr->dev = dev;
390 	intr->filter = filter;
391 	intr->handler = handler;
392 	intr->arg = arg;
393 	intr->irq = res->r_bushandle;
394 	intr->flags = flags;
395 	intr->sem = -1;
396 	intr->thread = -1;
397 
398 	if (filter != NULL) {
399 		status = install_io_interrupt_handler(intr->irq,
400 			(interrupt_handler)intr->filter, intr->arg, 0);
401 	} else {
402 		snprintf(semName, sizeof(semName), "%s intr", dev->device_name);
403 
404 		intr->sem = create_sem(0, semName);
405 		if (intr->sem < B_OK) {
406 			free(intr);
407 			return B_NO_MEMORY;
408 		}
409 
410 		snprintf(semName, sizeof(semName), "%s intr handler", dev->device_name);
411 
412 		intr->thread = spawn_kernel_thread(intr_handler, semName,
413 			B_REAL_TIME_DISPLAY_PRIORITY, intr);
414 		if (intr->thread < B_OK) {
415 			delete_sem(intr->sem);
416 			free(intr);
417 			return B_NO_MEMORY;
418 		}
419 
420 		status = install_io_interrupt_handler(intr->irq,
421 			intr_wrapper, intr, 0);
422 	}
423 
424 	if (status == B_OK && res->r_bustag == BUS_SPACE_TAG_MSI && gPCIx86 != NULL) {
425 		// this is an msi, enable it
426 		struct root_device_softc* root_softc = ((struct root_device_softc *)dev->root->softc);
427 		if (root_softc->is_msi) {
428 			if (gPCIx86->enable_msi(root_softc->pci_info.bus, root_softc->pci_info.device,
429 					root_softc->pci_info.function) != B_OK) {
430 				device_printf(dev, "enabling msi failed\n");
431 				bus_teardown_intr(dev, res, intr);
432 				return ENODEV;
433 			}
434 		} else if (root_softc->is_msix) {
435 			if (gPCIx86->enable_msix(root_softc->pci_info.bus, root_softc->pci_info.device,
436 					root_softc->pci_info.function) != B_OK) {
437 				device_printf(dev, "enabling msix failed\n");
438 				bus_teardown_intr(dev, res, intr);
439 				return ENODEV;
440 			}
441 		}
442 	}
443 
444 	if (status < B_OK) {
445 		free_internal_intr(intr);
446 		return status;
447 	}
448 
449 	resume_thread(intr->thread);
450 
451 	*_cookie = intr;
452 	return 0;
453 }
454 
455 
456 int
457 bus_teardown_intr(device_t dev, struct resource *res, void *arg)
458 {
459 	struct internal_intr *intr = (struct internal_intr *)arg;
460 	if (intr == NULL)
461 		return -1;
462 
463 	struct root_device_softc *root = (struct root_device_softc *)dev->root->softc;
464 
465 	if ((root->is_msi || root->is_msix) && gPCIx86 != NULL) {
466 		// disable msi generation
467 		pci_info *info = &root->pci_info;
468 		gPCIx86->disable_msi(info->bus, info->device, info->function);
469 	}
470 
471 	if (intr->filter != NULL) {
472 		remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter,
473 			intr->arg);
474 	} else {
475 		remove_io_interrupt_handler(intr->irq, intr_wrapper, intr);
476 	}
477 
478 	free_internal_intr(intr);
479 	return 0;
480 }
481 
482 
483 int
484 bus_bind_intr(device_t dev, struct resource *res, int cpu)
485 {
486 	if (dev->parent == NULL)
487 		return EINVAL;
488 
489 	// TODO
490 	return 0;
491 }
492 
493 
494 int bus_describe_intr(device_t dev, struct resource *irq, void *cookie,
495 	const char* fmt, ...)
496 {
497 	if (dev->parent == NULL)
498 		return EINVAL;
499 
500 	// we don't really support names for interrupts
501 	return 0;
502 }
503 
504 
505 //	#pragma mark - bus functions
506 
507 
508 bus_dma_tag_t
509 bus_get_dma_tag(device_t dev)
510 {
511 	return NULL;
512 }
513 
514 
515 int
516 bus_generic_suspend(device_t dev)
517 {
518 	UNIMPLEMENTED();
519 	return B_ERROR;
520 }
521 
522 
523 int
524 bus_generic_resume(device_t dev)
525 {
526 	UNIMPLEMENTED();
527 	return B_ERROR;
528 }
529 
530 
531 void
532 bus_generic_shutdown(device_t dev)
533 {
534 	UNIMPLEMENTED();
535 }
536 
537 
538 int
539 bus_print_child_header(device_t dev, device_t child)
540 {
541 	UNIMPLEMENTED();
542 	return B_ERROR;
543 }
544 
545 
546 int
547 bus_print_child_footer(device_t dev, device_t child)
548 {
549 	UNIMPLEMENTED();
550 	return B_ERROR;
551 }
552 
553 
554 int
555 bus_generic_print_child(device_t dev, device_t child)
556 {
557 	UNIMPLEMENTED();
558 	return B_ERROR;
559 }
560 
561 
562 void
563 bus_generic_driver_added(device_t dev, driver_t *driver)
564 {
565 	UNIMPLEMENTED();
566 }
567 
568 
569 int
570 bus_child_present(device_t child)
571 {
572 	device_t parent = device_get_parent(child);
573 	if (parent == NULL)
574 		return 0;
575 
576 	return bus_child_present(parent);
577 }
578 
579 
580 void
581 bus_enumerate_hinted_children(device_t bus)
582 {
583 #if 0
584 	UNIMPLEMENTED();
585 #endif
586 }
587