summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2016-05-05 22:26:23 +0000
committerjhb <jhb@FreeBSD.org>2016-05-05 22:26:23 +0000
commiteb663acb54bc8677cc47448aa68f2e2393d5455b (patch)
tree29e5b04d28fc642267d56b82b45a237b92fda1c3
parentf05973c5eca6f66c0e216887c7125608d22b3689 (diff)
downloadFreeBSD-src-eb663acb54bc8677cc47448aa68f2e2393d5455b.zip
FreeBSD-src-eb663acb54bc8677cc47448aa68f2e2393d5455b.tar.gz
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
-rw-r--r--sys/amd64/conf/GENERIC1
-rw-r--r--sys/arm64/conf/GENERIC1
-rw-r--r--sys/conf/NOTES1
-rw-r--r--sys/conf/options1
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c7
-rw-r--r--sys/dev/pci/pci_pci.c530
-rw-r--r--sys/dev/pci/pcib_private.h20
-rw-r--r--sys/dev/pci/pcireg.h8
-rw-r--r--sys/i386/conf/GENERIC1
-rw-r--r--sys/powerpc/conf/GENERIC1
-rw-r--r--sys/powerpc/conf/GENERIC641
11 files changed, 572 insertions, 0 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 747acc5..2859de5 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -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
diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC
index 2b52196..9300e77 100644
--- a/sys/arm64/conf/GENERIC
+++ b/sys/arm64/conf/GENERIC
@@ -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
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index f1553bf..51e81f1 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -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
diff --git a/sys/conf/options b/sys/conf/options
index 4d6ee66..61bbaa7 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -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
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index e9bcbd1..506e267 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -29,6 +29,8 @@
__FBSDID("$FreeBSD$");
#include "opt_acpi.h"
+#include "opt_pci.h"
+
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
@@ -312,6 +314,11 @@ acpi_pcib_osc(struct acpi_hpcib_softc *sc)
/* 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)) {
diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c
index 76643bc..e3539f5 100644
--- a/sys/dev/pci/pci_pci.c
+++ b/sys/dev/pci/pci_pci.c
@@ -35,6 +35,8 @@ __FBSDID("$FreeBSD$");
* PCI:PCI bridge support.
*/
+#include "opt_pci.h"
+
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
@@ -43,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
+#include <sys/taskqueue.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
@@ -67,6 +70,11 @@ static int pcib_try_enable_ari(device_t pcib, device_t dev);
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 *sc)
}
#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, uint16_t rid, int *bus, int *slot,
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, u_int s, u_int f, u_int reg, int width)
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);
diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h
index 93d9a71..c209162 100644
--- a/sys/dev/pci/pcib_private.h
+++ b/sys/dev/pci/pcib_private.h
@@ -33,6 +33,9 @@
#ifndef __PCIB_PRIVATE_H__
#define __PCIB_PRIVATE_H__
+#include <sys/_callout.h>
+#include <sys/_task.h>
+
#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,
diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h
index 68b5378..d463b7a 100644
--- a/sys/dev/pci/pcireg.h
+++ b/sys/dev/pci/pcireg.h
@@ -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
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index ee2df93..aefc507 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -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
diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC
index f67e5e5..d464ac7 100644
--- a/sys/powerpc/conf/GENERIC
+++ b/sys/powerpc/conf/GENERIC
@@ -102,6 +102,7 @@ device cpufreq
# Standard busses
device pci
+options PCI_HP # PCI-Express native HotPlug
device agp
# ATA controllers
diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64
index 847101f..88d4811 100644
--- a/sys/powerpc/conf/GENERIC64
+++ b/sys/powerpc/conf/GENERIC64
@@ -99,6 +99,7 @@ device cpufreq
# Standard busses
device pci
+options PCI_HP # PCI-Express native HotPlug
device agp
# ATA controllers
OpenPOWER on IntegriCloud