1 /* 2 * Copyright 2022, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 extern "C" { 7 #include "device.h" 8 9 #include <compat/machine/resource.h> 10 #include <compat/dev/pci/pcireg.h> 11 #include <compat/dev/pci/pcivar.h> 12 } 13 14 #include <PCI.h> 15 #include <PCI_x86.h> 16 17 18 //#define DEBUG_PCI 19 #ifdef DEBUG_PCI 20 # define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args) 21 #else 22 # define TRACE_PCI(dev, format, args...) do { } while (0) 23 #endif 24 25 26 pci_module_info *gPci; 27 struct pci_x86_module_info *gPCIx86; 28 29 30 status_t 31 init_pci() 32 { 33 if (gPci != NULL) 34 return B_OK; 35 36 status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPci); 37 if (status != B_OK) 38 return status; 39 40 // if it fails we just don't support x86 specific features (like MSIs) 41 if (get_module(B_PCI_X86_MODULE_NAME, (module_info **)&gPCIx86) != B_OK) 42 gPCIx86 = NULL; 43 44 return B_OK; 45 } 46 47 48 void 49 uninit_pci() 50 { 51 if (gPci != NULL) 52 put_module(B_PCI_MODULE_NAME); 53 if (gPCIx86 != NULL) 54 put_module(B_PCI_X86_MODULE_NAME); 55 } 56 57 58 pci_info* 59 get_device_pci_info(device_t device) 60 { 61 struct root_device_softc* root_softc = (struct root_device_softc*)device->root->softc; 62 if (root_softc->bus != root_device_softc::BUS_pci) 63 return NULL; 64 return &root_softc->pci_info; 65 } 66 67 68 uint32_t 69 pci_read_config(device_t dev, int offset, int size) 70 { 71 pci_info* info = get_device_pci_info(dev); 72 73 uint32_t value = gPci->read_pci_config(info->bus, info->device, 74 info->function, offset, size); 75 TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value); 76 return value; 77 } 78 79 80 void 81 pci_write_config(device_t dev, int offset, uint32_t value, int size) 82 { 83 pci_info* info = get_device_pci_info(dev); 84 85 TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size); 86 87 gPci->write_pci_config(info->bus, info->device, info->function, offset, 88 size, value); 89 } 90 91 92 uint16_t 93 pci_get_vendor(device_t dev) 94 { 95 return pci_read_config(dev, PCI_vendor_id, 2); 96 } 97 98 99 uint16_t 100 pci_get_device(device_t dev) 101 { 102 return pci_read_config(dev, PCI_device_id, 2); 103 } 104 105 106 uint16_t 107 pci_get_subvendor(device_t dev) 108 { 109 return pci_read_config(dev, PCI_subsystem_vendor_id, 2); 110 } 111 112 113 uint16_t 114 pci_get_subdevice(device_t dev) 115 { 116 return pci_read_config(dev, PCI_subsystem_id, 2); 117 } 118 119 120 uint8_t 121 pci_get_revid(device_t dev) 122 { 123 return pci_read_config(dev, PCI_revision, 1); 124 } 125 126 127 uint32_t 128 pci_get_domain(device_t dev) 129 { 130 return 0; 131 } 132 133 uint32_t 134 pci_get_devid(device_t dev) 135 { 136 return pci_read_config(dev, PCI_device_id, 2) << 16 | 137 pci_read_config(dev, PCI_vendor_id, 2); 138 } 139 140 uint8_t 141 pci_get_cachelnsz(device_t dev) 142 { 143 return pci_read_config(dev, PCI_line_size, 1); 144 } 145 146 uint8_t * 147 pci_get_ether(device_t dev) 148 { 149 /* used in if_dc to get the MAC from CardBus CIS for Xircom card */ 150 return NULL; /* NULL is handled in the caller correctly */ 151 } 152 153 uint8_t 154 pci_get_bus(device_t dev) 155 { 156 pci_info *info 157 = &((struct root_device_softc *)dev->root->softc)->pci_info; 158 return info->bus; 159 } 160 161 162 uint8_t 163 pci_get_slot(device_t dev) 164 { 165 pci_info *info 166 = &((struct root_device_softc *)dev->root->softc)->pci_info; 167 return info->device; 168 } 169 170 171 uint8_t 172 pci_get_function(device_t dev) 173 { 174 pci_info* info = get_device_pci_info(dev); 175 return info->function; 176 } 177 178 179 device_t 180 pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func) 181 { 182 // We don't support that yet - if we want to support the multi port 183 // feature of the Broadcom BCM 570x driver, we would have to change 184 // that. 185 return NULL; 186 } 187 188 189 static void 190 pci_set_command_bit(device_t dev, uint16_t bit) 191 { 192 uint16_t command = pci_read_config(dev, PCI_command, 2); 193 pci_write_config(dev, PCI_command, command | bit, 2); 194 } 195 196 197 int 198 pci_enable_busmaster(device_t dev) 199 { 200 // We do this a bit later than FreeBSD does. 201 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) 202 pci_set_powerstate(dev, PCI_POWERSTATE_D0); 203 204 pci_set_command_bit(dev, PCI_command_master); 205 return 0; 206 } 207 208 209 int 210 pci_enable_io(device_t dev, int space) 211 { 212 /* adapted from FreeBSD's pci_enable_io_method */ 213 int bit = 0; 214 215 switch (space) { 216 case SYS_RES_IOPORT: 217 bit = PCI_command_io; 218 break; 219 case SYS_RES_MEMORY: 220 bit = PCI_command_memory; 221 break; 222 default: 223 return EINVAL; 224 } 225 226 pci_set_command_bit(dev, bit); 227 if (pci_read_config(dev, PCI_command, 2) & bit) 228 return 0; 229 230 device_printf(dev, "pci_enable_io(%d) failed.\n", space); 231 232 return ENXIO; 233 } 234 235 236 int 237 pci_find_cap(device_t dev, int capability, int *capreg) 238 { 239 return pci_find_extcap(dev, capability, capreg); 240 } 241 242 243 int 244 pci_find_extcap(device_t child, int capability, int *_capabilityRegister) 245 { 246 uint8 capabilityPointer; 247 uint8 headerType; 248 uint16 status; 249 250 status = pci_read_config(child, PCIR_STATUS, 2); 251 if ((status & PCIM_STATUS_CAPPRESENT) == 0) 252 return ENXIO; 253 254 headerType = pci_read_config(child, PCI_header_type, 1); 255 switch (headerType & PCIM_HDRTYPE) { 256 case 0: 257 case 1: 258 capabilityPointer = PCIR_CAP_PTR; 259 break; 260 case 2: 261 capabilityPointer = PCIR_CAP_PTR_2; 262 break; 263 default: 264 return ENXIO; 265 } 266 capabilityPointer = pci_read_config(child, capabilityPointer, 1); 267 268 while (capabilityPointer != 0) { 269 if (pci_read_config(child, capabilityPointer + PCICAP_ID, 1) 270 == capability) { 271 if (_capabilityRegister != NULL) 272 *_capabilityRegister = capabilityPointer; 273 return 0; 274 } 275 capabilityPointer = pci_read_config(child, 276 capabilityPointer + PCICAP_NEXTPTR, 1); 277 } 278 279 return ENOENT; 280 } 281 282 283 int 284 pci_msi_count(device_t dev) 285 { 286 if (gPCIx86 == NULL) 287 return 0; 288 289 pci_info* info = get_device_pci_info(dev); 290 return gPCIx86->get_msi_count(info->bus, info->device, info->function); 291 } 292 293 294 int 295 pci_alloc_msi(device_t dev, int *count) 296 { 297 if (gPCIx86 == NULL) 298 return ENODEV; 299 300 pci_info* info = get_device_pci_info(dev); 301 uint8 startVector = 0; 302 if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, 303 &startVector) != B_OK) { 304 return ENODEV; 305 } 306 307 ((struct root_device_softc *)dev->root->softc)->is_msi = true; 308 info->u.h0.interrupt_line = startVector; 309 return EOK; 310 } 311 312 313 int 314 pci_release_msi(device_t dev) 315 { 316 if (gPCIx86 == NULL) 317 return ENODEV; 318 319 pci_info* info = get_device_pci_info(dev); 320 gPCIx86->unconfigure_msi(info->bus, info->device, info->function); 321 ((struct root_device_softc *)dev->root->softc)->is_msi = false; 322 ((struct root_device_softc *)dev->root->softc)->is_msix = false; 323 return EOK; 324 } 325 326 327 int 328 pci_msix_table_bar(device_t dev) 329 { 330 pci_info* info = get_device_pci_info(dev); 331 332 uint8 capability_offset; 333 if (gPci->find_pci_capability(info->bus, info->device, info->function, 334 PCI_cap_id_msix, &capability_offset) != B_OK) 335 return -1; 336 337 uint32 table_value = gPci->read_pci_config(info->bus, info->device, info->function, 338 capability_offset + PCI_msix_table, 4); 339 340 uint32 bar = table_value & PCI_msix_bir_mask; 341 return PCIR_BAR(bar); 342 } 343 344 345 int 346 pci_msix_count(device_t dev) 347 { 348 if (gPCIx86 == NULL) 349 return 0; 350 351 pci_info* info = get_device_pci_info(dev); 352 return gPCIx86->get_msix_count(info->bus, info->device, info->function); 353 } 354 355 356 int 357 pci_alloc_msix(device_t dev, int *count) 358 { 359 if (gPCIx86 == NULL) 360 return ENODEV; 361 362 pci_info* info = get_device_pci_info(dev); 363 uint8 startVector = 0; 364 if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count, 365 &startVector) != B_OK) { 366 return ENODEV; 367 } 368 369 ((struct root_device_softc *)dev->root->softc)->is_msix = true; 370 info->u.h0.interrupt_line = startVector; 371 return EOK; 372 } 373 374 375 int 376 pci_get_max_read_req(device_t dev) 377 { 378 int cap; 379 uint16_t val; 380 381 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 382 return (0); 383 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 384 val &= PCIM_EXP_CTL_MAX_READ_REQUEST; 385 val >>= 12; 386 return (1 << (val + 7)); 387 } 388 389 390 int 391 pci_set_max_read_req(device_t dev, int size) 392 { 393 int cap; 394 uint16_t val; 395 396 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 397 return (0); 398 if (size < 128) 399 size = 128; 400 if (size > 4096) 401 size = 4096; 402 size = (1 << (fls(size) - 1)); 403 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 404 val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; 405 val |= (fls(size) - 8) << 12; 406 pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); 407 return (size); 408 } 409 410 411 int 412 pci_get_powerstate(device_t dev) 413 { 414 int capabilityRegister; 415 uint16 status; 416 int powerState = PCI_POWERSTATE_D0; 417 418 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 419 return powerState; 420 421 status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2); 422 switch (status & PCI_pm_mask) { 423 case PCI_pm_state_d0: 424 break; 425 case PCI_pm_state_d1: 426 powerState = PCI_POWERSTATE_D1; 427 break; 428 case PCI_pm_state_d2: 429 powerState = PCI_POWERSTATE_D2; 430 break; 431 case PCI_pm_state_d3: 432 powerState = PCI_POWERSTATE_D3; 433 break; 434 default: 435 powerState = PCI_POWERSTATE_UNKNOWN; 436 break; 437 } 438 439 TRACE_PCI(dev, "%s: D%i\n", __func__, powerState); 440 return powerState; 441 } 442 443 444 int 445 pci_set_powerstate(device_t dev, int newPowerState) 446 { 447 int capabilityRegister; 448 int oldPowerState; 449 uint8 currentPowerManagementStatus; 450 uint8 newPowerManagementStatus; 451 uint16 powerManagementCapabilities; 452 bigtime_t stateTransitionDelayInUs = 0; 453 454 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 455 return EOPNOTSUPP; 456 457 oldPowerState = pci_get_powerstate(dev); 458 if (oldPowerState == newPowerState) 459 return EOK; 460 461 switch (max_c(oldPowerState, newPowerState)) { 462 case PCI_POWERSTATE_D2: 463 stateTransitionDelayInUs = 200; 464 break; 465 case PCI_POWERSTATE_D3: 466 stateTransitionDelayInUs = 10000; 467 break; 468 } 469 470 currentPowerManagementStatus = pci_read_config(dev, capabilityRegister 471 + PCIR_POWER_STATUS, 2); 472 newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask; 473 powerManagementCapabilities = pci_read_config(dev, capabilityRegister 474 + PCIR_POWER_CAP, 2); 475 476 switch (newPowerState) { 477 case PCI_POWERSTATE_D0: 478 newPowerManagementStatus |= PCIM_PSTAT_D0; 479 break; 480 case PCI_POWERSTATE_D1: 481 if ((powerManagementCapabilities & PCI_pm_d1supp) == 0) 482 return EOPNOTSUPP; 483 newPowerManagementStatus |= PCIM_PSTAT_D1; 484 break; 485 case PCI_POWERSTATE_D2: 486 if ((powerManagementCapabilities & PCI_pm_d2supp) == 0) 487 return EOPNOTSUPP; 488 newPowerManagementStatus |= PCIM_PSTAT_D2; 489 break; 490 case PCI_POWERSTATE_D3: 491 newPowerManagementStatus |= PCIM_PSTAT_D3; 492 break; 493 default: 494 return EINVAL; 495 } 496 497 TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState); 498 pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, 499 newPowerManagementStatus, 2); 500 if (stateTransitionDelayInUs != 0) 501 snooze(stateTransitionDelayInUs); 502 503 return EOK; 504 } 505