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 23static inline int hest_match_pci(struct acpi_hest_aer_common *p, 24 struct pci_dev *pci) 25{ 26 return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) && 27 ACPI_HEST_BUS(p->bus) == pci->bus->number && 28 p->device == PCI_SLOT(pci->devfn) && 29 p->function == PCI_FUNC(pci->devfn); 30} 31 32static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, 33 struct pci_dev *dev) 34{ 35 u16 hest_type = hest_hdr->type; 36 u8 pcie_type = pci_pcie_type(dev); 37 38 if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && 39 pcie_type == PCI_EXP_TYPE_ROOT_PORT) || 40 (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && 41 pcie_type == PCI_EXP_TYPE_ENDPOINT) || 42 (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && 43 (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) 44 return true; 45 return false; 46} 47 48struct aer_hest_parse_info { 49 struct pci_dev *pci_dev; 50 int firmware_first; 51}; 52 53static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr) 54{ 55 if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT || 56 hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT || 57 hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE) 58 return 1; 59 return 0; 60} 61 62static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) 63{ 64 struct aer_hest_parse_info *info = data; 65 struct acpi_hest_aer_common *p; 66 int ff; 67 68 if (!hest_source_is_pcie_aer(hest_hdr)) 69 return 0; 70 71 p = (struct acpi_hest_aer_common *)(hest_hdr + 1); 72 ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); 73 74 /* 75 * If no specific device is supplied, determine whether 76 * FIRMWARE_FIRST is set for *any* PCIe device. 77 */ 78 if (!info->pci_dev) { 79 info->firmware_first |= ff; 80 return 0; 81 } 82 83 /* Otherwise, check the specific device */ 84 if (p->flags & ACPI_HEST_GLOBAL) { 85 if (hest_match_type(hest_hdr, info->pci_dev)) 86 info->firmware_first = ff; 87 } else 88 if (hest_match_pci(p, info->pci_dev)) 89 info->firmware_first = ff; 90 91 return 0; 92} 93 94static void aer_set_firmware_first(struct pci_dev *pci_dev) 95{ 96 int rc; 97 struct aer_hest_parse_info info = { 98 .pci_dev = pci_dev, 99 .firmware_first = 0, 100 }; 101 102 rc = apei_hest_parse(aer_hest_parse, &info); 103 104 if (rc) 105 pci_dev->__aer_firmware_first = 0; 106 else 107 pci_dev->__aer_firmware_first = info.firmware_first; 108 pci_dev->__aer_firmware_first_valid = 1; 109} 110 111int pcie_aer_get_firmware_first(struct pci_dev *dev) 112{ 113 if (!pci_is_pcie(dev)) 114 return 0; 115 116 if (!dev->__aer_firmware_first_valid) 117 aer_set_firmware_first(dev); 118 return dev->__aer_firmware_first; 119} 120 121static bool aer_firmware_first; 122 123/** 124 * aer_acpi_firmware_first - Check if APEI should control AER. 125 */ 126bool aer_acpi_firmware_first(void) 127{ 128 static bool parsed = false; 129 struct aer_hest_parse_info info = { 130 .pci_dev = NULL, /* Check all PCIe devices */ 131 .firmware_first = 0, 132 }; 133 134 if (!parsed) { 135 apei_hest_parse(aer_hest_parse, &info); 136 aer_firmware_first = info.firmware_first; 137 parsed = true; 138 } 139 return aer_firmware_first; 140} 141#endif 142