From owner-svn-src-all@freebsd.org Thu May 5 22:26:26 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 40654B2FE06; Thu, 5 May 2016 22:26:26 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 08638116D; Thu, 5 May 2016 22:26:25 +0000 (UTC) (envelope-from jhb@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u45MQPwD093730; Thu, 5 May 2016 22:26:25 GMT (envelope-from jhb@FreeBSD.org) Received: (from jhb@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u45MQORq093719; Thu, 5 May 2016 22:26:24 GMT (envelope-from jhb@FreeBSD.org) Message-Id: <201605052226.u45MQORq093719@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: jhb set sender to jhb@FreeBSD.org using -f From: John Baldwin Date: Thu, 5 May 2016 22:26:24 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r299142 - in head/sys: amd64/conf arm64/conf conf dev/acpica dev/pci i386/conf powerpc/conf X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 05 May 2016 22:26:26 -0000 Author: jhb Date: Thu May 5 22:26:23 2016 New Revision: 299142 URL: https://svnweb.freebsd.org/changeset/base/299142 Log: Native PCI-express HotPlug support. PCI-express HotPlug support is implemented via bits in the slot registers of the PCI-express capability of the downstream port along with an interrupt that triggers when bits in the slot status register change. This is implemented for FreeBSD by adding HotPlug support to the PCI-PCI bridge driver which attaches to the virtual PCI-PCI bridges representing downstream ports on HotPlug slots. The PCI-PCI bridge driver registers an interrupt handler to receive HotPlug events. It also uses the slot registers to determine the current HotPlug state and drive an internal HotPlug state machine. For simplicty of implementation, the PCI-PCI bridge device detaches and deletes the child PCI device when a card is removed from a slot and creates and attaches a PCI child device when a card is inserted into the slot. The PCI-PCI bridge driver provides a bus_child_present which claims that child devices are present on HotPlug-capable slots only when a card is inserted. Rather than requiring a timeout in the RC for config accesses to not-present children, the pcib_read/write_config methods fail all requests when a card is not present (or not yet ready). These changes include support for various optional HotPlug capabilities such as a power controller, mechanical latch, electro-mechanical interlock, indicators, and an attention button. It also includes support for devices which require waiting for command completion events before initiating a subsequent HotPlug command. However, it has only been tested on ExpressCard systems which support surprise removal and have none of these optional capabilities. PCI-express HotPlug support is conditional on the PCI_HP option which is enabled by default on arm64, x86, and powerpc. Reviewed by: adrian, imp, vangyzen (older versions) Relnotes: yes Differential Revision: https://reviews.freebsd.org/D6136 Modified: head/sys/amd64/conf/GENERIC head/sys/arm64/conf/GENERIC head/sys/conf/NOTES head/sys/conf/options head/sys/dev/acpica/acpi_pcib_acpi.c head/sys/dev/pci/pci_pci.c head/sys/dev/pci/pcib_private.h head/sys/dev/pci/pcireg.h head/sys/i386/conf/GENERIC head/sys/powerpc/conf/GENERIC head/sys/powerpc/conf/GENERIC64 Modified: head/sys/amd64/conf/GENERIC ============================================================================== --- head/sys/amd64/conf/GENERIC Thu May 5 22:10:41 2016 (r299141) +++ head/sys/amd64/conf/GENERIC Thu May 5 22:26:23 2016 (r299142) @@ -102,6 +102,7 @@ device cpufreq device acpi options ACPI_DMAR device pci +options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support # Floppy drives Modified: head/sys/arm64/conf/GENERIC ============================================================================== --- head/sys/arm64/conf/GENERIC Thu May 5 22:10:41 2016 (r299141) +++ head/sys/arm64/conf/GENERIC Thu May 5 22:26:23 2016 (r299142) @@ -96,6 +96,7 @@ device vtnet # Bus drivers device pci +options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support # Ethernet NICs Modified: head/sys/conf/NOTES ============================================================================== --- head/sys/conf/NOTES Thu May 5 22:10:41 2016 (r299141) +++ head/sys/conf/NOTES Thu May 5 22:26:23 2016 (r299142) @@ -1411,6 +1411,7 @@ options MSGBUF_SIZE=40960 # PCI bus & PCI options: # device pci +options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support Modified: head/sys/conf/options ============================================================================== --- head/sys/conf/options Thu May 5 22:10:41 2016 (r299141) +++ head/sys/conf/options Thu May 5 22:26:23 2016 (r299142) @@ -177,6 +177,7 @@ NO_SYSCTL_DESCR opt_global.h NSWBUF_MIN opt_swap.h MBUF_PACKET_ZONE_DISABLE opt_global.h PANIC_REBOOT_WAIT_TIME opt_panic.h +PCI_HP opt_pci.h PCI_IOV opt_global.h PPC_DEBUG opt_ppc.h PPC_PROBE_CHIPSET opt_ppc.h Modified: head/sys/dev/acpica/acpi_pcib_acpi.c ============================================================================== --- head/sys/dev/acpica/acpi_pcib_acpi.c Thu May 5 22:10:41 2016 (r299141) +++ head/sys/dev/acpica/acpi_pcib_acpi.c Thu May 5 22:26:23 2016 (r299142) @@ -29,6 +29,8 @@ __FBSDID("$FreeBSD$"); #include "opt_acpi.h" +#include "opt_pci.h" + #include #include #include @@ -312,6 +314,11 @@ acpi_pcib_osc(struct acpi_hpcib_softc *s /* Control Field */ cap_set[2] = 0; +#ifdef PCI_HP + /* Control Field: PCI Express Native Hot Plug */ + cap_set[2] |= 0x1; +#endif + status = acpi_EvaluateOSC(sc->ap_handle, pci_host_bridge_uuid, 1, nitems(cap_set), cap_set, cap_set, false); if (ACPI_FAILURE(status)) { Modified: head/sys/dev/pci/pci_pci.c ============================================================================== --- head/sys/dev/pci/pci_pci.c Thu May 5 22:10:41 2016 (r299141) +++ head/sys/dev/pci/pci_pci.c Thu May 5 22:26:23 2016 (r299142) @@ -35,6 +35,8 @@ __FBSDID("$FreeBSD$"); * PCI:PCI bridge support. */ +#include "opt_pci.h" + #include #include #include @@ -43,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -67,6 +70,11 @@ static int pcib_try_enable_ari(device_t 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); +#ifdef PCI_HP +static void pcib_pcie_ab_timeout(void *arg); +static void pcib_pcie_cc_timeout(void *arg); +static void pcib_pcie_dll_timeout(void *arg); +#endif static device_method_t pcib_methods[] = { /* Device interface */ @@ -78,6 +86,7 @@ static device_method_t pcib_methods[] = DEVMETHOD(device_resume, pcib_resume), /* Bus interface */ + DEVMETHOD(bus_child_present, pcib_child_present), DEVMETHOD(bus_read_ivar, pcib_read_ivar), DEVMETHOD(bus_write_ivar, pcib_write_ivar), DEVMETHOD(bus_alloc_resource, pcib_alloc_resource), @@ -839,6 +848,466 @@ pcib_set_mem_decode(struct pcib_softc *s } #endif +#ifdef PCI_HP +/* + * PCI-express HotPlug support. + */ +static void +pcib_probe_hotplug(struct pcib_softc *sc) +{ + device_t dev; + + dev = sc->dev; + if (pci_find_cap(dev, PCIY_EXPRESS, NULL) != 0) + return; + + if (!(pcie_read_config(dev, PCIER_FLAGS, 2) & PCIEM_FLAGS_SLOT)) + return; + + sc->pcie_link_cap = pcie_read_config(dev, PCIER_LINK_CAP, 4); + sc->pcie_slot_cap = pcie_read_config(dev, PCIER_SLOT_CAP, 4); + + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_HPC) + sc->flags |= PCIB_HOTPLUG; +} + +/* + * Send a HotPlug command to the slot control register. If this slot + * uses command completion interrupts, these updates will be buffered + * while a previous command is completing. + */ +static void +pcib_pcie_hotplug_command(struct pcib_softc *sc, uint16_t val, uint16_t mask) +{ + device_t dev; + uint16_t ctl, new; + + dev = sc->dev; + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_NCCS) { + ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); + new = (ctl & ~mask) | val; + if (new != ctl) + pcie_write_config(dev, PCIER_SLOT_CTL, new, 2); + return; + } + + if (sc->flags & PCIB_HOTPLUG_CMD_PENDING) { + sc->pcie_pending_link_ctl_val &= ~mask; + sc->pcie_pending_link_ctl_val |= val; + sc->pcie_pending_link_ctl_mask |= mask; + } else { + ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); + new = (ctl & ~mask) | val; + if (new != ctl) { + pcie_write_config(dev, PCIER_SLOT_CTL, ctl, 2); + sc->flags |= PCIB_HOTPLUG_CMD_PENDING; + if (!cold) + callout_reset(&sc->pcie_cc_timer, hz, + pcib_pcie_cc_timeout, sc); + } + } +} + +static void +pcib_pcie_hotplug_command_completed(struct pcib_softc *sc) +{ + device_t dev; + uint16_t ctl, new; + + dev = sc->dev; + + if (bootverbose) + device_printf(dev, "Command Completed\n"); + if (!(sc->flags & PCIB_HOTPLUG_CMD_PENDING)) + return; + if (sc->pcie_pending_link_ctl_mask != 0) { + ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); + new = ctl & ~sc->pcie_pending_link_ctl_mask; + new |= sc->pcie_pending_link_ctl_val; + if (new != ctl) { + pcie_write_config(dev, PCIER_SLOT_CTL, ctl, 2); + if (!cold) + callout_reset(&sc->pcie_cc_timer, hz, + pcib_pcie_cc_timeout, sc); + } else + sc->flags &= ~PCIB_HOTPLUG_CMD_PENDING; + sc->pcie_pending_link_ctl_mask = 0; + sc->pcie_pending_link_ctl_val = 0; + } else { + callout_stop(&sc->pcie_cc_timer); + sc->flags &= ~PCIB_HOTPLUG_CMD_PENDING; + } +} + +/* + * Returns true if a card is fully inserted from the user's + * perspective. It may not yet be ready for access, but the driver + * can now start enabling access if necessary. + */ +static bool +pcib_hotplug_inserted(struct pcib_softc *sc) +{ + + /* Pretend the card isn't present if a detach is forced. */ + if (sc->flags & PCIB_DETACHING) + return (false); + + /* Card must be present in the slot. */ + if ((sc->pcie_slot_sta & PCIEM_SLOT_STA_PDS) == 0) + return (false); + + /* A power fault implicitly turns off power to the slot. */ + if (sc->pcie_slot_sta & PCIEM_SLOT_STA_PFD) + return (false); + + /* If the MRL is disengaged, the slot is powered off. */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_MRLSP && + (sc->pcie_slot_sta & PCIEM_SLOT_STA_MRLSS) != 0) + return (false); + + return (true); +} + +/* + * Returns -1 if the card is fully inserted, powered, and ready for + * access. Otherwise, returns 0. + */ +static int +pcib_hotplug_present(struct pcib_softc *sc) +{ + device_t dev; + + dev = sc->dev; + + /* Card must be inserted. */ + if (!pcib_hotplug_inserted(sc)) + return (0); + + /* + * Require the Electromechanical Interlock to be engaged if + * present. + */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_EIP && + (sc->pcie_slot_sta & PCIEM_SLOT_STA_EIS) == 0) + return (0); + + /* Require the Data Link Layer to be active. */ + if (sc->pcie_link_cap & PCIEM_LINK_CAP_DL_ACTIVE) { + if (!(sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE)) + return (0); + } + + return (-1); +} + +static void +pcib_pcie_hotplug_update(struct pcib_softc *sc, uint16_t val, uint16_t mask, + bool schedule_task) +{ + bool card_inserted; + + /* Clear DETACHING if Present Detect has cleared. */ + if ((sc->pcie_slot_sta & (PCIEM_SLOT_STA_PDC | PCIEM_SLOT_STA_PDS)) == + PCIEM_SLOT_STA_PDC) + sc->flags &= ~PCIB_DETACHING; + + card_inserted = pcib_hotplug_inserted(sc); + + /* Turn the power indicator on if a card is inserted. */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_PIP) { + mask |= PCIEM_SLOT_CTL_PIC; + if (card_inserted) + val |= PCIEM_SLOT_CTL_PI_ON; + else if (sc->flags & PCIB_DETACH_PENDING) + val |= PCIEM_SLOT_CTL_PI_BLINK; + else + val |= PCIEM_SLOT_CTL_PI_OFF; + } + + /* Turn the power on via the Power Controller if a card is inserted. */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_PCP) { + mask |= PCIEM_SLOT_CTL_PCC; + if (card_inserted) + val |= PCIEM_SLOT_CTL_PC_ON; + else + val |= PCIEM_SLOT_CTL_PC_OFF; + } + + /* + * If a card is inserted, enable the Electromechanical + * Interlock. If a card is not inserted (or we are in the + * process of detaching), disable the Electromechanical + * Interlock. + */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_EIP) { + if (card_inserted != + !(sc->pcie_slot_sta & PCIEM_SLOT_STA_EIS)) { + mask |= PCIEM_SLOT_CTL_EIC; + val |= PCIEM_SLOT_CTL_EIC; + } + } + + /* + * Start a timer to see if the Data Link Layer times out. + * Note that we only start the timer if Presence Detect + * changed on this interrupt. Stop any scheduled timer if + * the Data Link Layer is active. + */ + if (sc->pcie_link_cap & PCIEM_LINK_CAP_DL_ACTIVE) { + if (card_inserted && + !(sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE) && + sc->pcie_slot_sta & PCIEM_SLOT_STA_PDC) { + if (cold) + device_printf(sc->dev, + "Data Link Layer inactive\n"); + else + callout_reset(&sc->pcie_dll_timer, hz, + pcib_pcie_dll_timeout, sc); + } else if (sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE) + callout_stop(&sc->pcie_dll_timer); + } + + pcib_pcie_hotplug_command(sc, val, mask); + + /* + * During attach the child "pci" device is added sychronously; + * otherwise, the task is scheduled to manage the child + * device. + */ + if (schedule_task && + (pcib_hotplug_present(sc) != 0) != (sc->child != NULL)) + taskqueue_enqueue(taskqueue_thread, &sc->pcie_hp_task); +} + +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->pcie_slot_sta & PCIEM_SLOT_STA_ABP) { + if (sc->flags & PCIB_DETACH_PENDING) { + device_printf(dev, + "Attention Button Pressed: Detach Cancelled\n"); + sc->flags &= ~PCIB_DETACH_PENDING; + callout_stop(&sc->pcie_ab_timer); + } else { + device_printf(dev, + "Attention Button Pressed: Detaching in 5 seconds\n"); + sc->flags |= PCIB_DETACH_PENDING; + callout_reset(&sc->pcie_ab_timer, 5 * hz, + pcib_pcie_ab_timeout, sc); + } + } + if (sc->pcie_slot_sta & PCIEM_SLOT_STA_PFD) + device_printf(dev, "Power Fault Detected\n"); + if (sc->pcie_slot_sta & PCIEM_SLOT_STA_MRLSC) + device_printf(dev, "MRL Sensor Changed to %s\n", + sc->pcie_slot_sta & PCIEM_SLOT_STA_MRLSS ? "open" : + "closed"); + if (bootverbose && sc->pcie_slot_sta & PCIEM_SLOT_STA_PDC) + device_printf(dev, "Present Detect Changed to %s\n", + sc->pcie_slot_sta & PCIEM_SLOT_STA_PDS ? "card present" : + "empty"); + if (sc->pcie_slot_sta & PCIEM_SLOT_STA_CC) + pcib_pcie_hotplug_command_completed(sc); + if (sc->pcie_slot_sta & PCIEM_SLOT_STA_DLLSC) { + sc->pcie_link_sta = pcie_read_config(dev, PCIER_LINK_STA, 2); + if (bootverbose) + device_printf(dev, + "Data Link Layer State Changed to %s\n", + sc->pcie_link_sta & PCIEM_LINK_STA_DL_ACTIVE ? + "active" : "inactive"); + } + + pcib_pcie_hotplug_update(sc, 0, 0, true); +} + +static void +pcib_pcie_hotplug_task(void *context, int pending) +{ + struct pcib_softc *sc; + device_t dev; + + sc = context; + mtx_lock(&Giant); + dev = sc->dev; + if (pcib_hotplug_present(sc) != 0) { + if (sc->child == NULL) { + sc->child = device_add_child(dev, "pci", -1); + bus_generic_attach(dev); + } + } else { + if (sc->child != NULL) { + if (device_delete_child(dev, sc->child) == 0) + sc->child = NULL; + } + } + mtx_unlock(&Giant); +} + +static void +pcib_pcie_ab_timeout(void *arg) +{ + struct pcib_softc *sc; + device_t dev; + + sc = arg; + dev = sc->dev; + mtx_assert(&Giant, MA_OWNED); + if (sc->flags & PCIB_DETACH_PENDING) { + sc->flags |= PCIB_DETACHING; + sc->flags &= ~PCIB_DETACH_PENDING; + pcib_pcie_hotplug_update(sc, 0, 0, true); + } +} + +static void +pcib_pcie_cc_timeout(void *arg) +{ + struct pcib_softc *sc; + device_t dev; + + sc = arg; + dev = sc->dev; + mtx_assert(&Giant, MA_OWNED); + if (sc->flags & PCIB_HOTPLUG_CMD_PENDING) { + device_printf(dev, + "Hotplug Command Timed Out - forcing detach\n"); + sc->flags &= ~(PCIB_HOTPLUG_CMD_PENDING | PCIB_DETACH_PENDING); + sc->flags |= PCIB_DETACHING; + pcib_pcie_hotplug_update(sc, 0, 0, true); + } +} + +static void +pcib_pcie_dll_timeout(void *arg) +{ + struct pcib_softc *sc; + device_t dev; + uint16_t sta; + + sc = arg; + dev = sc->dev; + mtx_assert(&Giant, MA_OWNED); + sta = pcie_read_config(dev, PCIER_LINK_STA, 2); + if (!(sta & PCIEM_LINK_STA_DL_ACTIVE)) { + device_printf(dev, + "Timed out waiting for Data Link Layer Active\n"); + sc->flags |= PCIB_DETACHING; + pcib_pcie_hotplug_update(sc, 0, 0, true); + } else if (sta != sc->pcie_link_sta) { + device_printf(dev, + "Missed HotPlug interrupt waiting for DLL Active\n"); + pcib_pcie_intr(sc); + } +} + +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, 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 void +pcib_setup_hotplug(struct pcib_softc *sc) +{ + device_t dev; + uint16_t mask, val; + + dev = sc->dev; + callout_init(&sc->pcie_ab_timer, 0); + callout_init(&sc->pcie_cc_timer, 0); + callout_init(&sc->pcie_dll_timer, 0); + TASK_INIT(&sc->pcie_hp_task, 0, pcib_pcie_hotplug_task, sc); + + /* Allocate IRQ. */ + if (pcib_alloc_pcie_irq(sc) != 0) + return; + + sc->pcie_link_sta = pcie_read_config(dev, PCIER_LINK_STA, 2); + sc->pcie_slot_sta = pcie_read_config(dev, PCIER_SLOT_STA, 2); + + /* Enable HotPlug events. */ + mask = PCIEM_SLOT_CTL_DLLSCE | PCIEM_SLOT_CTL_HPIE | + PCIEM_SLOT_CTL_CCIE | PCIEM_SLOT_CTL_PDCE | PCIEM_SLOT_CTL_MRLSCE | + PCIEM_SLOT_CTL_PFDE | PCIEM_SLOT_CTL_ABPE; + val = PCIEM_SLOT_CTL_PDCE | PCIEM_SLOT_CTL_HPIE; + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_APB) + val |= PCIEM_SLOT_CTL_ABPE; + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_PCP) + val |= PCIEM_SLOT_CTL_PFDE; + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_MRLSP) + val |= PCIEM_SLOT_CTL_MRLSCE; + if (!(sc->pcie_slot_cap & PCIEM_SLOT_CAP_NCCS)) + val |= PCIEM_SLOT_CTL_CCIE; + if (sc->pcie_link_cap & PCIEM_LINK_CAP_DL_ACTIVE) + val |= PCIEM_SLOT_CTL_DLLSCE; + + /* Turn the attention indicator off. */ + if (sc->pcie_slot_cap & PCIEM_SLOT_CAP_AIP) { + mask |= PCIEM_SLOT_CTL_AIC; + val |= PCIEM_SLOT_CTL_AI_OFF; + } + + pcib_pcie_hotplug_update(sc, val, mask, false); +} +#endif + /* * Get current bridge configuration. */ @@ -1017,12 +1486,19 @@ pcib_attach_common(device_t dev) pci_read_config(dev, PCIR_PROGIF, 1) == PCIP_BRIDGE_PCI_SUBTRACTIVE) sc->flags |= PCIB_SUBTRACTIVE; +#ifdef PCI_HP + pcib_probe_hotplug(sc); +#endif #ifdef NEW_PCIB #ifdef PCI_RES_BUS pcib_setup_secbus(dev, &sc->bus, 1); #endif pcib_probe_windows(sc); #endif +#ifdef PCI_HP + if (sc->flags & PCIB_HOTPLUG) + pcib_setup_hotplug(sc); +#endif if (bootverbose) { device_printf(dev, " domain %d\n", sc->domain); device_printf(dev, " secondary bus %d\n", sc->bus.sec); @@ -1074,6 +1550,17 @@ pcib_attach_common(device_t dev) pci_enable_busmaster(dev); } +#ifdef PCI_HP +static int +pcib_present(struct pcib_softc *sc) +{ + + if (sc->flags & PCIB_HOTPLUG) + return (pcib_hotplug_present(sc) != 0); + return (1); +} +#endif + int pcib_attach_child(device_t dev) { @@ -1085,6 +1572,13 @@ pcib_attach_child(device_t dev) return(0); } +#ifdef PCI_HP + if (!pcib_present(sc)) { + /* An empty HotPlug slot, so don't add a PCI bus yet. */ + return (0); + } +#endif + sc->child = device_add_child(dev, "pci", -1); return (bus_generic_attach(dev)); } @@ -1129,6 +1623,22 @@ pcib_bridge_init(device_t dev) } int +pcib_child_present(device_t dev, device_t child) +{ +#ifdef PCI_HP + struct pcib_softc *sc = device_get_softc(dev); + int retval; + + retval = bus_child_present(dev); + if (retval != 0 && sc->flags & PCIB_HOTPLUG) + retval = pcib_hotplug_present(sc); + return (retval); +#else + return (bus_child_present(dev)); +#endif +} + +int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct pcib_softc *sc = device_get_softc(dev); @@ -1909,7 +2419,21 @@ pcib_ari_decode_rid(device_t pcib, uint1 static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width) { +#ifdef PCI_HP + struct pcib_softc *sc; + sc = device_get_softc(dev); + if (!pcib_present(sc)) { + switch (width) { + case 2: + return (0xffff); + case 1: + return (0xff); + default: + return (0xffffffff); + } + } +#endif pcib_xlate_ari(dev, b, &s, &f); return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, width)); @@ -1918,7 +2442,13 @@ pcib_read_config(device_t dev, u_int b, static void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width) { +#ifdef PCI_HP + struct pcib_softc *sc; + sc = device_get_softc(dev); + if (!pcib_present(sc)) + return; +#endif pcib_xlate_ari(dev, b, &s, &f); PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, val, width); Modified: head/sys/dev/pci/pcib_private.h ============================================================================== --- head/sys/dev/pci/pcib_private.h Thu May 5 22:10:41 2016 (r299141) +++ head/sys/dev/pci/pcib_private.h Thu May 5 22:26:23 2016 (r299142) @@ -33,6 +33,9 @@ #ifndef __PCIB_PRIVATE_H__ #define __PCIB_PRIVATE_H__ +#include +#include + #ifdef NEW_PCIB /* * Data structure and routines that Host to PCI bridge drivers can use @@ -107,6 +110,10 @@ struct pcib_softc #define PCIB_DISABLE_MSI 0x2 #define PCIB_DISABLE_MSIX 0x4 #define PCIB_ENABLE_ARI 0x8 +#define PCIB_HOTPLUG 0x10 +#define PCIB_HOTPLUG_CMD_PENDING 0x20 +#define PCIB_DETACH_PENDING 0x40 +#define PCIB_DETACHING 0x80 u_int domain; /* domain number */ u_int pribus; /* primary bus number */ struct pcib_secbus bus; /* secondary bus numbers */ @@ -123,6 +130,18 @@ struct pcib_softc uint32_t iolimit; /* topmost address of port window */ #endif uint16_t bridgectl; /* bridge control register */ + uint16_t pcie_link_sta; + uint16_t pcie_slot_sta; + uint32_t pcie_link_cap; + uint32_t pcie_slot_cap; + uint16_t pcie_pending_link_ctl_mask; + uint16_t pcie_pending_link_ctl_val; + struct resource *pcie_irq; + void *pcie_ihand; + struct task pcie_hp_task; + struct callout pcie_ab_timer; + struct callout pcie_cc_timer; + struct callout pcie_dll_timer; }; #define PCIB_SUPPORTED_ARI_VER 1 @@ -151,6 +170,7 @@ void pcib_bridge_init(device_t dev); #ifdef NEW_PCIB const char *pcib_child_name(device_t child); #endif +int pcib_child_present(device_t dev, device_t child); int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value); struct resource *pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, Modified: head/sys/dev/pci/pcireg.h ============================================================================== --- head/sys/dev/pci/pcireg.h Thu May 5 22:10:41 2016 (r299141) +++ head/sys/dev/pci/pcireg.h Thu May 5 22:26:23 2016 (r299142) @@ -850,8 +850,16 @@ #define PCIEM_SLOT_CTL_CCIE 0x0010 #define PCIEM_SLOT_CTL_HPIE 0x0020 #define PCIEM_SLOT_CTL_AIC 0x00c0 +#define PCIEM_SLOT_CTL_AI_ON 0x0040 +#define PCIEM_SLOT_CTL_AI_BLINK 0x0080 +#define PCIEM_SLOT_CTL_AI_OFF 0x00c0 #define PCIEM_SLOT_CTL_PIC 0x0300 +#define PCIEM_SLOT_CTL_PI_ON 0x0100 +#define PCIEM_SLOT_CTL_PI_BLINK 0x0200 +#define PCIEM_SLOT_CTL_PI_OFF 0x0300 #define PCIEM_SLOT_CTL_PCC 0x0400 +#define PCIEM_SLOT_CTL_PC_ON 0x0000 +#define PCIEM_SLOT_CTL_PC_OFF 0x0400 #define PCIEM_SLOT_CTL_EIC 0x0800 #define PCIEM_SLOT_CTL_DLLSCE 0x1000 #define PCIER_SLOT_STA 0x1a Modified: head/sys/i386/conf/GENERIC ============================================================================== --- head/sys/i386/conf/GENERIC Thu May 5 22:10:41 2016 (r299141) +++ head/sys/i386/conf/GENERIC Thu May 5 22:26:23 2016 (r299142) @@ -101,6 +101,7 @@ device cpufreq # Bus support. device acpi device pci +options PCI_HP # PCI-Express native HotPlug options PCI_IOV # PCI SR-IOV support # Floppy drives Modified: head/sys/powerpc/conf/GENERIC ============================================================================== --- head/sys/powerpc/conf/GENERIC Thu May 5 22:10:41 2016 (r299141) +++ head/sys/powerpc/conf/GENERIC Thu May 5 22:26:23 2016 (r299142) @@ -102,6 +102,7 @@ device cpufreq # Standard busses device pci +options PCI_HP # PCI-Express native HotPlug device agp # ATA controllers Modified: head/sys/powerpc/conf/GENERIC64 ============================================================================== --- head/sys/powerpc/conf/GENERIC64 Thu May 5 22:10:41 2016 (r299141) +++ head/sys/powerpc/conf/GENERIC64 Thu May 5 22:26:23 2016 (r299142) @@ -99,6 +99,7 @@ device cpufreq # Standard busses device pci +options PCI_HP # PCI-Express native HotPlug device agp # ATA controllers