• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
19 #include <acpi/apei.h>
20 #include "aerdrv.h"
21 
22 #ifdef CONFIG_ACPI_APEI
hest_match_pci(struct acpi_hest_aer_common * p,struct pci_dev * pci)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 
aer_hest_parse(struct acpi_hest_header * hest_hdr,void * data)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) {
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 
aer_set_firmware_first(struct pci_dev * pci_dev)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 
pcie_aer_get_firmware_first(struct pci_dev * dev)90 int 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 }
96 
97 static bool aer_firmware_first;
98 
aer_hest_parse_aff(struct acpi_hest_header * hest_hdr,void * data)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) {
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  */
aer_acpi_firmware_first(void)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
131