PCI: Add quirk for Intel DH895xCC VF PCI config erratum
authorTadeusz Struk <tadeusz.struk@intel.com>
Fri, 7 Aug 2015 18:34:42 +0000 (11:34 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Mon, 10 Aug 2015 15:20:17 +0000 (23:20 +0800)
The PCI capabilities list for Intel DH895xCC VFs (device id 0x0443) with
QuickAssist Technology is prematurely terminated in hardware.
Workaround the issue by hard-coding the known expected next capability
pointer and saving the PCIE cap into internal buffer.

Patch generated against cryptodev-2.6

Signed-off-by: Tadeusz Struk <tadeusz.struk@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/pci/quirks.c

index e9fd0e90fa3b5c6656f7d8939a522add97c29b06..6a80f5ac7bd8523ef4d5b6dfb0b9035fc1eddefe 100644 (file)
@@ -4008,3 +4008,88 @@ void pci_dev_specific_enable_acs(struct pci_dev *dev)
                }
        }
 }
+
+/*
+ * The PCI capabilities list for Intel DH895xCC VFs (device id 0x0443) with
+ * QuickAssist Technology (QAT) is prematurely terminated in hardware.  The
+ * Next Capability pointer in the MSI Capability Structure should point to
+ * the PCIe Capability Structure but is incorrectly hardwired as 0 terminating
+ * the list.
+ */
+static void quirk_intel_qat_vf_cap(struct pci_dev *pdev)
+{
+       int pos, i = 0;
+       u8 next_cap;
+       u16 reg16, *cap;
+       struct pci_cap_saved_state *state;
+
+       /* Bail if the hardware bug is fixed */
+       if (pdev->pcie_cap || pci_find_capability(pdev, PCI_CAP_ID_EXP))
+               return;
+
+       /* Bail if MSI Capability Structure is not found for some reason */
+       pos = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+       if (!pos)
+               return;
+
+       /*
+        * Bail if Next Capability pointer in the MSI Capability Structure
+        * is not the expected incorrect 0x00.
+        */
+       pci_read_config_byte(pdev, pos + 1, &next_cap);
+       if (next_cap)
+               return;
+
+       /*
+        * PCIe Capability Structure is expected to be at 0x50 and should
+        * terminate the list (Next Capability pointer is 0x00).  Verify
+        * Capability Id and Next Capability pointer is as expected.
+        * Open-code some of set_pcie_port_type() and pci_cfg_space_size_ext()
+        * to correctly set kernel data structures which have already been
+        * set incorrectly due to the hardware bug.
+        */
+       pos = 0x50;
+       pci_read_config_word(pdev, pos, &reg16);
+       if (reg16 == (0x0000 | PCI_CAP_ID_EXP)) {
+               u32 status;
+#ifndef PCI_EXP_SAVE_REGS
+#define PCI_EXP_SAVE_REGS     7
+#endif
+               int size = PCI_EXP_SAVE_REGS * sizeof(u16);
+
+               pdev->pcie_cap = pos;
+               pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
+               pdev->pcie_flags_reg = reg16;
+               pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
+               pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+
+               pdev->cfg_size = PCI_CFG_SPACE_EXP_SIZE;
+               if (pci_read_config_dword(pdev, PCI_CFG_SPACE_SIZE, &status) !=
+                   PCIBIOS_SUCCESSFUL || (status == 0xffffffff))
+                       pdev->cfg_size = PCI_CFG_SPACE_SIZE;
+
+               if (pci_find_saved_cap(pdev, PCI_CAP_ID_EXP))
+                       return;
+
+               /*
+                * Save PCIE cap
+                */
+               state = kzalloc(sizeof(*state) + size, GFP_KERNEL);
+               if (!state)
+                       return;
+
+               state->cap.cap_nr = PCI_CAP_ID_EXP;
+               state->cap.cap_extended = 0;
+               state->cap.size = size;
+               cap = (u16 *)&state->cap.data[0];
+               pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_RTCTL,  &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_LNKCTL2, &cap[i++]);
+               pcie_capability_read_word(pdev, PCI_EXP_SLTCTL2, &cap[i++]);
+               hlist_add_head(&state->next, &pdev->saved_cap_space);
+       }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);