1/*
2 * Copyright 2022, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ECAMPCIController.h"
8
9#include <AutoDeleterDrivers.h>
10
11#include <string.h>
12
13
14status_t
15ECAMPCIControllerFDT::ReadResourceInfo()
16{
17	DeviceNodePutter<&gDeviceManager> fdtNode(gDeviceManager->get_parent_node(fNode));
18
19	fdt_device_module_info *fdtModule;
20	fdt_device* fdtDev;
21	CHECK_RET(gDeviceManager->get_driver(fdtNode.Get(),
22		(driver_module_info**)&fdtModule, (void**)&fdtDev));
23
24	const void* prop;
25	int propLen;
26
27	prop = fdtModule->get_prop(fdtDev, "bus-range", &propLen);
28	if (prop != NULL && propLen == 8) {
29		uint32 busBeg = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 0));
30		uint32 busEnd = B_BENDIAN_TO_HOST_INT32(*((uint32*)prop + 1));
31		dprintf("  bus-range: %" B_PRIu32 " - %" B_PRIu32 "\n", busBeg, busEnd);
32	}
33
34	prop = fdtModule->get_prop(fdtDev, "ranges", &propLen);
35	if (prop == NULL) {
36		dprintf("  \"ranges\" property not found");
37		return B_ERROR;
38	}
39	dprintf("  ranges:\n");
40	for (uint32_t *it = (uint32_t*)prop; (uint8_t*)it - (uint8_t*)prop < propLen; it += 7) {
41		dprintf("    ");
42		uint32_t type      = B_BENDIAN_TO_HOST_INT32(*(it + 0));
43		uint64_t childAdr  = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 1));
44		uint64_t parentAdr = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 3));
45		uint64_t len       = B_BENDIAN_TO_HOST_INT64(*(uint64_t*)(it + 5));
46
47		pci_resource_range range = {};
48		range.host_address = parentAdr;
49		range.pci_address = childAdr;
50		range.size = len;
51
52		if ((type & fdtPciRangePrefechable) != 0)
53			range.address_type |= PCI_address_prefetchable;
54
55		switch (type & fdtPciRangeTypeMask) {
56		case fdtPciRangeIoPort:
57			range.type = B_IO_PORT;
58			fResourceRanges.Add(range);
59			break;
60		case fdtPciRangeMmio32Bit:
61			range.type = B_IO_MEMORY;
62			range.address_type |= PCI_address_type_32;
63			fResourceRanges.Add(range);
64			break;
65		case fdtPciRangeMmio64Bit:
66			range.type = B_IO_MEMORY;
67			range.address_type |= PCI_address_type_64;
68			fResourceRanges.Add(range);
69			break;
70		}
71
72		switch (type & fdtPciRangeTypeMask) {
73		case fdtPciRangeConfig:    dprintf("CONFIG"); break;
74		case fdtPciRangeIoPort:    dprintf("IOPORT"); break;
75		case fdtPciRangeMmio32Bit: dprintf("MMIO32"); break;
76		case fdtPciRangeMmio64Bit: dprintf("MMIO64"); break;
77		}
78
79		dprintf(" (0x%08" B_PRIx32 "): ", type);
80		dprintf("child: %08" B_PRIx64, childAdr);
81		dprintf(", parent: %08" B_PRIx64, parentAdr);
82		dprintf(", len: %" B_PRIx64 "\n", len);
83	}
84
85	uint64 regs = 0;
86	if (!fdtModule->get_reg(fdtDev, 0, &regs, &fRegsLen))
87		return B_ERROR;
88
89	fRegsArea.SetTo(map_physical_memory("PCI Config MMIO", regs, fRegsLen, B_ANY_KERNEL_ADDRESS,
90		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&fRegs));
91	CHECK_RET(fRegsArea.Get());
92
93	return B_OK;
94}
95
96
97status_t
98ECAMPCIControllerFDT::Finalize()
99{
100	dprintf("finalize PCI controller from FDT\n");
101
102	DeviceNodePutter<&gDeviceManager> parent(gDeviceManager->get_parent_node(fNode));
103
104	fdt_device_module_info* parentModule;
105	fdt_device* parentDev;
106
107	CHECK_RET(gDeviceManager->get_driver(parent.Get(), (driver_module_info**)&parentModule,
108		(void**)&parentDev));
109
110	struct fdt_interrupt_map* interruptMap = parentModule->get_interrupt_map(parentDev);
111	parentModule->print_interrupt_map(interruptMap);
112
113	for (int bus = 0; bus < 8; bus++) {
114		// TODO: Proper multiple domain handling. (domain, bus) pair should be converted to virtual
115		// bus before calling PCI module interface.
116		for (int device = 0; device < 32; device++) {
117			uint32 vendorID = gPCI->read_pci_config(bus, device, 0, PCI_vendor_id, 2);
118			if ((vendorID != 0xffffffff) && (vendorID != 0xffff)) {
119				uint32 headerType = gPCI->read_pci_config(bus, device, 0, PCI_header_type, 1);
120				if ((headerType & 0x80) != 0) {
121					for (int function = 0; function < 8; function++) {
122						FinalizeInterrupts(parentModule, interruptMap, bus, device, function);
123					}
124				} else {
125					FinalizeInterrupts(parentModule, interruptMap, bus, device, 0);
126				}
127			}
128		}
129	}
130
131	return B_OK;
132}
133
134
135void
136ECAMPCIControllerFDT::FinalizeInterrupts(fdt_device_module_info* fdtModule,
137	struct fdt_interrupt_map* interruptMap, int bus, int device, int function)
138{
139	uint32 childAddr = ((bus & 0xff) << 16) | ((device & 0x1f) << 11) | ((function & 0x07) << 8);
140	uint32 interruptPin = gPCI->read_pci_config(bus, device, function, PCI_interrupt_pin, 1);
141
142	if (interruptPin == 0xffffffff) {
143		dprintf("Error: Unable to read interrupt pin!\n");
144		return;
145	}
146
147	uint32 irq = fdtModule->lookup_interrupt_map(interruptMap, childAddr, interruptPin);
148	if (irq == 0xffffffff) {
149		dprintf("no interrupt mapping for childAddr: (%d:%d:%d), childIrq: %d)\n",
150			bus, device, function, interruptPin);
151	} else {
152		dprintf("configure interrupt (%d,%d,%d) --> %d\n",
153			bus, device, function, irq);
154		gPCI->update_interrupt_line(bus, device, function, irq);
155	}
156}
157