summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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