powerpc/pseries: Force 32 bit MSIs for devices that require it
authorBrian King <brking@linux.vnet.ibm.com>
Fri, 3 May 2013 11:30:59 +0000 (11:30 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Sun, 5 May 2013 23:25:37 +0000 (09:25 +1000)
The following patch implements a new PAPR change which allows
the OS to force the use of 32 bit MSIs, regardless of what
the PCI capabilities indicate. This is required for some
devices that advertise support for 64 bit MSIs but don't
actually support them.

Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/platforms/pseries/msi.c

index ffbc5fd549acbb72bcab569612ea38d4dd0962cb..0694f73db22dd728e1f8e25d81371ef83553e504 100644 (file)
@@ -163,6 +163,8 @@ struct pci_dn {
 
        int     pci_ext_config_space;   /* for pci devices */
 
+       int     force_32bit_msi:1;
+
        struct  pci_dev *pcidev;        /* back-pointer to the pci device */
 #ifdef CONFIG_EEH
        struct eeh_dev *edev;           /* eeh device */
index e5b084723131cc1ef0b6260f2a571f7071ee5995..420524e6f8c95c6118462cefb80723312d213437 100644 (file)
@@ -24,6 +24,7 @@ static int query_token, change_token;
 #define RTAS_RESET_FN          2
 #define RTAS_CHANGE_MSI_FN     3
 #define RTAS_CHANGE_MSIX_FN    4
+#define RTAS_CHANGE_32MSI_FN   5
 
 static struct pci_dn *get_pdn(struct pci_dev *pdev)
 {
@@ -58,7 +59,8 @@ static int rtas_change_msi(struct pci_dn *pdn, u32 func, u32 num_irqs)
 
        seq_num = 1;
        do {
-               if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN)
+               if (func == RTAS_CHANGE_MSI_FN || func == RTAS_CHANGE_MSIX_FN ||
+                   func == RTAS_CHANGE_32MSI_FN)
                        rc = rtas_call(change_token, 6, 4, rtas_ret, addr,
                                        BUID_HI(buid), BUID_LO(buid),
                                        func, num_irqs, seq_num);
@@ -426,9 +428,12 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
         */
 again:
        if (type == PCI_CAP_ID_MSI) {
-               rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
+               if (pdn->force_32bit_msi)
+                       rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
+               else
+                       rc = rtas_change_msi(pdn, RTAS_CHANGE_MSI_FN, nvec);
 
-               if (rc < 0) {
+               if (rc < 0 && !pdn->force_32bit_msi) {
                        pr_debug("rtas_msi: trying the old firmware call.\n");
                        rc = rtas_change_msi(pdn, RTAS_CHANGE_FN, nvec);
                }
@@ -512,3 +517,13 @@ static int rtas_msi_init(void)
        return 0;
 }
 arch_initcall(rtas_msi_init);
+
+static void quirk_radeon(struct pci_dev *dev)
+{
+       struct pci_dn *pdn = get_pdn(dev);
+
+       if (pdn)
+               pdn->force_32bit_msi = 1;
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon);