1 /* 2 * Copyright 2022, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ECAMPCIController.h" 8 #include <acpi.h> 9 10 #include <AutoDeleterDrivers.h> 11 12 #include "acpi_irq_routing_table.h" 13 14 #include <string.h> 15 #include <new> 16 17 18 static uint32 19 ReadReg8(addr_t adr) 20 { 21 uint32 ofs = adr % 4; 22 adr = ROUNDDOWN(adr, 4); 23 union { 24 uint32 in; 25 uint8 out[4]; 26 } val{.in = *(vuint32*)adr}; 27 return val.out[ofs]; 28 } 29 30 31 static uint32 32 ReadReg16(addr_t adr) 33 { 34 uint32 ofs = adr / 2 % 2; 35 adr = ROUNDDOWN(adr, 4); 36 union { 37 uint32 in; 38 uint16 out[2]; 39 } val{.in = *(vuint32*)adr}; 40 return val.out[ofs]; 41 } 42 43 44 static void 45 WriteReg8(addr_t adr, uint32 value) 46 { 47 uint32 ofs = adr % 4; 48 adr = ROUNDDOWN(adr, 4); 49 union { 50 uint32 in; 51 uint8 out[4]; 52 } val{.in = *(vuint32*)adr}; 53 val.out[ofs] = (uint8)value; 54 *(vuint32*)adr = val.in; 55 } 56 57 58 static void 59 WriteReg16(addr_t adr, uint32 value) 60 { 61 uint32 ofs = adr / 2 % 2; 62 adr = ROUNDDOWN(adr, 4); 63 union { 64 uint32 in; 65 uint16 out[2]; 66 } val{.in = *(vuint32*)adr}; 67 val.out[ofs] = (uint16)value; 68 *(vuint32*)adr = val.in; 69 } 70 71 72 //#pragma mark - driver 73 74 75 float 76 ECAMPCIController::SupportsDevice(device_node* parent) 77 { 78 const char* bus; 79 status_t status = gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false); 80 if (status < B_OK) 81 return -1.0f; 82 83 if (strcmp(bus, "fdt") == 0) { 84 const char* compatible; 85 status = gDeviceManager->get_attr_string(parent, "fdt/compatible", &compatible, false); 86 if (status < B_OK) 87 return -1.0f; 88 89 if (strcmp(compatible, "pci-host-ecam-generic") != 0) 90 return 0.0f; 91 92 return 1.0f; 93 } 94 95 if (strcmp(bus, "acpi") == 0) { 96 const char* hid; 97 if (gDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false) < B_OK) 98 return -1.0f; 99 100 if (strcmp(hid, "PNP0A03") != 0 && strcmp(hid, "PNP0A08") != 0) 101 return 0.0f; 102 103 return 1.0f; 104 } 105 106 return 0.0f; 107 } 108 109 110 status_t 111 ECAMPCIController::RegisterDevice(device_node* parent) 112 { 113 device_attr attrs[] = { 114 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "ECAM PCI Host Controller"} }, 115 { B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} }, 116 {} 117 }; 118 119 return gDeviceManager->register_node(parent, ECAM_PCI_DRIVER_MODULE_NAME, attrs, NULL, NULL); 120 } 121 122 123 #if !defined(ECAM_PCI_CONTROLLER_NO_INIT) 124 status_t 125 ECAMPCIController::InitDriver(device_node* node, ECAMPCIController*& outDriver) 126 { 127 dprintf("+ECAMPCIController::InitDriver()\n"); 128 DeviceNodePutter<&gDeviceManager> parentNode(gDeviceManager->get_parent_node(node)); 129 130 ObjectDeleter<ECAMPCIController> driver; 131 132 const char* bus; 133 CHECK_RET(gDeviceManager->get_attr_string(parentNode.Get(), B_DEVICE_BUS, &bus, false)); 134 if (strcmp(bus, "fdt") == 0) 135 driver.SetTo(new(std::nothrow) ECAMPCIControllerFDT()); 136 else if (strcmp(bus, "acpi") == 0) 137 driver.SetTo(new(std::nothrow) ECAMPCIControllerACPI()); 138 else 139 return B_ERROR; 140 141 if (!driver.IsSet()) 142 return B_NO_MEMORY; 143 144 driver->fNode = node; 145 146 CHECK_RET(driver->ReadResourceInfo()); 147 outDriver = driver.Detach(); 148 149 dprintf("-ECAMPCIController::InitDriver()\n"); 150 return B_OK; 151 } 152 153 154 void 155 ECAMPCIController::UninitDriver() 156 { 157 delete this; 158 } 159 #endif 160 161 162 /** Compute the virtual address for accessing a PCI ECAM register. 163 * 164 * \returns NULL if the address is out of bounds. 165 */ 166 addr_t 167 ECAMPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset) 168 { 169 PciAddressEcam address { 170 .offset = offset, 171 .function = function, 172 .device = device, 173 .bus = bus 174 }; 175 if ((ROUNDDOWN(address.val, 4) + 4) > fRegsLen) 176 return 0; 177 178 return (addr_t)fRegs + address.val; 179 } 180 181 182 //#pragma mark - PCI controller 183 184 185 status_t 186 ECAMPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function, 187 uint16 offset, uint8 size, uint32& value) 188 { 189 addr_t address = ConfigAddress(bus, device, function, offset); 190 if (address == 0) 191 return ERANGE; 192 193 switch (size) { 194 case 1: value = ReadReg8(address); break; 195 case 2: value = ReadReg16(address); break; 196 case 4: value = *(vuint32*)address; break; 197 default: 198 return B_BAD_VALUE; 199 } 200 201 return B_OK; 202 } 203 204 205 status_t 206 ECAMPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function, 207 uint16 offset, uint8 size, uint32 value) 208 { 209 addr_t address = ConfigAddress(bus, device, function, offset); 210 if (address == 0) 211 return ERANGE; 212 213 switch (size) { 214 case 1: WriteReg8(address, value); break; 215 case 2: WriteReg16(address, value); break; 216 case 4: *(vuint32*)address = value; break; 217 default: 218 return B_BAD_VALUE; 219 } 220 221 return B_OK; 222 } 223 224 225 status_t 226 ECAMPCIController::GetMaxBusDevices(int32& count) 227 { 228 count = 32; 229 return B_OK; 230 } 231 232 233 status_t 234 ECAMPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function, 235 uint8 pin, uint8& irq) 236 { 237 return B_UNSUPPORTED; 238 } 239 240 241 status_t 242 ECAMPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function, 243 uint8 pin, uint8 irq) 244 { 245 return B_UNSUPPORTED; 246 } 247 248 249 status_t 250 ECAMPCIController::GetRange(uint32 index, pci_resource_range* range) 251 { 252 if (index >= (uint32)fResourceRanges.Count()) 253 return B_BAD_INDEX; 254 255 *range = fResourceRanges[index]; 256 return B_OK; 257 } 258