diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries/msi.c')
| -rw-r--r-- | arch/powerpc/platforms/pseries/msi.c | 75 |
1 files changed, 40 insertions, 35 deletions
diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index 420524e6f8c9..6d2f0abce6fa 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c | |||
| @@ -26,26 +26,6 @@ static int query_token, change_token; | |||
| 26 | #define RTAS_CHANGE_MSIX_FN 4 | 26 | #define RTAS_CHANGE_MSIX_FN 4 |
| 27 | #define RTAS_CHANGE_32MSI_FN 5 | 27 | #define RTAS_CHANGE_32MSI_FN 5 |
| 28 | 28 | ||
| 29 | static struct pci_dn *get_pdn(struct pci_dev *pdev) | ||
| 30 | { | ||
| 31 | struct device_node *dn; | ||
| 32 | struct pci_dn *pdn; | ||
| 33 | |||
| 34 | dn = pci_device_to_OF_node(pdev); | ||
| 35 | if (!dn) { | ||
| 36 | dev_dbg(&pdev->dev, "rtas_msi: No OF device node\n"); | ||
| 37 | return NULL; | ||
| 38 | } | ||
| 39 | |||
| 40 | pdn = PCI_DN(dn); | ||
| 41 | if (!pdn) { | ||
| 42 | dev_dbg(&pdev->dev, "rtas_msi: No PCI DN\n"); | ||
| 43 | return NULL; | ||
| 44 | } | ||
| 45 | |||
| 46 | return pdn; | ||
| 47 | } | ||
| 48 | |||
| 49 | /* RTAS Helpers */ | 29 | /* RTAS Helpers */ |
| 50 | 30 | ||
| 51 | static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs) | 31 | static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs) |
| @@ -91,7 +71,7 @@ static void rtas_disable_msi(struct pci_dev *pdev) | |||
| 91 | { | 71 | { |
| 92 | struct pci_dn *pdn; | 72 | struct pci_dn *pdn; |
| 93 | 73 | ||
| 94 | pdn = get_pdn(pdev); | 74 | pdn = pci_get_pdn(pdev); |
| 95 | if (!pdn) | 75 | if (!pdn) |
| 96 | return; | 76 | return; |
| 97 | 77 | ||
| @@ -152,7 +132,7 @@ static int check_req(struct pci_dev *pdev, int nvec, char *prop_name) | |||
| 152 | struct pci_dn *pdn; | 132 | struct pci_dn *pdn; |
| 153 | const u32 *req_msi; | 133 | const u32 *req_msi; |
| 154 | 134 | ||
| 155 | pdn = get_pdn(pdev); | 135 | pdn = pci_get_pdn(pdev); |
| 156 | if (!pdn) | 136 | if (!pdn) |
| 157 | return -ENODEV; | 137 | return -ENODEV; |
| 158 | 138 | ||
| @@ -394,6 +374,23 @@ static int check_msix_entries(struct pci_dev *pdev) | |||
| 394 | return 0; | 374 | return 0; |
| 395 | } | 375 | } |
| 396 | 376 | ||
| 377 | static void rtas_hack_32bit_msi_gen2(struct pci_dev *pdev) | ||
| 378 | { | ||
| 379 | u32 addr_hi, addr_lo; | ||
| 380 | |||
| 381 | /* | ||
| 382 | * We should only get in here for IODA1 configs. This is based on the | ||
| 383 | * fact that we using RTAS for MSIs, we don't have the 32 bit MSI RTAS | ||
| 384 | * support, and we are in a PCIe Gen2 slot. | ||
| 385 | */ | ||
| 386 | dev_info(&pdev->dev, | ||
| 387 | "rtas_msi: No 32 bit MSI firmware support, forcing 32 bit MSI\n"); | ||
| 388 | pci_read_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_HI, &addr_hi); | ||
| 389 | addr_lo = 0xffff0000 | ((addr_hi >> (48 - 32)) << 4); | ||
| 390 | pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_LO, addr_lo); | ||
| 391 | pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_ADDRESS_HI, 0); | ||
| 392 | } | ||
| 393 | |||
| 397 | static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type) | 394 | static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type) |
| 398 | { | 395 | { |
| 399 | struct pci_dn *pdn; | 396 | struct pci_dn *pdn; |
| @@ -401,8 +398,9 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type) | |||
| 401 | struct msi_desc *entry; | 398 | struct msi_desc *entry; |
| 402 | struct msi_msg msg; | 399 | struct msi_msg msg; |
| 403 | int nvec = nvec_in; | 400 | int nvec = nvec_in; |
| 401 | int use_32bit_msi_hack = 0; | ||
| 404 | 402 | ||
| 405 | pdn = get_pdn(pdev); | 403 | pdn = pci_get_pdn(pdev); |
| 406 | if (!pdn) | 404 | if (!pdn) |
| 407 | return -ENODEV; | 405 | return -ENODEV; |
| 408 | 406 | ||
| @@ -428,15 +426,31 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type) | |||
| 428 | */ | 426 | */ |
| 429 | again: | 427 | again: |
| 430 | if (type == PCI_CAP_ID_MSI) { | 428 | if (type == PCI_CAP_ID_MSI) { |
| 431 | if (pdn->force_32bit_msi) | 429 | if (pdn->force_32bit_msi) { |
| 432 | rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec); | 430 | rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec); |
| 433 | else | 431 | if (rc < 0) { |
| 432 | /* | ||
| 433 | * We only want to run the 32 bit MSI hack below if | ||
| 434 | * the max bus speed is Gen2 speed | ||
| 435 | */ | ||
| 436 | if (pdev->bus->max_bus_speed != PCIE_SPEED_5_0GT) | ||
| 437 | return rc; | ||
| 438 | |||
| 439 | use_32bit_msi_hack = 1; | ||
| 440 | } | ||
| 441 | } else | ||
| 442 | rc = -1; | ||
| 443 | |||
| 444 | if (rc < 0) | ||
| 434 | rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec); | 445 | rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec); |
| 435 | 446 | ||
| 436 | if (rc < 0 && !pdn->force_32bit_msi) { | 447 | if (rc < 0) { |
| 437 | pr_debug("rtas_msi: trying the old firmware call.\n"); | 448 | pr_debug("rtas_msi: trying the old firmware call.\n"); |
| 438 | rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec); | 449 | rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec); |
| 439 | } | 450 | } |
| 451 | |||
| 452 | if (use_32bit_msi_hack && rc > 0) | ||
| 453 | rtas_hack_32bit_msi_gen2(pdev); | ||
| 440 | } else | 454 | } else |
| 441 | rc = rtas_change_msi(pdn, RTAS_CHANGE_MSIX_FN, nvec); | 455 | rc = rtas_change_msi(pdn, RTAS_CHANGE_MSIX_FN, nvec); |
| 442 | 456 | ||
| @@ -518,12 +532,3 @@ static int rtas_msi_init(void) | |||
| 518 | } | 532 | } |
| 519 | arch_initcall(rtas_msi_init); | 533 | arch_initcall(rtas_msi_init); |
| 520 | 534 | ||
| 521 | static void quirk_radeon(struct pci_dev *dev) | ||
| 522 | { | ||
| 523 | struct pci_dn *pdn = get_pdn(dev); | ||
| 524 | |||
| 525 | if (pdn) | ||
| 526 | pdn->force_32bit_msi = 1; | ||
| 527 | } | ||
| 528 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon); | ||
| 529 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon); | ||
