]>
Commit | Line | Data |
---|---|---|
4e52de75 FG |
1 | From: Alex Williamson <alex.williamson@redhat.com> |
2 | Subject: [PATCH 2/2] PCI: Quirk PCH root port ACS for Sunrise Point | |
3 | ||
4 | As noted in the comments, these root ports attempted to implement ACS | |
5 | but used dwords for the capability and control registers, putting the | |
6 | control register at the wrong offset. We use quirks to enable and | |
7 | test ACS for these devices, which match the standard functions modulo | |
8 | the broken control register offset. | |
9 | ||
10 | Signed-off-by: Alex Williamson <alex.williamson@redhat.com> | |
11 | --- | |
12 | drivers/pci/quirks.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ | |
13 | 1 file changed, 78 insertions(+) | |
14 | ||
15 | diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c | |
16 | index 701fad6..d6606e4 100644 | |
17 | --- a/drivers/pci/quirks.c | |
18 | +++ b/drivers/pci/quirks.c | |
19 | @@ -3992,6 +3992,55 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) | |
20 | return acs_flags & ~flags ? 0 : 1; | |
21 | } | |
22 | ||
23 | +/* | |
24 | + * Sunrise Point PCH root ports implement ACS, but unfortunately as shown in | |
25 | + * the datasheet (Intel 100 Series Chipset Family PCH Datasheet, Vol. 2, | |
26 | + * 12.1.46, 12.1.47)[1] this chipset uses dwords for the ACS capability and | |
27 | + * control registers whereas the PCIe spec packs them into words (Rev 3.0, | |
28 | + * 7.16 ACS Extended Capability). The bit definitions are correct, but the | |
29 | + * control register is at offset 8 instead of 6 and we should probably use | |
30 | + * dword accesses to them. This applies to the following PCI Device IDs, as | |
31 | + * found in volume 1 of the datasheet[2]: | |
32 | + * | |
33 | + * 0xa110-0xa11f Sunrise Point-H PCI Express Root Port #{0-16} | |
34 | + * 0xa167-0xa16a Sunrise Point-H PCI Express Root Port #{17-20} | |
35 | + * | |
36 | + * NB. This doesn't fix what lspci shows. | |
37 | + * | |
38 | + * [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html | |
39 | + * [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html | |
40 | + */ | |
41 | +static bool pci_quirk_intel_spt_pch_acs_match(struct pci_dev *dev) | |
42 | +{ | |
43 | + return pci_is_pcie(dev) && | |
44 | + pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT && | |
45 | + ((dev->device & ~0xf) == 0xa110 || | |
46 | + (dev->device >= 0xa167 && dev->device <= 0xa16a)); | |
47 | +} | |
48 | + | |
49 | +#define INTEL_SPT_ACS_CTRL (PCI_ACS_CAP + 4) | |
50 | + | |
51 | +static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags) | |
52 | +{ | |
53 | + int pos; | |
54 | + u32 cap, ctrl; | |
55 | + | |
56 | + if (!pci_quirk_intel_spt_pch_acs_match(dev)) | |
57 | + return -ENOTTY; | |
58 | + | |
59 | + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); | |
60 | + if (!pos) | |
61 | + return -ENOTTY; | |
62 | + | |
63 | + /* see pci_acs_flags_enabled() */ | |
64 | + pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap); | |
65 | + acs_flags &= (cap | PCI_ACS_EC); | |
66 | + | |
67 | + pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl); | |
68 | + | |
69 | + return acs_flags & ~ctrl ? 0 : 1; | |
70 | +} | |
71 | + | |
72 | static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags) | |
73 | { | |
74 | /* | |
75 | @@ -4024,6 +4073,7 @@ static const struct pci_dev_acs_enabled { | |
76 | { PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs }, | |
77 | /* Intel PCH root ports */ | |
78 | { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, | |
79 | + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs }, | |
80 | { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ | |
81 | { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */ | |
82 | /* Cavium ThunderX */ | |
83 | @@ -4159,12 +4209,40 @@ static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) | |
84 | return 0; | |
85 | } | |
86 | ||
87 | +static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev) | |
88 | +{ | |
89 | + int pos; | |
90 | + u32 cap, ctrl; | |
91 | + | |
92 | + if (!pci_quirk_intel_spt_pch_acs_match(dev)) | |
93 | + return -ENOTTY; | |
94 | + | |
95 | + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); | |
96 | + if (!pos) | |
97 | + return -ENOTTY; | |
98 | + | |
99 | + pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap); | |
100 | + pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl); | |
101 | + | |
102 | + ctrl |= (cap & PCI_ACS_SV); | |
103 | + ctrl |= (cap & PCI_ACS_RR); | |
104 | + ctrl |= (cap & PCI_ACS_CR); | |
105 | + ctrl |= (cap & PCI_ACS_UF); | |
106 | + | |
107 | + pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl); | |
108 | + | |
109 | + dev_info(&dev->dev, "Intel SPT PCH root port ACS workaround enabled\n"); | |
110 | + | |
111 | + return 0; | |
112 | +} | |
113 | + | |
114 | static const struct pci_dev_enable_acs { | |
115 | u16 vendor; | |
116 | u16 device; | |
117 | int (*enable_acs)(struct pci_dev *dev); | |
118 | } pci_dev_enable_acs[] = { | |
119 | { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs }, | |
120 | + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_spt_pch_acs }, | |
121 | { 0 } | |
122 | }; | |
123 |