From 56a74bca6965d0c4dfa3c281d115cc2897eca677 Mon Sep 17 00:00:00 2001 From: Stephen Douthit Date: Mon, 5 Aug 2019 12:49:08 -0400 Subject: soc/intel/dnv: Set INT_LINE accouting for PIRQ routing & swizzling This code also sets unused interrupt lines to the recommended safe value of 0xff instead of ignoring such devices. Change-Id: I7582b41eb3288c400a949e20402e9820f6b72434 Signed-off-by: Stephen Douthit Reviewed-on: https://review.coreboot.org/c/coreboot/+/34714 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- src/soc/intel/denverton_ns/lpc.c | 276 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 259 insertions(+), 17 deletions(-) diff --git a/src/soc/intel/denverton_ns/lpc.c b/src/soc/intel/denverton_ns/lpc.c index 6481cbe00b..8c0a181d0c 100644 --- a/src/soc/intel/denverton_ns/lpc.c +++ b/src/soc/intel/denverton_ns/lpc.c @@ -65,6 +65,261 @@ static void pch_enable_ioapic(struct device *dev) io_apic_write((void *)IO_APIC_ADDR, 0x03, 0x01); } +/* interrupt router lookup for internal devices */ +struct dnv_ir_lut { + /* (dev << 3) | fn */ + u8 devfn; + u8 ir; +}; + +#define DEVFN(dev, fn) ((dev << 3) | (fn)) + +static const struct dnv_ir_lut dnv_ir_lut[] = { + {.devfn = DEVFN(0x05, 0), .ir = 3}, /* RCEC */ + {.devfn = DEVFN(0x06, 0), .ir = 4}, /* Virtual RP to QAT */ + {.devfn = DEVFN(0x09, 0), .ir = 7}, /* PCIe RP0 */ + {.devfn = DEVFN(0x0a, 0), .ir = 7}, /* PCIe RP1 */ + {.devfn = DEVFN(0x0b, 0), .ir = 7}, /* PCIe RP2 */ + {.devfn = DEVFN(0x0c, 0), .ir = 7}, /* PCIe RP3 */ + {.devfn = DEVFN(0x0e, 0), .ir = 8}, /* PCIe RP4 */ + {.devfn = DEVFN(0x0f, 0), .ir = 8}, /* PCIe RP5 */ + {.devfn = DEVFN(0x10, 0), .ir = 8}, /* PCIe RP6 */ + {.devfn = DEVFN(0x11, 0), .ir = 8}, /* PCIe RP7 */ + {.devfn = DEVFN(0x12, 0), .ir = 10}, /* SMBus - Host */ + {.devfn = DEVFN(0x13, 0), .ir = 6}, /* AHCI0 */ + {.devfn = DEVFN(0x14, 0), .ir = 11}, /* AHCI1 */ + {.devfn = DEVFN(0x15, 0), .ir = 9}, /* USB */ + {.devfn = DEVFN(0x16, 0), .ir = 1}, /* Virtual RP to LAN0 */ + {.devfn = DEVFN(0x17, 0), .ir = 2}, /* Virtual RP to LAN1 */ + {.devfn = DEVFN(0x18, 0), .ir = 5}, /* ME HECI1 */ + {.devfn = DEVFN(0x18, 1), .ir = 5}, /* ME HECI1 */ + {.devfn = DEVFN(0x18, 2), .ir = 5}, /* ME PTIO-IDER */ + {.devfn = DEVFN(0x18, 3), .ir = 5}, /* ME PTIO-KT */ + {.devfn = DEVFN(0x18, 4), .ir = 5}, /* ME HECI3 */ + {.devfn = DEVFN(0x1a, 0), .ir = 10}, /* HSUART0 */ + {.devfn = DEVFN(0x1a, 1), .ir = 10}, /* HSUART1 */ + {.devfn = DEVFN(0x1a, 2), .ir = 10}, /* HSUART2 */ + {.devfn = DEVFN(0x1b, 0), .ir = 12}, /* IE HECI1 */ + {.devfn = DEVFN(0x1b, 1), .ir = 12}, /* IE HECI1 */ + {.devfn = DEVFN(0x1b, 2), .ir = 12}, /* IE PTIO-IDER */ + {.devfn = DEVFN(0x1b, 3), .ir = 12}, /* IE PTIO-KT */ + {.devfn = DEVFN(0x1b, 4), .ir = 12}, /* IE HECI3 */ + {.devfn = DEVFN(0x1c, 0), .ir = 12}, /* SDHCI */ + {.devfn = DEVFN(0x1f, 0), .ir = 0}, /* LPC */ + {.devfn = DEVFN(0x1f, 1), .ir = 0}, /* PS2B */ + {.devfn = DEVFN(0x1f, 4), .ir = 0}, /* SMBus - Legacy */ + {.devfn = DEVFN(0x1f, 7), .ir = 0}, /* Trace Hub */ +}; + +/* + * Only 6 of the 8 root ports have swizzling, return '1' if this bdf is one of + * them, '0' otherwise + */ +static int is_dnv_swizzled_rp(uint16_t bdf) +{ + switch (bdf) { + case DEVFN(10, 0): + case DEVFN(11, 0): + case DEVFN(12, 0): + case DEVFN(15, 0): + case DEVFN(16, 0): + case DEVFN(17, 0): + return 1; + } + + return 0; +} + +/* + * Figure out which upstream interrupt pin a downstream device gets swizzled to + * + * config - pointer to chip_info containing routing info + * devfn - device/function of root port to check swizzling for + * pin - interrupt pin 1-4 = A-D + * + * Return new pin mapping, 0 if invalid pin + */ +static int dnv_get_swizzled_pin(config_t *config, u8 devfn, u8 pin) +{ + if (pin < 1 || pin > 4) + return 0; + + devfn >>= 3; + if (devfn < 13) + devfn -= 9; + else + devfn -= 14; + + return ((pin - 1 + devfn) % 4) + 1; +} + +/* + * Figure out which upstream interrupt pin a downstream device gets swizzled to + * + * config - pointer to chip_info containing routing info + * devfn - device/function of root port to check swizzling for + * pin - interrupt pin 1-4 = A-D + * + * Return new pin mapping, 0 if invalid pin + */ +static int dnv_get_ir(config_t *config, u8 devfn, u8 pin) +{ + int i = 0; + int line = 0xff; + u16 ir = 0xffff; + + /* The only valid pin values are 1-4 for A-D */ + if (pin < 1 || pin > 4) { + printk(BIOS_WARNING, "%s: pin %d is invalid\n", __func__, pin); + goto dnv_get_ir_done; + } + + for (i = 0; i < ARRAY_SIZE(dnv_ir_lut); i++) { + if (dnv_ir_lut[i].devfn == devfn) + break; + } + + if (i == ARRAY_SIZE(dnv_ir_lut)) { + printk(BIOS_WARNING, "%s: no entry\n", __func__); + goto dnv_get_ir_done; + } + + switch (dnv_ir_lut[i].ir) { + case 0: + ir = config->ir00_routing; + break; + case 1: + ir = config->ir01_routing; + break; + case 2: + ir = config->ir02_routing; + break; + case 3: + ir = config->ir03_routing; + break; + case 4: + ir = config->ir04_routing; + break; + case 5: + ir = config->ir05_routing; + break; + case 6: + ir = config->ir06_routing; + break; + case 7: + ir = config->ir07_routing; + break; + case 8: + ir = config->ir08_routing; + break; + case 9: + ir = config->ir09_routing; + break; + case 10: + ir = config->ir10_routing; + break; + case 11: + ir = config->ir11_routing; + break; + case 12: + ir = config->ir12_routing; + break; + default: + printk(BIOS_ERR, "%s: invalid ir %d for entry %d\n", __func__, dnv_ir_lut[i].ir, + i); + goto dnv_get_ir_done; + } + + ir >>= (pin - 1) * 4; + ir &= 0xf; + switch (ir) { + case 0: + line = config->pirqa_routing; + break; + case 1: + line = config->pirqb_routing; + break; + case 2: + line = config->pirqc_routing; + break; + case 3: + line = config->pirqd_routing; + break; + case 4: + line = config->pirqe_routing; + break; + case 5: + line = config->pirqf_routing; + break; + case 6: + line = config->pirqg_routing; + break; + case 7: + line = config->pirqh_routing; + break; + default: + printk(BIOS_ERR, "%s: invalid ir pirq %d for entry %d\n", __func__, ir, i); + break; + } + +dnv_get_ir_done: + return line; +} + +/* + * PCI devices have the INT_LINE (0x3C) and INT_PIN (0x3D) registers which + * report interrupt routing information to operating systems and drivers. The + * INT_PIN register is generally read only and reports which interrupt pin + * A - D it uses. The INT_LINE register is configurable and reports which IRQ + * (generally the PIC IRQs 1 - 15) it will use. This needs to take interrupt + * pin swizzling on devices that are downstream on a PCI bridge into account. + */ +static u8 dnv_get_int_line(struct device *irq_dev) +{ + config_t *config; + struct device *targ_dev = NULL; + uint16_t parent_bdf = 0; + int8_t original_int_pin = 0, new_int_pin = 0, swiz_int_pin = 0; + uint8_t int_line = 0xff; + + if (irq_dev->path.type != DEVICE_PATH_PCI || !irq_dev->enabled) { + printk(BIOS_ERR, "%s for non pci device?\n", __func__); + goto dnv_get_int_line_done; + } + + /* + * Get the INT_PIN swizzled up to the root port if necessary + * using the existing coreboot pci_device code + */ + original_int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); + new_int_pin = get_pci_irq_pins(irq_dev, &targ_dev); + if (targ_dev == NULL || new_int_pin < 1) + goto dnv_get_int_line_done; + + printk(BIOS_DEBUG, "%s: irq_dev %s, targ_dev %s:\n", __func__, dev_path(irq_dev), + dev_path(targ_dev)); + printk(BIOS_DEBUG, "%s: std swizzle %s from %c to %c\n", __func__, dev_path(targ_dev), + '@' + original_int_pin, '@' + new_int_pin); + + /* Swizzle this device if needed */ + config = targ_dev->chip_info; + parent_bdf = targ_dev->path.pci.devfn | targ_dev->bus->secondary << 8; + if (is_dnv_swizzled_rp(parent_bdf) && irq_dev != targ_dev) { + swiz_int_pin = dnv_get_swizzled_pin(config, parent_bdf, new_int_pin); + printk(BIOS_DEBUG, "%s: dnv swizzle %s from %c to %c\n", __func__, + dev_path(targ_dev), '@' + new_int_pin, '@' + swiz_int_pin); + } else { + swiz_int_pin = new_int_pin; + } + + /* Look up the routing for the pin */ + int_line = dnv_get_ir(config, parent_bdf, swiz_int_pin); + +dnv_get_int_line_done: + printk(BIOS_DEBUG, "\tINT_LINE\t\t: %d\n", int_line); + return int_line; +} + /* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control * 0x00 - 0000 = Reserved * 0x01 - 0001 = Reserved @@ -150,6 +405,7 @@ static void pch_pirq_init(struct device *dev) config->ipc3); for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { + int devfn = irq_dev->path.pci.devfn; u8 int_pin = 0, int_line = 0; if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) @@ -157,23 +413,9 @@ static void pch_pirq_init(struct device *dev) int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); - switch (int_pin) { - case 1: /* INTA# */ - int_line = config->pirqa_routing; - break; - case 2: /* INTB# */ - int_line = config->pirqb_routing; - break; - case 3: /* INTC# */ - int_line = config->pirqc_routing; - break; - case 4: /* INTD# */ - int_line = config->pirqd_routing; - break; - } - - if (!int_line) - continue; + int_line = dnv_get_int_line(irq_dev); + printk(BIOS_DEBUG, "%s: %02x:%02x.%d pin %d int line %d\n", __func__, + irq_dev->bus->secondary, devfn >> 3, devfn & 0x7, int_pin, int_line); pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); } -- cgit v1.2.3