Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
aerdrv_acpi.c
Go to the documentation of this file.
1 /*
2  * Access ACPI _OSC method
3  *
4  * Copyright (C) 2006 Intel Corp.
5  * Tom Long Nguyen ([email protected])
6  * Zhang Yanmin ([email protected])
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>
19 #include <acpi/apei.h>
20 #include "aerdrv.h"
21 
22 #ifdef CONFIG_ACPI_APEI
23 static 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 
32 struct aer_hest_parse_info {
33  struct pci_dev *pci_dev;
34  int firmware_first;
35 };
36 
37 static 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) {
47  pcie_type = PCI_EXP_TYPE_ROOT_PORT;
48  break;
50  pcie_type = PCI_EXP_TYPE_ENDPOINT;
51  break;
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 ((pci_is_pcie(info->pci_dev) &&
63  pci_pcie_type(info->pci_dev) == 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 
73 static 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 
90 int pcie_aer_get_firmware_first(struct pci_dev *dev)
91 {
93  aer_set_firmware_first(dev);
94  return dev->__aer_firmware_first;
95 }
96 
97 static bool aer_firmware_first;
98 
99 static 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) {
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 
120 bool 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 }
130 #endif