diff options
Diffstat (limited to 'drivers/pci/pcie/aer/aerdrv_acpi.c')
-rw-r--r-- | drivers/pci/pcie/aer/aerdrv_acpi.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 04814087658..f278d7b0d95 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/acpi.h> | 16 | #include <linux/acpi.h> |
17 | #include <linux/pci-acpi.h> | 17 | #include <linux/pci-acpi.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <acpi/apei.h> | ||
19 | #include "aerdrv.h" | 20 | #include "aerdrv.h" |
20 | 21 | ||
21 | /** | 22 | /** |
@@ -53,3 +54,79 @@ int aer_osc_setup(struct pcie_device *pciedev) | |||
53 | 54 | ||
54 | return 0; | 55 | return 0; |
55 | } | 56 | } |
57 | |||
58 | #ifdef CONFIG_ACPI_APEI | ||
59 | static inline int hest_match_pci(struct acpi_hest_aer_common *p, | ||
60 | struct pci_dev *pci) | ||
61 | { | ||
62 | return (0 == pci_domain_nr(pci->bus) && | ||
63 | p->bus == pci->bus->number && | ||
64 | p->device == PCI_SLOT(pci->devfn) && | ||
65 | p->function == PCI_FUNC(pci->devfn)); | ||
66 | } | ||
67 | |||
68 | struct aer_hest_parse_info { | ||
69 | struct pci_dev *pci_dev; | ||
70 | int firmware_first; | ||
71 | }; | ||
72 | |||
73 | static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) | ||
74 | { | ||
75 | struct aer_hest_parse_info *info = data; | ||
76 | struct acpi_hest_aer_common *p; | ||
77 | u8 pcie_type = 0; | ||
78 | u8 bridge = 0; | ||
79 | int ff = 0; | ||
80 | |||
81 | switch (hest_hdr->type) { | ||
82 | case ACPI_HEST_TYPE_AER_ROOT_PORT: | ||
83 | pcie_type = PCI_EXP_TYPE_ROOT_PORT; | ||
84 | break; | ||
85 | case ACPI_HEST_TYPE_AER_ENDPOINT: | ||
86 | pcie_type = PCI_EXP_TYPE_ENDPOINT; | ||
87 | break; | ||
88 | case ACPI_HEST_TYPE_AER_BRIDGE: | ||
89 | if ((info->pci_dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) | ||
90 | bridge = 1; | ||
91 | break; | ||
92 | default: | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | p = (struct acpi_hest_aer_common *)(hest_hdr + 1); | ||
97 | if (p->flags & ACPI_HEST_GLOBAL) { | ||
98 | if ((info->pci_dev->is_pcie && | ||
99 | info->pci_dev->pcie_type == pcie_type) || bridge) | ||
100 | ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); | ||
101 | } else | ||
102 | if (hest_match_pci(p, info->pci_dev)) | ||
103 | ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); | ||
104 | info->firmware_first = ff; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static void aer_set_firmware_first(struct pci_dev *pci_dev) | ||
110 | { | ||
111 | int rc; | ||
112 | struct aer_hest_parse_info info = { | ||
113 | .pci_dev = pci_dev, | ||
114 | .firmware_first = 0, | ||
115 | }; | ||
116 | |||
117 | rc = apei_hest_parse(aer_hest_parse, &info); | ||
118 | |||
119 | if (rc) | ||
120 | pci_dev->__aer_firmware_first = 0; | ||
121 | else | ||
122 | pci_dev->__aer_firmware_first = info.firmware_first; | ||
123 | pci_dev->__aer_firmware_first_valid = 1; | ||
124 | } | ||
125 | |||
126 | int pcie_aer_get_firmware_first(struct pci_dev *dev) | ||
127 | { | ||
128 | if (!dev->__aer_firmware_first_valid) | ||
129 | aer_set_firmware_first(dev); | ||
130 | return dev->__aer_firmware_first; | ||
131 | } | ||
132 | #endif | ||