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