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 pci_info* info = get_device_pci_info(dev); 240 uint8 offset; 241 status_t status; 242 243 status = gPci->find_pci_capability(info->bus, info->device, info->function, 244 capability, &offset); 245 *capreg = offset; 246 return status; 247 } 248 249 250 int 251 pci_find_extcap(device_t dev, int capability, int *capreg) 252 { 253 pci_info* info = get_device_pci_info(dev); 254 uint16 offset; 255 status_t status; 256 257 status = gPci->find_pci_extended_capability(info->bus, info->device, info->function, 258 capability, &offset); 259 *capreg = offset; 260 return status; 261 } 262 263 264 int 265 pci_msi_count(device_t dev) 266 { 267 if (gPCIx86 == NULL) 268 return 0; 269 270 pci_info* info = get_device_pci_info(dev); 271 return gPCIx86->get_msi_count(info->bus, info->device, info->function); 272 } 273 274 275 int 276 pci_alloc_msi(device_t dev, int *count) 277 { 278 if (gPCIx86 == NULL) 279 return ENODEV; 280 281 pci_info* info = get_device_pci_info(dev); 282 uint8 startVector = 0; 283 if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, 284 &startVector) != B_OK) { 285 return ENODEV; 286 } 287 288 ((struct root_device_softc *)dev->root->softc)->is_msi = true; 289 info->u.h0.interrupt_line = startVector; 290 return EOK; 291 } 292 293 294 int 295 pci_release_msi(device_t dev) 296 { 297 if (gPCIx86 == NULL) 298 return ENODEV; 299 300 pci_info* info = get_device_pci_info(dev); 301 gPCIx86->unconfigure_msi(info->bus, info->device, info->function); 302 ((struct root_device_softc *)dev->root->softc)->is_msi = false; 303 ((struct root_device_softc *)dev->root->softc)->is_msix = false; 304 return EOK; 305 } 306 307 308 int 309 pci_msix_table_bar(device_t dev) 310 { 311 pci_info* info = get_device_pci_info(dev); 312 313 uint8 capability_offset; 314 if (gPci->find_pci_capability(info->bus, info->device, info->function, 315 PCI_cap_id_msix, &capability_offset) != B_OK) 316 return -1; 317 318 uint32 table_value = gPci->read_pci_config(info->bus, info->device, info->function, 319 capability_offset + PCI_msix_table, 4); 320 321 uint32 bar = table_value & PCI_msix_bir_mask; 322 return PCIR_BAR(bar); 323 } 324 325 326 int 327 pci_msix_count(device_t dev) 328 { 329 if (gPCIx86 == NULL) 330 return 0; 331 332 pci_info* info = get_device_pci_info(dev); 333 return gPCIx86->get_msix_count(info->bus, info->device, info->function); 334 } 335 336 337 int 338 pci_alloc_msix(device_t dev, int *count) 339 { 340 if (gPCIx86 == NULL) 341 return ENODEV; 342 343 pci_info* info = get_device_pci_info(dev); 344 uint8 startVector = 0; 345 if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count, 346 &startVector) != B_OK) { 347 return ENODEV; 348 } 349 350 ((struct root_device_softc *)dev->root->softc)->is_msix = true; 351 info->u.h0.interrupt_line = startVector; 352 return EOK; 353 } 354 355 356 int 357 pci_get_max_read_req(device_t dev) 358 { 359 int cap; 360 uint16_t val; 361 362 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 363 return (0); 364 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 365 val &= PCIM_EXP_CTL_MAX_READ_REQUEST; 366 val >>= 12; 367 return (1 << (val + 7)); 368 } 369 370 371 int 372 pci_set_max_read_req(device_t dev, int size) 373 { 374 int cap; 375 uint16_t val; 376 377 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 378 return (0); 379 if (size < 128) 380 size = 128; 381 if (size > 4096) 382 size = 4096; 383 size = (1 << (fls(size) - 1)); 384 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 385 val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; 386 val |= (fls(size) - 8) << 12; 387 pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); 388 return (size); 389 } 390 391 392 int 393 pci_get_powerstate(device_t dev) 394 { 395 int capabilityRegister; 396 uint16 status; 397 int powerState = PCI_POWERSTATE_D0; 398 399 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 400 return powerState; 401 402 status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2); 403 switch (status & PCI_pm_mask) { 404 case PCI_pm_state_d0: 405 break; 406 case PCI_pm_state_d1: 407 powerState = PCI_POWERSTATE_D1; 408 break; 409 case PCI_pm_state_d2: 410 powerState = PCI_POWERSTATE_D2; 411 break; 412 case PCI_pm_state_d3: 413 powerState = PCI_POWERSTATE_D3; 414 break; 415 default: 416 powerState = PCI_POWERSTATE_UNKNOWN; 417 break; 418 } 419 420 TRACE_PCI(dev, "%s: D%i\n", __func__, powerState); 421 return powerState; 422 } 423 424 425 int 426 pci_set_powerstate(device_t dev, int newPowerState) 427 { 428 int capabilityRegister; 429 int oldPowerState; 430 uint8 currentPowerManagementStatus; 431 uint8 newPowerManagementStatus; 432 uint16 powerManagementCapabilities; 433 bigtime_t stateTransitionDelayInUs = 0; 434 435 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 436 return EOPNOTSUPP; 437 438 oldPowerState = pci_get_powerstate(dev); 439 if (oldPowerState == newPowerState) 440 return EOK; 441 442 switch (max_c(oldPowerState, newPowerState)) { 443 case PCI_POWERSTATE_D2: 444 stateTransitionDelayInUs = 200; 445 break; 446 case PCI_POWERSTATE_D3: 447 stateTransitionDelayInUs = 10000; 448 break; 449 } 450 451 currentPowerManagementStatus = pci_read_config(dev, capabilityRegister 452 + PCIR_POWER_STATUS, 2); 453 newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask; 454 powerManagementCapabilities = pci_read_config(dev, capabilityRegister 455 + PCIR_POWER_CAP, 2); 456 457 switch (newPowerState) { 458 case PCI_POWERSTATE_D0: 459 newPowerManagementStatus |= PCIM_PSTAT_D0; 460 break; 461 case PCI_POWERSTATE_D1: 462 if ((powerManagementCapabilities & PCI_pm_d1supp) == 0) 463 return EOPNOTSUPP; 464 newPowerManagementStatus |= PCIM_PSTAT_D1; 465 break; 466 case PCI_POWERSTATE_D2: 467 if ((powerManagementCapabilities & PCI_pm_d2supp) == 0) 468 return EOPNOTSUPP; 469 newPowerManagementStatus |= PCIM_PSTAT_D2; 470 break; 471 case PCI_POWERSTATE_D3: 472 newPowerManagementStatus |= PCIM_PSTAT_D3; 473 break; 474 default: 475 return EINVAL; 476 } 477 478 TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState); 479 pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, 480 newPowerManagementStatus, 2); 481 if (stateTransitionDelayInUs != 0) 482 snooze(stateTransitionDelayInUs); 483 484 return EOK; 485 } 486