Index: FreeBSD/sys/dev/pci/pci_pci.c =================================================================== --- FreeBSD/sys/dev/pci/pci_pci.c +++ FreeBSD/sys/dev/pci/pci_pci.c @@ -71,7 +71,10 @@ static int pcib_ari_enabled(device_t pcib); static void pcib_ari_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, int *func); +static void pcib_pcie_intr(void *arg); +static void pcib_pcie_intr_aer(void *arg); #ifdef PCI_HP +static void pcib_pcie_intr_hotplug(void *arg); static void pcib_pcie_ab_timeout(void *arg); static void pcib_pcie_cc_timeout(void *arg); static void pcib_pcie_dll_timeout(void *arg); @@ -908,6 +911,278 @@ } #endif +static void +pcib_pcie_intr(void *arg) +{ + struct pcib_softc *sc; + device_t dev; + + sc = arg; + dev = sc->dev; + sc->pcie_slot_sta = pcie_read_config(dev, PCIER_SLOT_STA, 2); + + /* Clear the events just reported. */ + pcie_write_config(dev, PCIER_SLOT_STA, sc->pcie_slot_sta, 2); + + if (sc->flags & PCIB_AER) + pcib_pcie_intr_aer(sc); +#ifdef PCI_HP + if (sc->flags & PCIB_HOTPLUG) + pcib_pcie_intr_hotplug(sc); +#endif +} + +static int +pcib_alloc_pcie_irq(struct pcib_softc *sc) +{ + device_t dev; + int count, error, rid; + + /* + * Can be called multiple times, only initalize once. + */ + if (sc->pcie_irq_ref != 0) { + sc->pcie_irq_ref++; + return (0); + } + + rid = -1; + dev = sc->dev; + + /* + * For simplicity, only use MSI-X if there is a single message. + * To support a device with multiple messages we would have to + * use remap intr if the MSI number is not 0. + */ + count = pci_msix_count(dev); + if (count == 1) { + error = pci_alloc_msix(dev, &count); + if (error == 0) + rid = 1; + } + + if (rid < 0 && pci_msi_count(dev) > 0) { + count = 1; + error = pci_alloc_msi(dev, &count); + if (error == 0) + rid = 1; + } + + if (rid < 0) + rid = 0; + + sc->pcie_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->pcie_irq == NULL) { + device_printf(dev, + "Failed to allocate interrupt for PCI-e events\n"); + if (rid > 0) + pci_release_msi(dev); + return (ENXIO); + } + + error = bus_setup_intr(dev, sc->pcie_irq, INTR_TYPE_MISC, + NULL, pcib_pcie_intr, sc, &sc->pcie_ihand); + if (error) { + device_printf(dev, "Failed to setup PCI-e interrupt handler\n"); + bus_release_resource(dev, SYS_RES_IRQ, rid, sc->pcie_irq); + if (rid > 0) + pci_release_msi(dev); + return (error); + } + sc->pcie_irq_ref++; + return (0); +} + +static int +pcib_release_pcie_irq(struct pcib_softc *sc) +{ + device_t dev; + int error; + + if (sc->pcie_irq_ref > 1) { + sc->pcie_irq_ref--; + return (0); + } + dev = sc->dev; + error = bus_teardown_intr(dev, sc->pcie_irq, sc->pcie_ihand); + if (error) + return (error); + error = bus_free_resource(dev, SYS_RES_IRQ, sc->pcie_irq); + if (error) + return (error); + error = pci_release_msi(dev); + if (error) + return (error); + sc->pcie_irq_ref--; + return (0); +} + +/* + * PCI-express Advanced Error Reporting (AER) + */ +static int pci_enable_pcie_aer = 1; +SYSCTL_INT(_hw_pci, OID_AUTO, enable_pcie_aer, CTLFLAG_RDTUN, + &pci_enable_pcie_aer, 0, + "Enable support for native PCI-express Advanced Error Reporting."); + +static void +pcib_probe_aer(struct pcib_softc *sc) +{ + device_t dev; + uint32_t pcie_type; + + if (!pci_enable_pcie_aer) + return; + + dev = sc->dev; + if (pci_find_cap(dev, PCIY_EXPRESS, NULL) != 0) + return; + + if (pci_find_extcap(dev, PCIZ_AER, &sc->pcie_aer_cap) != 0) + return; + + pcie_type = pcie_read_config(dev, PCIER_FLAGS, 2); + pcie_type &= PCIEM_FLAGS_TYPE; + if (pcie_type == PCIEM_TYPE_ROOT_PORT || pcie_type == PCIEM_TYPE_ROOT_EC) + sc->flags |= PCIB_AER; +} + +static void +pcib_setup_aer(struct pcib_softc *sc) +{ + device_t dev; + + dev = sc->dev; +#if 0 + uint32_t devctl; + + /* + * Enable reporting of errors: Correctable, non-fatal, fatal + * and unsupported request. Often these are enabled by default, + * but doing it here makes sure. + */ + devctl = pcie_read_config(dev, PCIER_DEVICE_CTL, 1); + devctl |= PCIEM_CTL_COR_ENABLE | PCIEM_CTL_NFER_ENABLE | + PCIEM_CTL_FER_ENABLE | PCIEM_CTL_URR_ENABLE; + pcie_write_config(dev, PCIER_DEVICE_CTL, devctl, 1); +#endif + /* + * Tabulate any pre-existnig errors. + */ + pcib_pcie_intr_aer(sc); + + /* XXX need to select proper vector? */ + if (pcib_alloc_pcie_irq(sc) != 0) { + /* + * No interrupt -> no asyncrhonous AER reporting + * XXX -- Or should I create a timeout? + */ + device_printf(dev ,"Found AER, but can't get interrupt\n"); + sc->flags &= ~PCIB_AER; + sc->pcie_aer_cap = 0; + return; + } + + device_printf(dev ,"Found AER Root Complex at offset %#x\n", + sc->pcie_aer_cap); + + /* + * Enable device advanced error report. Root complexes (and event + * collectors) can interrupt. We should read the + * PCIR_AER_ROOTERR_STATUS register to determine what MSI/MSIX + * register will be used. + */ + pcie_write_config(dev, sc->pcie_aer_cap + PCIR_AER_ROOTERR_CMD, + PCIM_AER_ROOTERR_COR_ENABLE | PCIM_AER_ROOTERR_NF_ENABLE | + PCIM_AER_ROOTERR_F_ENABLE, 4); +} + +static int +pcib_detach_aer(struct pcib_softc *sc) +{ + + return (pcib_release_pcie_irq(sc)); +} + +static void +pcib_pcie_intr_aer(void *arg) +{ + struct pcib_softc *sc; + device_t dev; + uint32_t tmp; + u_int uncorr; + u_int corr; + + sc = arg; + dev = sc->dev; + /* sc->pcie_slot_sta read in pcib_pcie_intr() and acked */ + + /* + * Note: This is all rather a hash. We need to clear bits individually + * after reading the headers out of the appropraite registers. We also + * need to create an AER record for each of the errors we can find + * + * Plus, there appaears to be some siliness between this and the + * errors signalled PCIER_DEVICE_STA register. But since pciconf is + * getting the UC_STATUS and CORR_STATUS registers, I'd have thought + * this would properly clear them out. WTF it doesn't on the 100G + * machines... They seem to have all ff's. But the machines at home + * don't. + * + * Read section 6.2 and 7.10 in PCIe 3.0 spec + */ + + /* Uncorrected Errors */ + tmp = pcie_read_config(dev, sc->pcie_aer_cap + PCIR_AER_UC_STATUS, 4); + if (tmp) + pcie_write_config(dev, sc->pcie_aer_cap + PCIR_AER_UC_STATUS, + tmp, 4); + uncorr = bitcountl(tmp); + + /* Uncorrected Error Severity */ + tmp = pcie_read_config(dev, sc->pcie_aer_cap + PCIR_AER_UC_SEVERITY, 4); + + /* Correctable Error Status */ + tmp = pcie_read_config(dev, sc->pcie_aer_cap + PCIR_AER_COR_STATUS, 4); + if (tmp) + pcie_write_config(dev, sc->pcie_aer_cap + PCIR_AER_COR_STATUS, + tmp, 4); + corr = bitcountl(tmp); + + /* + * XXX should read out the TLP Header (offset 0x1c-0x2b) here when + * we record this error. + */ + + /* Root Port error status */ + tmp = pcie_read_config(dev, + sc->pcie_aer_cap + PCIR_AER_ROOTERR_STATUS, 4); + if (tmp) + pcie_write_config(dev, + sc->pcie_aer_cap + PCIR_AER_ROOTERR_STATUS, tmp, 4); + /* Error source */ + tmp = pcie_read_config(dev, + sc->pcie_aer_cap + PCIR_AER_ERR_SOURCE_ID, 2); + tmp = pcie_read_config(dev, + sc->pcie_aer_cap + PCIR_AER_COR_SOURCE_ID, 2); + + /* + * XXX if the port supports End-End TLP Prefix, then we should read + * the TLP-only registers here. + */ + + /* + * -- check in the standard to make sure this is right + * -- Increment per-bit error counters + * -- bonus points: add a record to the circular buffer + */ + sc->aer_num_uncorrected += uncorr; + sc->aer_num_corrected += corr; + device_printf(dev, "AER new errors: %d corrected %d uncorrected\n", + corr, uncorr); +} + #ifdef PCI_HP /* * PCI-express HotPlug support. @@ -1161,13 +1436,9 @@ struct pcib_softc *sc; device_t dev; + /* sc->pcie_slot_sta read in pcib_pcie_intr() and acked */ sc = arg; dev = sc->dev; - sc->pcie_slot_sta = pcie_read_config(dev, PCIER_SLOT_STA, 2); - - /* Clear the events just reported. */ - pcie_write_config(dev, PCIER_SLOT_STA, sc->pcie_slot_sta, 2); - if (bootverbose) device_printf(dev, "HotPlug interrupt: %#x\n", sc->pcie_slot_sta); @@ -1296,75 +1567,6 @@ } } -static int -pcib_alloc_pcie_irq(struct pcib_softc *sc) -{ - device_t dev; - int count, error, rid; - - rid = -1; - dev = sc->dev; - - /* - * For simplicity, only use MSI-X if there is a single message. - * To support a device with multiple messages we would have to - * use remap intr if the MSI number is not 0. - */ - count = pci_msix_count(dev); - if (count == 1) { - error = pci_alloc_msix(dev, &count); - if (error == 0) - rid = 1; - } - - if (rid < 0 && pci_msi_count(dev) > 0) { - count = 1; - error = pci_alloc_msi(dev, &count); - if (error == 0) - rid = 1; - } - - if (rid < 0) - rid = 0; - - sc->pcie_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); - if (sc->pcie_irq == NULL) { - device_printf(dev, - "Failed to allocate interrupt for PCI-e events\n"); - if (rid > 0) - pci_release_msi(dev); - return (ENXIO); - } - - error = bus_setup_intr(dev, sc->pcie_irq, INTR_TYPE_MISC, - NULL, pcib_pcie_intr_hotplug, sc, &sc->pcie_ihand); - if (error) { - device_printf(dev, "Failed to setup PCI-e interrupt handler\n"); - bus_release_resource(dev, SYS_RES_IRQ, rid, sc->pcie_irq); - if (rid > 0) - pci_release_msi(dev); - return (error); - } - return (0); -} - -static int -pcib_release_pcie_irq(struct pcib_softc *sc) -{ - device_t dev; - int error; - - dev = sc->dev; - error = bus_teardown_intr(dev, sc->pcie_irq, sc->pcie_ihand); - if (error) - return (error); - error = bus_free_resource(dev, SYS_RES_IRQ, sc->pcie_irq); - if (error) - return (error); - return (pci_release_msi(dev)); -} - static void pcib_setup_hotplug(struct pcib_softc *sc) { @@ -1632,6 +1834,7 @@ pci_read_config(dev, PCIR_PROGIF, 1) == PCIP_BRIDGE_PCI_SUBTRACTIVE) sc->flags |= PCIB_SUBTRACTIVE; + pcib_probe_aer(sc); #ifdef PCI_HP pcib_probe_hotplug(sc); #endif @@ -1641,6 +1844,14 @@ #endif pcib_probe_windows(sc); #endif + if (sc->flags & PCIB_AER) + pcib_setup_aer(sc); + if (sc->flags & PCIB_AER) { + SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "aer_uncorrected", + CTLFLAG_RD, &sc->aer_num_uncorrected, 0, "Subordinate bus number"); + SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "aer_corrected", + CTLFLAG_RD, &sc->aer_num_corrected, 0, "Subordinate bus number"); + } #ifdef PCI_HP if (sc->flags & PCIB_HOTPLUG) pcib_setup_hotplug(sc); @@ -1740,14 +1951,13 @@ int pcib_detach(device_t dev) { -#if defined(PCI_HP) || defined(NEW_PCIB) struct pcib_softc *sc; -#endif int error; -#if defined(PCI_HP) || defined(NEW_PCIB) sc = device_get_softc(dev); -#endif + if (sc->flags & PCIB_AER) + pcib_detach_aer(sc); + error = bus_generic_detach(dev); if (error) return (error); Index: FreeBSD/sys/dev/pci/pcib_private.h =================================================================== --- FreeBSD/sys/dev/pci/pcib_private.h +++ FreeBSD/sys/dev/pci/pcib_private.h @@ -114,6 +114,7 @@ #define PCIB_HOTPLUG_CMD_PENDING 0x20 #define PCIB_DETACH_PENDING 0x40 #define PCIB_DETACHING 0x80 +#define PCIB_AER 0x100 u_int domain; /* domain number */ u_int pribus; /* primary bus number */ struct pcib_secbus bus; /* secondary bus numbers */ @@ -130,15 +131,19 @@ uint32_t iolimit; /* topmost address of port window */ #endif uint16_t bridgectl; /* bridge control register */ + uint32_t pcie_aer_cap; uint16_t pcie_link_sta; uint16_t pcie_slot_sta; uint32_t pcie_slot_cap; struct resource *pcie_irq; void *pcie_ihand; + int pcie_irq_ref; struct task pcie_hp_task; struct callout pcie_ab_timer; struct callout pcie_cc_timer; struct callout pcie_dll_timer; + u_int aer_num_uncorrected; + u_int aer_num_corrected; }; #define PCIB_SUPPORTED_ARI_VER 1