blob: 275bf158ffa7184978e33fb23124888e25570cc2 [file] [log] [blame]
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +08001/*
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>
Huang Yingaffb72c2010-05-18 14:35:16 +080019#include <acpi/apei.h>
Zhang, Yanmin6c2b3742006-07-31 15:21:33 +080020#include "aerdrv.h"
21
Huang Yingaffb72c2010-05-18 14:35:16 +080022#ifdef CONFIG_ACPI_APEI
23static inline int hest_match_pci(struct acpi_hest_aer_common *p,
24 struct pci_dev *pci)
25{
26 return (0 == pci_domain_nr(pci->bus) &&
27 p->bus == pci->bus->number &&
28 p->device == PCI_SLOT(pci->devfn) &&
29 p->function == PCI_FUNC(pci->devfn));
30}
31
32struct aer_hest_parse_info {
33 struct pci_dev *pci_dev;
34 int firmware_first;
35};
36
37static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
38{
39 struct aer_hest_parse_info *info = data;
40 struct acpi_hest_aer_common *p;
41 u8 pcie_type = 0;
42 u8 bridge = 0;
43 int ff = 0;
44
45 switch (hest_hdr->type) {
46 case ACPI_HEST_TYPE_AER_ROOT_PORT:
47 pcie_type = PCI_EXP_TYPE_ROOT_PORT;
48 break;
49 case ACPI_HEST_TYPE_AER_ENDPOINT:
50 pcie_type = PCI_EXP_TYPE_ENDPOINT;
51 break;
52 case ACPI_HEST_TYPE_AER_BRIDGE:
53 if ((info->pci_dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
54 bridge = 1;
55 break;
56 default:
57 return 0;
58 }
59
60 p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
61 if (p->flags & ACPI_HEST_GLOBAL) {
62 if ((info->pci_dev->is_pcie &&
63 info->pci_dev->pcie_type == pcie_type) || bridge)
64 ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
65 } else
66 if (hest_match_pci(p, info->pci_dev))
67 ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
68 info->firmware_first = ff;
69
70 return 0;
71}
72
73static void aer_set_firmware_first(struct pci_dev *pci_dev)
74{
75 int rc;
76 struct aer_hest_parse_info info = {
77 .pci_dev = pci_dev,
78 .firmware_first = 0,
79 };
80
81 rc = apei_hest_parse(aer_hest_parse, &info);
82
83 if (rc)
84 pci_dev->__aer_firmware_first = 0;
85 else
86 pci_dev->__aer_firmware_first = info.firmware_first;
87 pci_dev->__aer_firmware_first_valid = 1;
88}
89
90int pcie_aer_get_firmware_first(struct pci_dev *dev)
91{
92 if (!dev->__aer_firmware_first_valid)
93 aer_set_firmware_first(dev);
94 return dev->__aer_firmware_first;
95}
Rafael J. Wysockib22c3d82010-09-20 18:50:00 +020096
97static bool aer_firmware_first;
98
99static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data)
100{
101 struct acpi_hest_aer_common *p;
102
103 if (aer_firmware_first)
104 return 0;
105
106 switch (hest_hdr->type) {
107 case ACPI_HEST_TYPE_AER_ROOT_PORT:
108 case ACPI_HEST_TYPE_AER_ENDPOINT:
109 case ACPI_HEST_TYPE_AER_BRIDGE:
110 p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
111 aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
112 default:
113 return 0;
114 }
115}
116
117/**
118 * aer_acpi_firmware_first - Check if APEI should control AER.
119 */
120bool aer_acpi_firmware_first(void)
121{
122 static bool parsed = false;
123
124 if (!parsed) {
125 apei_hest_parse(aer_hest_parse_aff, NULL);
126 parsed = true;
127 }
128 return aer_firmware_first;
129}
Huang Yingaffb72c2010-05-18 14:35:16 +0800130#endif