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 = adr / 4 * 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 = adr / 4 * 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 = adr / 4 * 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 = adr / 4 * 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 addr_t 163 ECAMPCIController::ConfigAddress(uint8 bus, uint8 device, uint8 function, uint16 offset) 164 { 165 PciAddressEcam address { 166 .offset = offset, 167 .function = function, 168 .device = device, 169 .bus = bus 170 }; 171 if ((address.val + 4) > fRegsLen) 172 return 0; 173 174 return (addr_t)fRegs + address.val; 175 } 176 177 178 //#pragma mark - PCI controller 179 180 181 status_t 182 ECAMPCIController::ReadConfig(uint8 bus, uint8 device, uint8 function, 183 uint16 offset, uint8 size, uint32& value) 184 { 185 addr_t address = ConfigAddress(bus, device, function, offset); 186 if (address == 0) 187 return ERANGE; 188 189 switch (size) { 190 case 1: value = ReadReg8(address); break; 191 case 2: value = ReadReg16(address); break; 192 case 4: value = *(vuint32*)address; break; 193 default: 194 return B_BAD_VALUE; 195 } 196 197 return B_OK; 198 } 199 200 201 status_t 202 ECAMPCIController::WriteConfig(uint8 bus, uint8 device, uint8 function, 203 uint16 offset, uint8 size, uint32 value) 204 { 205 addr_t address = ConfigAddress(bus, device, function, offset); 206 if (address == 0) 207 return ERANGE; 208 209 switch (size) { 210 case 1: WriteReg8(address, value); break; 211 case 2: WriteReg16(address, value); break; 212 case 4: *(vuint32*)address = value; break; 213 default: 214 return B_BAD_VALUE; 215 } 216 217 return B_OK; 218 } 219 220 221 status_t 222 ECAMPCIController::GetMaxBusDevices(int32& count) 223 { 224 count = 32; 225 return B_OK; 226 } 227 228 229 status_t 230 ECAMPCIController::ReadIrq(uint8 bus, uint8 device, uint8 function, 231 uint8 pin, uint8& irq) 232 { 233 return B_UNSUPPORTED; 234 } 235 236 237 status_t 238 ECAMPCIController::WriteIrq(uint8 bus, uint8 device, uint8 function, 239 uint8 pin, uint8 irq) 240 { 241 return B_UNSUPPORTED; 242 } 243 244 245 status_t 246 ECAMPCIController::GetRange(uint32 index, pci_resource_range* range) 247 { 248 if (index >= (uint32)fResourceRanges.Count()) 249 return B_BAD_INDEX; 250 251 *range = fResourceRanges[index]; 252 return B_OK; 253 } 254