| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Enable/disable PCIe ECRC checking |
| * |
| * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. |
| * Andrew Patterson <andrew.patterson@hp.com> |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/pci.h> |
| #include <linux/pci_regs.h> |
| #include <linux/errno.h> |
| #include "../../pci.h" |
| |
| #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 *ecrc_policy_str[] = { |
| [ECRC_POLICY_DEFAULT] = "bios", |
| [ECRC_POLICY_OFF] = "off", |
| [ECRC_POLICY_ON] = "on" |
| }; |
| |
| /** |
| * enable_ercr_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 pos; |
| u32 reg32; |
| |
| if (!pci_is_pcie(dev)) |
| return -ENODEV; |
| |
| pos = dev->aer_cap; |
| if (!pos) |
| return -ENODEV; |
| |
| pci_read_config_dword(dev, pos + 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); |
| |
| return 0; |
| } |
| |
| /** |
| * disable_ercr_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 pos; |
| u32 reg32; |
| |
| if (!pci_is_pcie(dev)) |
| return -ENODEV; |
| |
| pos = dev->aer_cap; |
| if (!pos) |
| return -ENODEV; |
| |
| pci_read_config_dword(dev, pos + 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); |
| |
| 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) |
| { |
| 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 - parse kernel command-line ecrc option |
| */ |
| void pcie_ecrc_get_policy(char *str) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) |
| if (!strncmp(str, ecrc_policy_str[i], |
| strlen(ecrc_policy_str[i]))) |
| break; |
| if (i >= ARRAY_SIZE(ecrc_policy_str)) |
| return; |
| |
| ecrc_policy = i; |
| } |