Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
6c2b374d ZY |
2 | /* |
3 | * Access ACPI _OSC method | |
4 | * | |
5 | * Copyright (C) 2006 Intel Corp. | |
6 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | |
7 | * Zhang Yanmin (yanmin.zhang@intel.com) | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/pci.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/errno.h> | |
15 | #include <linux/pm.h> | |
16 | #include <linux/suspend.h> | |
17 | #include <linux/acpi.h> | |
18 | #include <linux/pci-acpi.h> | |
19 | #include <linux/delay.h> | |
affb72c3 | 20 | #include <acpi/apei.h> |
6c2b374d ZY |
21 | #include "aerdrv.h" |
22 | ||
affb72c3 HY |
23 | #ifdef CONFIG_ACPI_APEI |
24 | static inline int hest_match_pci(struct acpi_hest_aer_common *p, | |
25 | struct pci_dev *pci) | |
26 | { | |
339c0fc5 BD |
27 | return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && |
28 | ACPI_HEST_BUS(p->bus) == pci->bus->number && | |
29 | p->device == PCI_SLOT(pci->devfn) && | |
30 | p->function == PCI_FUNC(pci->devfn); | |
affb72c3 HY |
31 | } |
32 | ||
a6bd73cd BH |
33 | static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, |
34 | struct pci_dev *dev) | |
35 | { | |
36 | u16 hest_type = hest_hdr->type; | |
37 | u8 pcie_type = pci_pcie_type(dev); | |
38 | ||
39 | if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && | |
40 | pcie_type == PCI_EXP_TYPE_ROOT_PORT) || | |
41 | (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && | |
42 | pcie_type == PCI_EXP_TYPE_ENDPOINT) || | |
43 | (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && | |
44 | (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) | |
45 | return true; | |
46 | return false; | |
47 | } | |
48 | ||
affb72c3 HY |
49 | struct aer_hest_parse_info { |
50 | struct pci_dev *pci_dev; | |
51 | int firmware_first; | |
52 | }; | |
53 | ||
8e7f8d0b BD |
54 | static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) |
55 | { | |
56 | if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || | |
57 | hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || | |
58 | hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) | |
59 | return 1; | |
60 | return 0; | |
61 | } | |
62 | ||
affb72c3 HY |
63 | static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) |
64 | { | |
65 | struct aer_hest_parse_info *info = data; | |
66 | struct acpi_hest_aer_common *p; | |
8d2a171f | 67 | int ff; |
affb72c3 | 68 | |
8e7f8d0b BD |
69 | if (!hest_source_is_pcie_aer(hest_hdr)) |
70 | return 0; | |
71 | ||
affb72c3 | 72 | p = (struct acpi_hest_aer_common *)(hest_hdr + 1); |
8d2a171f | 73 | ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); |
3620437a BH |
74 | |
75 | /* | |
76 | * If no specific device is supplied, determine whether | |
77 | * FIRMWARE_FIRST is set for *any* PCIe device. | |
78 | */ | |
79 | if (!info->pci_dev) { | |
80 | info->firmware_first |= ff; | |
81 | return 0; | |
82 | } | |
83 | ||
84 | /* Otherwise, check the specific device */ | |
affb72c3 | 85 | if (p->flags & ACPI_HEST_GLOBAL) { |
a6bd73cd | 86 | if (hest_match_type(hest_hdr, info->pci_dev)) |
8d2a171f | 87 | info->firmware_first = ff; |
affb72c3 HY |
88 | } else |
89 | if (hest_match_pci(p, info->pci_dev)) | |
8d2a171f | 90 | info->firmware_first = ff; |
affb72c3 HY |
91 | |
92 | return 0; | |
93 | } | |
94 | ||
95 | static void aer_set_firmware_first(struct pci_dev *pci_dev) | |
96 | { | |
97 | int rc; | |
98 | struct aer_hest_parse_info info = { | |
99 | .pci_dev = pci_dev, | |
100 | .firmware_first = 0, | |
101 | }; | |
102 | ||
103 | rc = apei_hest_parse(aer_hest_parse, &info); | |
104 | ||
105 | if (rc) | |
106 | pci_dev->__aer_firmware_first = 0; | |
107 | else | |
108 | pci_dev->__aer_firmware_first = info.firmware_first; | |
109 | pci_dev->__aer_firmware_first_valid = 1; | |
110 | } | |
111 | ||
112 | int pcie_aer_get_firmware_first(struct pci_dev *dev) | |
113 | { | |
8b8d2b65 BH |
114 | if (!pci_is_pcie(dev)) |
115 | return 0; | |
116 | ||
affb72c3 HY |
117 | if (!dev->__aer_firmware_first_valid) |
118 | aer_set_firmware_first(dev); | |
119 | return dev->__aer_firmware_first; | |
120 | } | |
b22c3d82 RW |
121 | |
122 | static bool aer_firmware_first; | |
123 | ||
b22c3d82 RW |
124 | /** |
125 | * aer_acpi_firmware_first - Check if APEI should control AER. | |
126 | */ | |
127 | bool aer_acpi_firmware_first(void) | |
128 | { | |
129 | static bool parsed = false; | |
3620437a BH |
130 | struct aer_hest_parse_info info = { |
131 | .pci_dev = NULL, /* Check all PCIe devices */ | |
132 | .firmware_first = 0, | |
133 | }; | |
b22c3d82 RW |
134 | |
135 | if (!parsed) { | |
3620437a BH |
136 | apei_hest_parse(aer_hest_parse, &info); |
137 | aer_firmware_first = info.firmware_first; | |
b22c3d82 RW |
138 | parsed = true; |
139 | } | |
140 | return aer_firmware_first; | |
141 | } | |
affb72c3 | 142 | #endif |