|
|
|
@ -136,22 +136,18 @@ static const char * const ecrc_policy_str[] = {
|
|
|
|
|
*/
|
|
|
|
|
static int enable_ecrc_checking(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 reg32;
|
|
|
|
|
|
|
|
|
|
if (!pci_is_pcie(dev))
|
|
|
|
|
if (!aer)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
|
|
|
|
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, pos + PCI_ERR_CAP, reg32);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_CAP, reg32);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -164,19 +160,15 @@ static int enable_ecrc_checking(struct pci_dev *dev)
|
|
|
|
|
*/
|
|
|
|
|
static int disable_ecrc_checking(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 reg32;
|
|
|
|
|
|
|
|
|
|
if (!pci_is_pcie(dev))
|
|
|
|
|
if (!aer)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32);
|
|
|
|
|
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, pos + PCI_ERR_CAP, reg32);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_CAP, reg32);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -217,142 +209,22 @@ void pcie_ecrc_get_policy(char *str)
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_PCIE_ECRC */
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_ACPI_APEI
|
|
|
|
|
static inline int hest_match_pci(struct acpi_hest_aer_common *p,
|
|
|
|
|
struct pci_dev *pci)
|
|
|
|
|
{
|
|
|
|
|
return ACPI_HEST_SEGMENT(p->bus) == pci_domain_nr(pci->bus) &&
|
|
|
|
|
ACPI_HEST_BUS(p->bus) == pci->bus->number &&
|
|
|
|
|
p->device == PCI_SLOT(pci->devfn) &&
|
|
|
|
|
p->function == PCI_FUNC(pci->devfn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool hest_match_type(struct acpi_hest_header *hest_hdr,
|
|
|
|
|
struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
u16 hest_type = hest_hdr->type;
|
|
|
|
|
u8 pcie_type = pci_pcie_type(dev);
|
|
|
|
|
|
|
|
|
|
if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT &&
|
|
|
|
|
pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
|
|
|
|
|
(hest_type == ACPI_HEST_TYPE_AER_ENDPOINT &&
|
|
|
|
|
pcie_type == PCI_EXP_TYPE_ENDPOINT) ||
|
|
|
|
|
(hest_type == ACPI_HEST_TYPE_AER_BRIDGE &&
|
|
|
|
|
(dev->class >> 16) == PCI_BASE_CLASS_BRIDGE))
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct aer_hest_parse_info {
|
|
|
|
|
struct pci_dev *pci_dev;
|
|
|
|
|
int firmware_first;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int hest_source_is_pcie_aer(struct acpi_hest_header *hest_hdr)
|
|
|
|
|
{
|
|
|
|
|
if (hest_hdr->type == ACPI_HEST_TYPE_AER_ROOT_PORT ||
|
|
|
|
|
hest_hdr->type == ACPI_HEST_TYPE_AER_ENDPOINT ||
|
|
|
|
|
hest_hdr->type == ACPI_HEST_TYPE_AER_BRIDGE)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct aer_hest_parse_info *info = data;
|
|
|
|
|
struct acpi_hest_aer_common *p;
|
|
|
|
|
int ff;
|
|
|
|
|
|
|
|
|
|
if (!hest_source_is_pcie_aer(hest_hdr))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
|
|
|
|
|
ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If no specific device is supplied, determine whether
|
|
|
|
|
* FIRMWARE_FIRST is set for *any* PCIe device.
|
|
|
|
|
*/
|
|
|
|
|
if (!info->pci_dev) {
|
|
|
|
|
info->firmware_first |= ff;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise, check the specific device */
|
|
|
|
|
if (p->flags & ACPI_HEST_GLOBAL) {
|
|
|
|
|
if (hest_match_type(hest_hdr, info->pci_dev))
|
|
|
|
|
info->firmware_first = ff;
|
|
|
|
|
} else
|
|
|
|
|
if (hest_match_pci(p, info->pci_dev))
|
|
|
|
|
info->firmware_first = ff;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void aer_set_firmware_first(struct pci_dev *pci_dev)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
struct aer_hest_parse_info info = {
|
|
|
|
|
.pci_dev = pci_dev,
|
|
|
|
|
.firmware_first = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rc = apei_hest_parse(aer_hest_parse, &info);
|
|
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
|
pci_dev->__aer_firmware_first = 0;
|
|
|
|
|
else
|
|
|
|
|
pci_dev->__aer_firmware_first = info.firmware_first;
|
|
|
|
|
pci_dev->__aer_firmware_first_valid = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pcie_aer_get_firmware_first(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (!pci_is_pcie(dev))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (pcie_ports_native)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (!dev->__aer_firmware_first_valid)
|
|
|
|
|
aer_set_firmware_first(dev);
|
|
|
|
|
return dev->__aer_firmware_first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool aer_firmware_first;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* aer_acpi_firmware_first - Check if APEI should control AER.
|
|
|
|
|
*/
|
|
|
|
|
bool aer_acpi_firmware_first(void)
|
|
|
|
|
{
|
|
|
|
|
static bool parsed = false;
|
|
|
|
|
struct aer_hest_parse_info info = {
|
|
|
|
|
.pci_dev = NULL, /* Check all PCIe devices */
|
|
|
|
|
.firmware_first = 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (pcie_ports_native)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!parsed) {
|
|
|
|
|
apei_hest_parse(aer_hest_parse, &info);
|
|
|
|
|
aer_firmware_first = info.firmware_first;
|
|
|
|
|
parsed = true;
|
|
|
|
|
}
|
|
|
|
|
return aer_firmware_first;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
|
|
|
|
|
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
|
|
|
|
|
|
|
|
|
|
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
|
|
|
|
int pcie_aer_is_native(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (pcie_aer_get_firmware_first(dev))
|
|
|
|
|
return -EIO;
|
|
|
|
|
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
|
|
|
|
|
|
|
|
|
|
if (!dev->aer_cap)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return pcie_ports_native || host->native_aer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (!pcie_aer_is_native(dev))
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
|
|
|
|
@ -361,7 +233,7 @@ EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
|
|
|
|
|
|
|
|
|
|
int pci_disable_pcie_error_reporting(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (pcie_aer_get_firmware_first(dev))
|
|
|
|
|
if (!pcie_aer_is_native(dev))
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
|
|
|
|
@ -379,22 +251,18 @@ void pci_aer_clear_device_status(struct pci_dev *dev)
|
|
|
|
|
|
|
|
|
|
int pci_aer_clear_nonfatal_status(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 status, sev;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
if (pcie_aer_get_firmware_first(dev))
|
|
|
|
|
if (!pcie_aer_is_native(dev))
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
/* Clear status bits for ERR_NONFATAL errors only */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, &sev);
|
|
|
|
|
status &= ~sev;
|
|
|
|
|
if (status)
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -402,22 +270,18 @@ EXPORT_SYMBOL_GPL(pci_aer_clear_nonfatal_status);
|
|
|
|
|
|
|
|
|
|
void pci_aer_clear_fatal_status(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 status, sev;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pcie_aer_get_firmware_first(dev))
|
|
|
|
|
if (!pcie_aer_is_native(dev))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Clear status bits for ERR_FATAL errors only */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &sev);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, &sev);
|
|
|
|
|
status &= sev;
|
|
|
|
|
if (status)
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -431,35 +295,31 @@ void pci_aer_clear_fatal_status(struct pci_dev *dev)
|
|
|
|
|
*/
|
|
|
|
|
int pci_aer_raw_clear_status(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 status;
|
|
|
|
|
int port_type;
|
|
|
|
|
|
|
|
|
|
if (!pci_is_pcie(dev))
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
if (!aer)
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
port_type = pci_pcie_type(dev);
|
|
|
|
|
if (port_type == PCI_EXP_TYPE_ROOT_PORT) {
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS, status);
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, status);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pci_aer_clear_status(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
if (pcie_aer_get_firmware_first(dev))
|
|
|
|
|
if (!pcie_aer_is_native(dev))
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
return pci_aer_raw_clear_status(dev);
|
|
|
|
@ -467,12 +327,11 @@ int pci_aer_clear_status(struct pci_dev *dev)
|
|
|
|
|
|
|
|
|
|
void pci_save_aer_state(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
struct pci_cap_saved_state *save_state;
|
|
|
|
|
u32 *cap;
|
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
if (!aer)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ERR);
|
|
|
|
@ -480,22 +339,21 @@ void pci_save_aer_state(struct pci_dev *dev)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cap = &save_state->cap.data[0];
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, cap++);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, cap++);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, cap++);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_CAP, cap++);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, cap++);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, cap++);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, cap++);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_CAP, cap++);
|
|
|
|
|
if (pcie_cap_has_rtctl(dev))
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, cap++);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, cap++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pci_restore_aer_state(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
struct pci_cap_saved_state *save_state;
|
|
|
|
|
u32 *cap;
|
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
if (!aer)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_ERR);
|
|
|
|
@ -503,12 +361,12 @@ void pci_restore_aer_state(struct pci_dev *dev)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
cap = &save_state->cap.data[0];
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_CAP, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_UNCOR_SEVER, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_COR_MASK, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_CAP, *cap++);
|
|
|
|
|
if (pcie_cap_has_rtctl(dev))
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, *cap++);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, *cap++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pci_aer_init(struct pci_dev *dev)
|
|
|
|
@ -939,7 +797,7 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev)
|
|
|
|
|
*/
|
|
|
|
|
static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 status, mask;
|
|
|
|
|
u16 reg16;
|
|
|
|
|
|
|
|
|
@ -974,17 +832,16 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
|
|
|
|
|
if (!(reg16 & PCI_EXP_AER_FLAGS))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (!pos)
|
|
|
|
|
if (!aer)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/* Check if error is recorded */
|
|
|
|
|
if (e_info->severity == AER_CORRECTABLE) {
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK, &mask);
|
|
|
|
|
} else {
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS, &status);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK, &mask);
|
|
|
|
|
}
|
|
|
|
|
if (status & ~mask)
|
|
|
|
|
return true;
|
|
|
|
@ -1055,16 +912,15 @@ static bool find_source_device(struct pci_dev *parent,
|
|
|
|
|
*/
|
|
|
|
|
static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
|
|
|
|
|
{
|
|
|
|
|
int pos;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
|
|
|
|
|
if (info->severity == AER_CORRECTABLE) {
|
|
|
|
|
/*
|
|
|
|
|
* Correctable error does not need software intervention.
|
|
|
|
|
* No need to go through error recovery process.
|
|
|
|
|
*/
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
if (pos)
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
|
|
|
|
|
if (aer)
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_COR_STATUS,
|
|
|
|
|
info->status);
|
|
|
|
|
pci_aer_clear_device_status(dev);
|
|
|
|
|
} else if (info->severity == AER_NONFATAL)
|
|
|
|
@ -1155,22 +1011,21 @@ EXPORT_SYMBOL_GPL(aer_recover_queue);
|
|
|
|
|
*/
|
|
|
|
|
int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
|
|
|
|
{
|
|
|
|
|
int pos, temp;
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
|
|
/* Must reset in this function */
|
|
|
|
|
info->status = 0;
|
|
|
|
|
info->tlp_header_valid = 0;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
|
|
|
|
|
/* The device might not support AER */
|
|
|
|
|
if (!pos)
|
|
|
|
|
if (!aer)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (info->severity == AER_CORRECTABLE) {
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_STATUS,
|
|
|
|
|
&info->status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK,
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_COR_MASK,
|
|
|
|
|
&info->mask);
|
|
|
|
|
if (!(info->status & ~info->mask))
|
|
|
|
|
return 0;
|
|
|
|
@ -1179,27 +1034,27 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
|
|
|
|
|
info->severity == AER_NONFATAL) {
|
|
|
|
|
|
|
|
|
|
/* Link is still healthy for IO reads */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_STATUS,
|
|
|
|
|
&info->status);
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK,
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_UNCOR_MASK,
|
|
|
|
|
&info->mask);
|
|
|
|
|
if (!(info->status & ~info->mask))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Get First Error Pointer */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_CAP, &temp);
|
|
|
|
|
info->first_error = PCI_ERR_CAP_FEP(temp);
|
|
|
|
|
|
|
|
|
|
if (info->status & AER_LOG_TLP_MASKS) {
|
|
|
|
|
info->tlp_header_valid = 1;
|
|
|
|
|
pci_read_config_dword(dev,
|
|
|
|
|
pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
|
|
|
|
|
aer + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
|
|
|
|
|
pci_read_config_dword(dev,
|
|
|
|
|
pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
|
|
|
|
|
aer + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
|
|
|
|
|
pci_read_config_dword(dev,
|
|
|
|
|
pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
|
|
|
|
|
aer + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
|
|
|
|
|
pci_read_config_dword(dev,
|
|
|
|
|
pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
|
|
|
|
|
aer + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1305,15 +1160,15 @@ static irqreturn_t aer_irq(int irq, void *context)
|
|
|
|
|
struct pcie_device *pdev = (struct pcie_device *)context;
|
|
|
|
|
struct aer_rpc *rpc = get_service_data(pdev);
|
|
|
|
|
struct pci_dev *rp = rpc->rpd;
|
|
|
|
|
int aer = rp->aer_cap;
|
|
|
|
|
struct aer_err_source e_src = {};
|
|
|
|
|
int pos = rp->aer_cap;
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, &e_src.status);
|
|
|
|
|
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, &e_src.status);
|
|
|
|
|
if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV)))
|
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
|
|
|
|
pci_read_config_dword(rp, pos + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
|
|
|
|
|
pci_write_config_dword(rp, pos + PCI_ERR_ROOT_STATUS, e_src.status);
|
|
|
|
|
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
|
|
|
|
|
pci_write_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, e_src.status);
|
|
|
|
|
|
|
|
|
|
if (!kfifo_put(&rpc->aer_fifo, e_src))
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
@ -1365,7 +1220,7 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev,
|
|
|
|
|
static void aer_enable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
struct pci_dev *pdev = rpc->rpd;
|
|
|
|
|
int aer_pos;
|
|
|
|
|
int aer = pdev->aer_cap;
|
|
|
|
|
u16 reg16;
|
|
|
|
|
u32 reg32;
|
|
|
|
|
|
|
|
|
@ -1377,14 +1232,13 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
|
|
|
|
|
SYSTEM_ERROR_INTR_ON_MESG_MASK);
|
|
|
|
|
|
|
|
|
|
aer_pos = pdev->aer_cap;
|
|
|
|
|
/* Clear error status */
|
|
|
|
|
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, reg32);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Enable error reporting for the root port device and downstream port
|
|
|
|
@ -1393,9 +1247,9 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
set_downstream_devices_error_reporting(pdev, true);
|
|
|
|
|
|
|
|
|
|
/* Enable Root Port's interrupt in response to error messages */
|
|
|
|
|
pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
|
|
|
|
|
pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -1407,8 +1261,8 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
static void aer_disable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
{
|
|
|
|
|
struct pci_dev *pdev = rpc->rpd;
|
|
|
|
|
int aer = pdev->aer_cap;
|
|
|
|
|
u32 reg32;
|
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Disable error reporting for the root port device and downstream port
|
|
|
|
@ -1416,15 +1270,14 @@ static void aer_disable_rootport(struct aer_rpc *rpc)
|
|
|
|
|
*/
|
|
|
|
|
set_downstream_devices_error_reporting(pdev, false);
|
|
|
|
|
|
|
|
|
|
pos = pdev->aer_cap;
|
|
|
|
|
/* Disable Root's interrupt in response to error messages */
|
|
|
|
|
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
|
|
|
|
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
|
|
|
|
|
/* Clear Root's error status reg */
|
|
|
|
|
pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -1481,28 +1334,27 @@ static int aer_probe(struct pcie_device *dev)
|
|
|
|
|
*/
|
|
|
|
|
static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|
|
|
|
{
|
|
|
|
|
int aer = dev->aer_cap;
|
|
|
|
|
u32 reg32;
|
|
|
|
|
int pos;
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
pos = dev->aer_cap;
|
|
|
|
|
|
|
|
|
|
/* Disable Root's interrupt in response to error messages */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
|
|
|
|
|
rc = pci_bus_error_reset(dev);
|
|
|
|
|
pci_info(dev, "Root Port link has been reset\n");
|
|
|
|
|
|
|
|
|
|
/* Clear Root Error Status */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, ®32);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_STATUS, reg32);
|
|
|
|
|
|
|
|
|
|
/* Enable Root Port's interrupt in response to error messages */
|
|
|
|
|
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
pci_read_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
|
|
|
|
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
|
|
|
|
|
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
pci_write_config_dword(dev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
|
|
|
|
|
|
|
|
|
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
|
|
|
|
}
|
|
|
|
@ -1523,7 +1375,7 @@ static struct pcie_port_service_driver aerdriver = {
|
|
|
|
|
*/
|
|
|
|
|
int __init pcie_aer_init(void)
|
|
|
|
|
{
|
|
|
|
|
if (!pci_aer_available() || aer_acpi_firmware_first())
|
|
|
|
|
if (!pci_aer_available())
|
|
|
|
|
return -ENXIO;
|
|
|
|
|
return pcie_port_service_register(&aerdriver);
|
|
|
|
|
}
|
|
|
|
|