aboutsummaryrefslogtreecommitdiff
path: root/core/pci-quirk.c
diff options
context:
space:
mode:
authorOliver O'Halloran <oohall@gmail.com>2019-08-01 16:44:24 +1000
committerOliver O'Halloran <oohall@gmail.com>2019-08-02 15:22:24 +1000
commita7c613ebf0fb0242d892e8f183e1a0533cb1d549 (patch)
treefd5664744b61233a3ee7e1134d4345eaf97540bc /core/pci-quirk.c
parent452a9a46bcf7ecc8c8a0d0f48b273cb2658183eb (diff)
downloadskiboot-a7c613ebf0fb0242d892e8f183e1a0533cb1d549.zip
skiboot-a7c613ebf0fb0242d892e8f183e1a0533cb1d549.tar.gz
skiboot-a7c613ebf0fb0242d892e8f183e1a0533cb1d549.tar.bz2
core/pci-quirk: Microsemi switch UR workaround
Some Microsemi switches have a bug where accessing an unimplemented config space register causes an Unsupported Request error. This is a violation of the PCI spec which requires devices to ignore writes and return 0x00 when an unimplemented config space register is accessed. Linux allows userspace to access all of config space and tools (e.g. lspci) will read the entire 4KB space. This results in flood of spurious EEH events since POWER chips treat URs as an indication of a malfunctioning device. This patch adds a PCI device quirk that scans the config space of the switch in early boot to determine what ranges will trigger a UR. With this information we can then use config filters to block accesses to the problematic ranges. This scanning process is a little slow, but: a) This bug should be resolved by a switch firmware update eventually, and b) System firmware updates might result PCIe capabilities being added or removed from the switch's config space. This means that we would have a cache invalidation problem which isn't straightforward to resolve. We can check if the workaround is needed at all by reading 0xFF (the end of the legacy config space) since we know the switch never has anything implemented for that address. Do the simple thing for now rather than trying to make it faster since this should be a temporary workaround. Signed-off-by: Oliver O'Halloran <oohall@gmail.com> Reviewed-By: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'core/pci-quirk.c')
-rw-r--r--core/pci-quirk.c70
1 files changed, 70 insertions, 0 deletions
diff --git a/core/pci-quirk.c b/core/pci-quirk.c
index 6832b9c..1fa43d8 100644
--- a/core/pci-quirk.c
+++ b/core/pci-quirk.c
@@ -9,10 +9,79 @@
#include <skiboot.h>
#include <pci.h>
+#include <pci-cfg.h>
#include <pci-quirk.h>
#include <platform.h>
#include <ast.h>
+static int64_t cfg_block_filter(void *dev __unused,
+ struct pci_cfg_reg_filter *pcrf __unused,
+ uint32_t offset __unused, uint32_t len,
+ uint32_t *data, bool write)
+{
+ if (write)
+ return OPAL_SUCCESS;
+
+ switch (len) {
+ case 4:
+ *data = 0x0;
+ return OPAL_SUCCESS;
+ case 2:
+ *((uint16_t *)data) = 0x0;
+ return OPAL_SUCCESS;
+ case 1:
+ *((uint8_t *)data) = 0x0;
+ return OPAL_SUCCESS;
+ }
+
+ return OPAL_PARAMETER; /* should never happen */
+}
+
+/* blocks config accesses to registers in the range: [start, end] */
+#define BLOCK_CFG_RANGE(pd, start, end) \
+ pci_add_cfg_reg_filter(pd, start, end - start + 1, \
+ PCI_REG_FLAG_WRITE | PCI_REG_FLAG_READ, \
+ cfg_block_filter);
+
+static void quirk_microsemi_gen4_sw(struct phb *phb, struct pci_device *pd)
+{
+ uint8_t data;
+ bool frozen;
+ int offset;
+ int start;
+
+ pci_check_clear_freeze(phb);
+
+ /*
+ * Reading from 0xff should trigger a UR on the affected switches.
+ * If we don't get a freeze then we don't need the workaround
+ */
+ pci_cfg_read8(phb, pd->bdfn, 0xff, &data);
+ frozen = pci_check_clear_freeze(phb);
+ if (!frozen)
+ return;
+
+ for (start = -1, offset = 0; offset < 4096; offset++) {
+ pci_cfg_read8(phb, pd->bdfn, offset, &data);
+ frozen = pci_check_clear_freeze(phb);
+
+ if (start < 0 && frozen) { /* new UR range */
+ start = offset;
+ } else if (start >= 0 && !frozen) { /* end of range */
+ BLOCK_CFG_RANGE(pd, start, offset - 1);
+ PCINOTICE(phb, pd->bdfn, "Applied UR workaround to [%03x..%03x]\n", start, offset - 1);
+
+ start = -1;
+ }
+ }
+
+ /* range lasted until the end of config space */
+ if (start >= 0) {
+ BLOCK_CFG_RANGE(pd, start, 0xfff);
+ PCINOTICE(phb, pd->bdfn, "Applied UR workaround to [%03x..fff]\n", start);
+ }
+}
+
static void quirk_astbmc_vga(struct phb *phb __unused,
struct pci_device *pd)
{
@@ -44,6 +113,7 @@ static void quirk_astbmc_vga(struct phb *phb __unused,
static const struct pci_quirk quirk_table[] = {
/* ASPEED 2400 VGA device */
{ 0x1a03, 0x2000, &quirk_astbmc_vga },
+ { 0x11f8, 0x4052, &quirk_microsemi_gen4_sw },
{ 0, 0, NULL }
};