linux/drivers/pci/pcie/aer/aerdrv_acpi.c
<<
>>
Prefs
   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