// SPDX-License-Identifier: GPL-2.0
/*
* Implement the AER root port service driver. The driver registers an IRQ
* handler. When a root port triggers an AER interrupt, the IRQ handler
* collects root port status and schedules work.
*
* Copyright (C) 2006 Intel Corp.
* Tom Long Nguyen (tom.l.nguyen@intel.com)
* Zhang Yanmin (yanmin.zhang@intel.com)
*
* (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
* Andrew Patterson <andrew.patterson@hp.com>
*/
#define pr_fmt(fmt) "AER: " fmt
#define dev_fmt pr_fmt
#include <linux/bitops.h>
#include <linux/cper.h>
#include <linux/pci.h>
#include <linux/pci-acpi.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
#include <linux/slab.h>
#include <acpi/apei.h>
#include <acpi/ghes.h>
#include <ras/ras_event.h>
#include "../pci.h"
#include "portdrv.h"
#define AER_ERROR_SOURCES_MAX 128
#define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */
#define AER_MAX_TYPEOF_UNCOR_ERRS 27 /* as per PCI_ERR_UNCOR_STATUS*/
struct aer_err_source {
unsigned int status;
unsigned int id;
};
struct aer_rpc {
struct pci_dev *rpd; /* Root Port device */
DECLARE_KFIFO(aer_fifo, struct aer_err_source, AER_ERROR_SOURCES_MAX);
};
/* AER stats for the device */
struct aer_stats {
/*
* Fields for all AER capable devices. They indicate the errors
* "as seen by this device". Note that this may mean that if an
* end point is causing problems, the AER counters may increment
* at its link partner (e.g. root port) because the errors will be
* "seen" by the link partner and not the problematic end point
* itself (which may report all counters as 0 as it never saw any
* problems).
*/
/* Counters for different type of correctable errors */
u64 dev_cor_errs[AER_MAX_TYPEOF_COR_ERRS];
/* Counters for different type of fatal uncorrectable errors */
u64 dev_fatal_errs[AER_MAX_TYPEOF_UNCOR_ERRS];
/* Counters for different type of nonfatal uncorrectable errors */
u64 dev_nonfatal_errs[AER_MAX_TYPEOF_UNCOR_ERRS];
/* Total number of ERR_COR sent by this device */
u64 dev_total_cor_errs;
/* Total number of ERR_FATAL sent by this device */
u64 dev_total_fatal_errs;
/* Total number of ERR_NONFATAL sent by this device */
u64 dev_total_nonfatal_errs;
/*
* Fields for Root ports & root complex event collectors only, these
* indicate the total number of ERR_COR, ERR_FATAL, and ERR_NONFATAL
* messages received by the root port / event collector, INCLUDING the
* ones that are generated internally (by the rootport itself)
*/
u64 rootport_total_cor_errs;
u64 rootport_total_fatal_errs;
u64 rootport_total_nonfatal_errs;
};
#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
PCI_ERR_UNC_ECRC| \
PCI_ERR_UNC_UNSUP| \
PCI_ERR_UNC_COMP_ABORT| \
PCI_ERR_UNC_UNX_COMP| \
PCI_ERR_UNC_MALF_TLP)
#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
PCI_EXP_RTCTL_SENFEE| \
PCI_EXP_RTCTL_SEFEE)
#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \
PCI_ERR_ROOT_CMD_NONFATAL_EN| \
PCI_ERR_ROOT_CMD_FATAL_EN)
#define ERR_COR_ID(d) (d & 0xffff)
#define ERR_UNCOR_ID(d) (d >> 16)
#define AER_ERR_STATUS_MASK (PCI_ERR_ROOT_UNCOR_RCV | \
PCI_ERR_ROOT_COR_RCV | \
PCI_ERR_ROOT_MULTI_COR_RCV | \
PCI_ERR_ROOT_MULTI_UNCOR_RCV)
static int pcie_aer_disable;
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
void pci_no_aer(void)
{
pcie_aer_disable = 1;
}
bool pci_aer_available(void)
{
return !pcie_aer_disable && pci_msi_enabled();
}
#ifdef CONFIG_PCIE_ECRC
#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */
#define ECRC_POLICY_OFF 1 /* ECRC off for performance */
#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */
static int ecrc_policy = ECRC_POLICY_DEFAULT;
static const char * const ecrc_policy_str[] = {
[ECRC_POLICY_DEFAULT] = "bios",
[ECRC_POLICY_OFF] = "off",
[ECRC_POLICY_ON] = "on"
};
/**
* enable_ecrc_checking - enable PCIe ECRC checking for a device
* @dev: the PCI device
*
* Returns 0 on success, or negative on failure.
*/
static int enable_ecrc_checking(struct pci_dev *dev)
{
int aer = dev->aer_cap;
u32 reg32;
if (!aer)
return -ENODEV;
pci_read_config_dword(dev, aer + PCI_ERR_CAP, ®32);
if (reg32 & PCI_ERR_CAP_ECRC_GENC)
reg32 |= PCI_ERR_CAP_ECRC_GENE;
if (reg32 & PCI_ERR_CAP_ECRC_CHKC)
reg32 |= PCI_ERR_CAP_ECRC_CHKE;
pci_write_config_dword(dev, aer + PCI_ERR_CAP, reg32);
return 0;
}
/**
* disable_ecrc_checking - disables PCIe ECRC checking for a device
* @dev: the PCI device
*
* Returns 0 on success, or negative on failure.
*/
static int disable_ecrc_checking(struct pci_dev *dev)
{
int aer = dev->aer_cap;
u32 reg32;
if (!aer)
return -ENODEV;
pci_read_config_dword(dev, aer + PCI_ERR_CAP, ®32);
reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE);
pci_write_config_dword(dev, aer + PCI_ERR_CAP, reg32);
return 0;
}
/**
* pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy
* @dev: the PCI device
*/
void pcie_set_ecrc_checking(struct pci_dev *dev)
{
if (!pcie_aer_is_native(dev))
return;
switch (ecrc_policy) {
case ECRC_POLICY_DEFAULT:
return;
case ECRC_POLICY_OFF:
disable_ecrc_checking(dev);
break;
case ECRC_POLICY_ON:
enable_ecrc_checking(dev);
break;
default:
return;
}
}
/**
* pcie_ecrc_get_policy -
|