]>
Commit | Line | Data |
---|---|---|
6c2b374d ZY |
1 | /* |
2 | * Access ACPI _OSC method | |
3 | * | |
4 | * Copyright (C) 2006 Intel Corp. | |
5 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | |
6 | * Zhang Yanmin (yanmin.zhang@intel.com) | |
7 | * | |
8 | */ | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/pci.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/errno.h> | |
14 | #include <linux/pm.h> | |
15 | #include <linux/suspend.h> | |
16 | #include <linux/acpi.h> | |
17 | #include <linux/pci-acpi.h> | |
18 | #include <linux/delay.h> | |
affb72c3 | 19 | #include <acpi/apei.h> |
6c2b374d ZY |
20 | #include "aerdrv.h" |
21 | ||
22 | /** | |
23 | * aer_osc_setup - run ACPI _OSC method | |
8d29bfb7 | 24 | * @pciedev: pcie_device which AER is being enabled on |
6c2b374d | 25 | * |
8d29bfb7 | 26 | * @return: Zero on success. Nonzero otherwise. |
6c2b374d | 27 | * |
45e829ea | 28 | * Invoked when PCIe bus loads AER service driver. To avoid conflict with |
6c2b374d ZY |
29 | * BIOS AER support requires BIOS to yield AER control to OS native driver. |
30 | **/ | |
8d29bfb7 | 31 | int aer_osc_setup(struct pcie_device *pciedev) |
6c2b374d | 32 | { |
8d29bfb7 ZY |
33 | acpi_status status = AE_NOT_FOUND; |
34 | struct pci_dev *pdev = pciedev->port; | |
a01e035e | 35 | acpi_handle handle = NULL; |
6c2b374d | 36 | |
4c44bac8 YL |
37 | if (acpi_pci_disabled) |
38 | return -1; | |
39 | ||
056c58e8 | 40 | handle = acpi_find_root_bridge_handle(pdev); |
8d29bfb7 | 41 | if (handle) { |
75fb60f2 RW |
42 | u32 flags = OSC_PCI_EXPRESS_AER_CONTROL | |
43 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL; | |
44 | status = acpi_pci_osc_control_set(handle, &flags, flags); | |
8d29bfb7 | 45 | } |
6c2b374d | 46 | |
6c2b374d | 47 | if (ACPI_FAILURE(status)) { |
531f254e BH |
48 | dev_printk(KERN_DEBUG, &pciedev->device, "AER service couldn't " |
49 | "init device: %s\n", | |
50 | (status == AE_SUPPORT || status == AE_NOT_FOUND) ? | |
51 | "no _OSC support" : "_OSC failed"); | |
8d29bfb7 | 52 | return -1; |
6c2b374d ZY |
53 | } |
54 | ||
8d29bfb7 | 55 | return 0; |
6c2b374d | 56 | } |
affb72c3 HY |
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 |