Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F112033134
D3932.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
D3932.diff
View Options
Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -2130,6 +2130,7 @@
dev/pci/pci_user.c optional pci
dev/pci/pcib_if.m standard
dev/pci/pcib_support.c standard
+dev/pci/pcie_hp.c optional pci pci_ehp
dev/pci/vga_pci.c optional pci
dev/pcn/if_pcn.c optional pcn pci
dev/pdq/if_fea.c optional fea eisa
Index: sys/conf/options
===================================================================
--- sys/conf/options
+++ sys/conf/options
@@ -176,6 +176,7 @@
MBUF_PACKET_ZONE_DISABLE opt_global.h
PANIC_REBOOT_WAIT_TIME opt_panic.h
PCI_IOV opt_global.h
+PCI_EHP opt_pciehp.h
PPC_DEBUG opt_ppc.h
PPC_PROBE_CHIPSET opt_ppc.h
PPS_SYNC opt_ntp.h
Index: sys/dev/acpica/acpi_pci.c
===================================================================
--- sys/dev/acpica/acpi_pci.c
+++ sys/dev/acpica/acpi_pci.c
@@ -83,6 +83,7 @@
int state);
static void acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child);
static bus_dma_tag_t acpi_pci_get_dma_tag(device_t bus, device_t child);
+static device_t acpi_pci_add_child_method(device_t, uint8_t, uint8_t);
#ifdef PCI_IOV
static device_t acpi_pci_create_iov_child(device_t bus, device_t pf,
@@ -103,6 +104,7 @@
/* PCI interface */
DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method),
+ DEVMETHOD(pci_add_child, acpi_pci_add_child_method),
#ifdef PCI_IOV
DEVMETHOD(pci_create_iov_child, acpi_pci_create_iov_child),
#endif
@@ -223,6 +225,14 @@
return (error);
}
+static device_t
+acpi_pci_add_child_method(device_t dev, uint8_t s,
+ uint8_t f)
+{
+
+ return pci_add_child_size(dev, s, f, sizeof(struct acpi_pci_devinfo));
+}
+
static void
acpi_pci_update_device(ACPI_HANDLE handle, device_t pci_child)
{
@@ -322,7 +332,7 @@
* pci_add_children() doesn't find. We currently just ignore
* these devices.
*/
- pci_add_children(dev, domain, busno, sizeof(struct acpi_pci_devinfo));
+ pci_add_children(dev);
AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 1,
acpi_pci_save_handle, NULL, dev, NULL);
Index: sys/dev/cardbus/cardbus.c
===================================================================
--- sys/dev/cardbus/cardbus.c
+++ sys/dev/cardbus/cardbus.c
@@ -174,7 +174,8 @@
{
device_t brdev = device_get_parent(cbdev);
device_t child;
- int bus, domain, slot, func;
+ uint32_t domain;
+ uint8_t bus, slot, func;
int cardattached = 0;
int cardbusfunchigh = 0;
struct cardbus_softc *sc;
Index: sys/dev/pci/pci.c
===================================================================
--- sys/dev/pci/pci.c
+++ sys/dev/pci/pci.c
@@ -30,6 +30,7 @@
__FBSDID("$FreeBSD$");
#include "opt_bus.h"
+#include "opt_pciehp.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -120,8 +121,11 @@
u_int irq);
static uint16_t pci_get_rid_method(device_t dev, device_t child);
+static int pci_child_present(device_t, uint8_t, uint8_t, uint8_t);
+static int pci_bus_child_present(device_t dev, device_t child);
+static device_t pci_add_child_method(device_t, uint8_t, uint8_t);
-static struct pci_devinfo * pci_fill_devinfo(device_t pcib, int d, int b, int s,
+static struct pci_devinfo *pci_fill_devinfo(device_t pcib, int d, int b, int s,
int f, uint16_t vid, uint16_t did, size_t size);
static device_method_t pci_methods[] = {
@@ -162,6 +166,7 @@
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
DEVMETHOD(bus_suspend_child, pci_suspend_child),
DEVMETHOD(bus_resume_child, pci_resume_child),
+ DEVMETHOD(bus_child_present, pci_bus_child_present),
/* PCI interface */
DEVMETHOD(pci_read_config, pci_read_config_method),
@@ -189,6 +194,7 @@
DEVMETHOD(pci_msix_count, pci_msix_count_method),
DEVMETHOD(pci_get_rid, pci_get_rid_method),
DEVMETHOD(pci_child_added, pci_child_added_method),
+ DEVMETHOD(pci_add_child, pci_add_child_method),
#ifdef PCI_IOV
DEVMETHOD(pci_iov_attach, pci_iov_attach_method),
DEVMETHOD(pci_iov_detach, pci_iov_detach_method),
@@ -611,7 +617,8 @@
/* read configuration header into pcicfgregs structure */
struct pci_devinfo *
-pci_read_device(device_t pcib, int d, int b, int s, int f, size_t size)
+pci_read_device(device_t pcib, uint32_t d, uint8_t b, uint8_t s, uint8_t f,
+ size_t size)
{
#define REG(n, w) PCIB_READ_CONFIG(pcib, b, s, f, n, w)
uint16_t vid, did;
@@ -3505,40 +3512,33 @@
#endif
}
-static struct pci_devinfo *
-pci_identify_function(device_t pcib, device_t dev, int domain, int busno,
- int slot, int func, size_t dinfo_size)
-{
- struct pci_devinfo *dinfo;
-
- dinfo = pci_read_device(pcib, domain, busno, slot, func, dinfo_size);
- if (dinfo != NULL)
- pci_add_child(dev, dinfo);
-
- return (dinfo);
-}
-
void
-pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size)
+pci_add_children(device_t dev)
{
-#define REG(n, w) PCIB_READ_CONFIG(pcib, busno, s, f, n, w)
- device_t pcib = device_get_parent(dev);
- struct pci_devinfo *dinfo;
+ device_t pcib, child;
int maxslots;
int s, f, pcifunchigh;
+ uint32_t domain;
+ uint8_t busno;
uint8_t hdrtype;
int first_func;
+ pcib = device_get_parent(dev);
+ domain = pcib_get_domain(dev);
+ busno = pcib_get_bus(dev);
+
/*
* Try to detect a device at slot 0, function 0. If it exists, try to
* enable ARI. We must enable ARI before detecting the rest of the
* functions on this bus as ARI changes the set of slots and functions
* that are legal on this bus.
*/
- dinfo = pci_identify_function(pcib, dev, domain, busno, 0, 0,
- dinfo_size);
- if (dinfo != NULL && pci_enable_ari)
- PCIB_TRY_ENABLE_ARI(pcib, dinfo->cfg.dev);
+ if (pci_child_present(pcib, busno, 0, 0) == -1 &&
+ pci_find_dbsf(domain, busno, 0, 0) == NULL) {
+ child = PCI_ADD_CHILD(dev, 0, 0);
+ if (pci_enable_ari)
+ PCIB_TRY_ENABLE_ARI(pcib, child);
+ }
/*
* Start looking for new devices on slot 0 at function 1 because we
@@ -3546,23 +3546,21 @@
*/
first_func = 1;
- KASSERT(dinfo_size >= sizeof(struct pci_devinfo),
- ("dinfo_size too small"));
maxslots = PCIB_MAXSLOTS(pcib);
for (s = 0; s <= maxslots; s++, first_func = 0) {
pcifunchigh = 0;
f = 0;
- DELAY(1);
- hdrtype = REG(PCIR_HDRTYPE, 1);
+ hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1);
if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
continue;
if (hdrtype & PCIM_MFDEV)
pcifunchigh = PCIB_MAXFUNCS(pcib);
- for (f = first_func; f <= pcifunchigh; f++)
- pci_identify_function(pcib, dev, domain, busno, s, f,
- dinfo_size);
+ for (f = first_func; f <= pcifunchigh; f++) {
+ if (pci_child_present(pcib, busno, s, f) == -1 &&
+ pci_find_dbsf(domain, busno, s, f) == NULL)
+ PCI_ADD_CHILD(dev, s, f);
+ }
}
-#undef REG
}
#ifdef PCI_IOV
@@ -3613,6 +3611,7 @@
void
pci_add_child(device_t bus, struct pci_devinfo *dinfo)
{
+
dinfo->cfg.dev = device_add_child(bus, NULL, -1);
device_set_ivars(dinfo->cfg.dev, dinfo);
resource_list_init(&dinfo->resources);
@@ -3629,6 +3628,35 @@
}
+static device_t
+pci_add_child_method(device_t dev, uint8_t s, uint8_t f)
+{
+
+ return pci_add_child_size(dev, s, f, sizeof(struct pci_devinfo));
+}
+
+device_t
+pci_add_child_size(device_t dev, uint8_t s, uint8_t f,
+ size_t dinfo_size)
+{
+ device_t pcib;
+ struct pci_devinfo *dinfo;
+ uint32_t d;
+ uint8_t b;
+
+ pcib = device_get_parent(dev);
+ d = pcib_get_domain(dev);
+ b = pcib_get_bus(dev);
+
+ dinfo = pci_read_device(pcib, d, b, s, f, dinfo_size);
+ if (dinfo == NULL)
+ return NULL;
+
+ pci_add_child(dev, dinfo);
+
+ return dinfo->cfg.dev;
+}
+
static int
pci_probe(device_t dev)
{
@@ -3683,13 +3711,18 @@
if (!tag_valid)
#endif
sc->sc_dma_tag = bus_get_dma_tag(dev);
+
+#ifdef PCI_EHP
+ pci_hotplug_init(dev);
+#endif
+
return (0);
}
static int
pci_attach(device_t dev)
{
- int busno, domain, error;
+ int error;
error = pci_attach_common(dev);
if (error)
@@ -3701,9 +3734,7 @@
* the unit number to decide which bus we are probing. We ask
* the parent pcib what our domain and bus numbers are.
*/
- domain = pcib_get_domain(dev);
- busno = pcib_get_bus(dev);
- pci_add_children(dev, domain, busno, sizeof(struct pci_devinfo));
+ pci_add_children(dev);
return (bus_generic_attach(dev));
}
@@ -3806,20 +3837,14 @@
case PCIC_BRIDGE:
case PCIC_BASEPERIPH:
BUS_RESUME_CHILD(dev, child);
+ devlist[i] = NULL;
break;
}
}
for (i = 0; i < numdevs; i++) {
child = devlist[i];
- switch (pci_get_class(child)) {
- case PCIC_DISPLAY:
- case PCIC_MEMORY:
- case PCIC_BRIDGE:
- case PCIC_BASEPERIPH:
- break;
- default:
+ if (child != NULL)
BUS_RESUME_CHILD(dev, child);
- }
}
free(devlist, M_TEMP);
return (0);
@@ -5374,3 +5399,25 @@
return (PCIB_GET_RID(device_get_parent(dev), child));
}
+
+static int
+pci_child_present(device_t pcib, uint8_t b, uint8_t s, uint8_t f)
+{
+
+ if (PCIB_READ_CONFIG(pcib, b, s, f, PCIR_DEVVENDOR, 4) != 0xfffffffful)
+ return -1; /* present */
+
+ return 0;
+}
+
+static int
+pci_bus_child_present(device_t dev, device_t child)
+{
+ uint8_t b, s, f;
+
+ b = pci_get_bus(child);
+ s = pci_get_slot(child);
+ f = pci_get_function(child);
+
+ return pci_child_present(device_get_parent(dev), b, s, f);
+}
Index: sys/dev/pci/pci_if.m
===================================================================
--- sys/dev/pci/pci_if.m
+++ sys/dev/pci/pci_if.m
@@ -202,6 +202,18 @@
device_t child;
};
+
+#
+# Add a new child at slot/function. It is expected that the device is known
+# to be present, but a NULL device_t handle may be returned if there was an
+# issue adding the device.
+#
+METHOD device_t add_child {
+ device_t dev;
+ uint8_t slot;
+ uint8_t func;
+};
+
METHOD int iov_attach {
device_t dev;
device_t child;
Index: sys/dev/pci/pci_private.h
===================================================================
--- sys/dev/pci/pci_private.h
+++ sys/dev/pci/pci_private.h
@@ -48,9 +48,10 @@
extern int pci_do_power_resume;
extern int pci_do_power_suspend;
-void pci_add_children(device_t dev, int domain, int busno,
- size_t dinfo_size);
+void pci_add_children(device_t dev);
void pci_add_child(device_t bus, struct pci_devinfo *dinfo);
+device_t pci_add_child_size(device_t dev, uint8_t s, uint8_t f,
+ size_t dinfo_size);
device_t pci_add_iov_child(device_t bus, device_t pf, size_t dinfo_size,
uint16_t rid, uint16_t vid, uint16_t did);
void pci_add_resources(device_t bus, device_t dev, int force,
@@ -114,8 +115,7 @@
void pci_delete_resource(device_t dev, device_t child,
int type, int rid);
struct resource_list *pci_get_resource_list (device_t dev, device_t child);
-struct pci_devinfo *pci_read_device(device_t pcib, int d, int b, int s, int f,
- size_t size);
+struct pci_devinfo *pci_read_device(device_t pcib, uint32_t d, uint8_t b, uint8_t s, uint8_t f, size_t size);
void pci_print_verbose(struct pci_devinfo *dinfo);
int pci_freecfg(struct pci_devinfo *dinfo);
void pci_child_detached(device_t dev, device_t child);
@@ -152,6 +152,8 @@
int type, int *rid, u_long start, u_long end, u_long count,
u_long num, u_int flags);
+void pci_hotplug_init(device_t dev);
+
int pci_iov_attach_method(device_t bus, device_t dev,
struct nvlist *pf_schema, struct nvlist *vf_schema);
int pci_iov_detach_method(device_t bus, device_t dev);
Index: sys/dev/pci/pcie_hp.c
===================================================================
--- /dev/null
+++ sys/dev/pci/pcie_hp.c
@@ -0,0 +1,546 @@
+/*-
+ * Copyright (c) 2012, Gavin Atkinson <gavin@FreeBSD.org>
+ * Copyright (c) 2015 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by John-Mark Gurney
+ * under sponsorship from the FreeBSD Foundation.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+#include "opt_kdtrace.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/endian.h>
+#include <sys/sdt.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__)
+#include <machine/intr_machdep.h>
+#endif
+
+#include <sys/pciio.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pci_private.h>
+
+#include "pcib_if.h"
+#include "pci_if.h"
+
+SDT_PROVIDER_DEFINE(pci);
+
+SDT_PROBE_DEFINE2(pci, pciehp, intr, entry, "uint32_t", "uint32_t");
+SDT_PROBE_DEFINE4(pci, pciehp, task, entry, "uint32_t", "uint32_t",
+ "uint32_t", "uint32_t");
+SDT_PROBE_DEFINE(pci, pciehp, task, add_children);
+SDT_PROBE_DEFINE(pci, pciehp, task, remove_children);
+SDT_PROBE_DEFINE(pci, pciehp, task, ignored);
+SDT_PROBE_DEFINE1(pci, pciehp, task, other, "uint32_t");
+
+static struct resource_spec hotplug_res_spec_msi[] = {
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
+
+static void
+pci_link_status_print(device_t pcib)
+{
+ struct pci_devinfo *dinfo;
+ int link_sta, pos;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+ link_sta = pci_read_config(pcib, pos + PCIER_LINK_STA, 2);
+ device_printf(pcib, "... LINK_STA=0x%b, width %dx\n",
+ link_sta,
+ "\020"
+#if 0
+ "\001<b0>"
+ "\002<b1>"
+ "\003<b2>"
+ "\004<b3>"
+ "\005<b4>"
+ "\006<b5>"
+ "\007<b6>"
+ "\010<b7>"
+ "\011<b8>"
+ "\012<b9>"
+ "\013Undef"
+#endif
+ "\014LinkTrain"
+ "\015SlotClkConfig"
+ "\016DLLLinkActive"
+ "\017LinkBWManStat"
+ "\020LinkAutonBwStat",
+ (link_sta & 0x03f0) >> 4);
+}
+
+static int power_values[] = { 250, 275, 300 };
+
+static void
+pci_slot_cap_power(uint32_t reg, char buf[6])
+{
+ int val, mag;
+ int whole, fract, fractcnt, fractmul;
+ int avail;
+
+ val = (reg >> 7) & 0xff;
+ mag = (reg >> 15) & 0x3;
+ fract = 0;
+ fractcnt = 0;
+ if (val < 0xf0) {
+ fractmul = 1;
+ whole = val;
+ while (mag--) {
+ fract += (whole % 10) * fractmul;
+ whole /= 10;
+ fractcnt++;
+ fractmul *= 10;
+ }
+ } else {
+ if (val < 0xf3)
+ whole = power_values[val - 0xf0];
+ else
+ whole = 301;
+ }
+ avail = 6;
+ snprintf(buf, avail, "%d", whole);
+ if (fractcnt) {
+ avail -= strlen(buf);
+ buf += strlen(buf);
+ snprintf(buf, avail, ".%0*d", fractcnt, fract);
+ }
+}
+
+static void
+pci_slot_cap_print(device_t pcib)
+{
+ char buf[6];
+ struct pci_devinfo *dinfo;
+ int pos;
+ uint32_t reg;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+ reg = pci_read_config(pcib, pos+PCIER_SLOT_CAP, 4);
+ pci_slot_cap_power(reg, buf);
+ device_printf(pcib, "... SLOT_CAP=0x%b, #%u, %sW\n", reg,
+ "\020"
+ "\001AttnButt"
+ "\002PowerCtrl"
+ "\003MRLSens"
+ "\004AttnInd"
+ "\005PwrInd"
+ "\006HotPlugSurp"
+ "\007HotPlugCap"
+#if 0
+ "\010<b7>"
+ "\011<b8>"
+ "\012<b9>"
+ "\013<b10>"
+ "\014<b11>"
+ "\015<b12>"
+ "\016<b13>"
+ "\017<b14>"
+ "\020<b15>"
+ "\021<b16>"
+#endif
+ "\022ElecMechInt"
+ "\023NoCCS"
+#if 0
+ "\024<b19>"
+ "\025<b20>"
+ "\026<b21>"
+ "\027<b22>"
+#endif
+ , PCIEM_SLOT_CAP_GETPSN(reg), buf);
+}
+
+static void
+pci_slot_control_print(device_t pcib)
+{
+ struct pci_devinfo *dinfo;
+ int pos;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+ device_printf(pcib, "... SLOT_CTL=0x%b\n",
+ pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2),
+ "\020"
+ "\001AttnButtPressEn"
+ "\002PowerFaultDetEn"
+ "\003MRLSensChgEn"
+ "\004PresDetChgEn"
+ "\005CmdCompIntEn"
+ "\006HotPlugIntEn"
+ "\007AttnIndCtl1"
+ "\010AttnIndCtl2"
+ "\012PwrIndCtl2"
+ "\013PwrCtrlrCtl"
+ "\014ElecMechIntCtl"
+ "\015DLLStatChEn"
+#if 0
+ "\016<b13>"
+ "\017<b14>"
+ "\020<b15>"
+#endif
+ );
+}
+
+static void
+pci_slot_status_print(device_t pcib)
+{
+ struct pci_devinfo *dinfo;
+ int pos;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+ device_printf(pcib, "... SLOT_STA=0x%b\n",
+ pci_read_config(pcib, pos + PCIER_SLOT_STA, 2),
+ "\020"
+ "\001AttnButtPress"
+ "\002PowerFaultDet"
+ "\003MRLSensChg"
+ "\004PresDetChg"
+ "\005CmdComplete"
+ "\006MRLSensState"
+ "\007PresDetState"
+ "\010ElecMechIntState"
+ "\011DLLStateChg"
+#if 0
+ "\012<b9>"
+ "\013<b10>"
+ "\014<b11>"
+ "\015<b12>"
+ "\016<b13>"
+ "\017<b14>"
+ "\020<b15>"
+#endif
+ );
+}
+
+void pci_slot_reg_print(device_t pcib);
+void
+pci_slot_reg_print(device_t pcib)
+{
+
+ pci_link_status_print(pcib);
+ pci_slot_cap_print(pcib);
+ pci_slot_status_print(pcib);
+ pci_slot_control_print(pcib);
+}
+
+static void
+rescan_bus(void *arg)
+{
+ device_t dev;
+
+ dev = arg;
+
+ SDT_PROBE(pci, pciehp, task, add_children, 0, 0, 0, 0, 0);
+ pci_add_children(dev);
+ (void)bus_generic_attach(dev);
+
+}
+
+static void
+pci_hotplug_intr_task(void *arg, int npending)
+{
+ device_t dev;
+ device_t pcib;
+ device_t *devlistp;
+ struct pci_devinfo *dinfo;
+ int devcnt, i, pos;
+ uint32_t linksta, slotsta, staclr;
+ uint16_t ctrl;
+
+ dev = arg;
+ pcib = device_get_parent(dev);
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+
+ mtx_lock(&Giant);
+
+ linksta = pci_read_config(pcib, pos + PCIER_LINK_STA, 2);
+ slotsta = pci_read_config(pcib, pos + PCIER_SLOT_STA, 2);
+ staclr = 0;
+
+ SDT_PROBE(pci, pciehp, task, entry,
+ slotsta,
+ linksta,
+ pci_read_config(pcib, pos + PCIER_SLOT_CAP, 2),
+ pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2),
+ 0);
+#if 0
+ pci_slot_reg_print(pcib);
+#endif
+/* XXXGA: HACK AHEAD */
+ if (slotsta & PCIEM_SLOT_STA_CC) {
+ /* XXX - handle command completed events to advance state machine */
+ }
+
+ if (slotsta & PCIEM_SLOT_STA_PDC && /* presence change */
+ dinfo->cfg.hp.hp_slotcap & PCIEM_SLOT_CAP_PCP && /* power ctrlr */
+ slotsta & PCIEM_SLOT_STA_PDS) { /* present */
+ ctrl = pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2);
+ ctrl &= ~PCIEM_SLOT_CTL_PCC;
+ pci_write_config(pcib, pos + PCIER_SLOT_CTL, ctrl, 2);
+ staclr |= PCIEM_SLOT_STA_PDC;
+ /* XXX - start timeout */
+ }
+
+ if (slotsta & PCIEM_SLOT_STA_PDC && /* presence change */
+ dinfo->cfg.hp.hp_slotcap & PCIEM_SLOT_CAP_PCP && /* power ctrlr */
+ !(slotsta & PCIEM_SLOT_STA_PDS)) { /* not present */
+ ctrl = pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2);
+ ctrl |= PCIEM_SLOT_CTL_PCC;
+ pci_write_config(pcib, pos + PCIER_SLOT_CTL, ctrl, 2);
+ staclr |= PCIEM_SLOT_STA_PDC;
+ }
+
+ if (slotsta & PCIEM_SLOT_STA_DLLSC) {
+ if ((linksta & PCIEM_LINK_STA_DL_ACTIVE) &&
+ dinfo->cfg.hp.hp_cnt == 0) {
+ dinfo->cfg.hp.hp_cnt = 1;
+ /*
+ * Per 6.7.3.3, delay for 100ms after DLL Active
+ * before talking to device.
+ */
+ callout_reset(&dinfo->cfg.hp.hp_co, hz / 10,
+ rescan_bus, dev);
+ } else if (((linksta & PCIEM_LINK_STA_DL_ACTIVE) == 0) &&
+ dinfo->cfg.hp.hp_cnt == 1) {
+
+ /*
+ * XXX - do we do the code here or when it is no
+ * longer present?
+ */
+ SDT_PROBE(pci, pciehp, task, remove_children, 0, 0, 0, 0, 0);
+ device_get_children(dev, &devlistp, &devcnt);
+ for (i = 0; i < devcnt; i++)
+ pci_delete_child(dev, devlistp[i]);
+ free(devlistp, M_TEMP);
+ dinfo->cfg.hp.hp_cnt = 0;
+ } else
+ SDT_PROBE(pci, pciehp, task, ignored, 0, 0, 0, 0, 0);
+
+ staclr |= PCIEM_SLOT_STA_DLLSC;
+ }
+
+ /* we only care about bits we can clear after here */
+ slotsta &= PCIEM_SLOT_STA_EMASK;
+
+ /* log any status changes we didn't handle */
+ if (slotsta & ~staclr)
+ SDT_PROBE(pci, pciehp, task, other,
+ slotsta & ~staclr, 0, 0, 0, 0);
+
+ /* always clear the sta reg as if we don't, we get an interrupt storm */
+ pci_write_config(pcib, pos + PCIER_SLOT_STA, slotsta, 2);
+
+ mtx_unlock(&Giant);
+}
+
+static int
+pci_hotplug_intr(void *arg)
+{
+ device_t dev = arg;
+ device_t pcib = device_get_parent(dev);
+ struct pci_devinfo *dinfo;
+ int pos;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+
+ SDT_PROBE(pci, pciehp, intr, entry,
+ pci_read_config(pcib, pos + PCIER_SLOT_STA, 2),
+ pci_read_config(pcib, pos + PCIER_LINK_STA, 2),
+ 0, 0, 0);
+#if 0
+ device_printf(dev, "Received interrupt!\n");
+ pci_slot_status_print(pcib);
+#endif
+ taskqueue_enqueue_fast(taskqueue_fast, &dinfo->cfg.hp.hp_inttask);
+
+ return (FILTER_HANDLED);
+}
+
+/*
+ * XXX - Make sure the minimum allocations are done.
+ *
+ * Minimum allocations for ExpressCard:
+ * 8 busses
+ * 0 Prefetchable
+ * 32Meg non-prefetchable
+ * 4k I/O
+ */
+static void
+pci_hotplug_setup(device_t dev)
+{
+ device_t pcib = device_get_parent(dev);
+ struct pci_devinfo *dinfo;
+ int cap, error, flags, msic, pos;
+ uint16_t slotctl;
+
+ dinfo = device_get_ivars(pcib);
+
+ pos = dinfo->cfg.pcie.pcie_location;
+ flags = pci_read_config(pcib, pos + PCIER_FLAGS, 2);
+ cap = pci_read_config(pcib, pos + PCIER_SLOT_CAP, 4);
+ dinfo->cfg.hp.hp_slotcap = cap;
+
+ if (bootverbose)
+ pci_slot_status_print(pcib);
+
+ dinfo->cfg.hp.hp_cnt = 0;
+ callout_init(&dinfo->cfg.hp.hp_co, 0);
+
+ device_printf(dev, "Hot plug slot number %u\n",
+ PCIEM_SLOT_CAP_GETPSN(cap));
+
+ if (!(cap & PCIEM_SLOT_CAP_NCCS))
+ device_printf(dev, "WARNING! Command Completion Status not supported!");
+
+#if 0
+ /*
+ * IRQ is ignored as PCIe requires MSI. It is possible that we could
+ * have PCIe devices on a PCI bus on a motherboard that doesn't
+ * support MSI, but I hope that we'll do the proper work in the bus
+ * code to support it.
+ */
+ int irq;
+ irq = (flags & PCIEM_FLAGS_IRQ) >> 9;
+ device_printf(dev, "IRQ = %d\n", irq);
+ device_printf(dev, "MSI count self %d parent %d\n",
+ pci_msi_count(dev), pci_msi_count(pcib));
+ device_printf(dev, "MSI-X count self %d parent %d\n",
+ pci_msix_count(dev), pci_msix_count(pcib));
+#endif
+ msic = pci_msi_count(pcib);
+ if (msic) {
+ /* I've seen a device w/ 8 MSI, but the first one works. */
+ msic = 1;
+ if (pci_alloc_msi(pcib, &msic) == 0) {
+ if (msic == 1) {
+ device_printf(dev,
+ "Using %d MSI messages\n",
+ msic);
+ dinfo->cfg.pcie.pcie_irq_spec =
+ hotplug_res_spec_msi;
+ } else {
+ device_printf(dev,
+ "Error: %d MSI messages, ABORTING.\n",
+ msic);
+ pci_release_msi(dev);
+ return;
+ }
+ }
+ }
+ error = bus_alloc_resources(pcib,
+ dinfo->cfg.pcie.pcie_irq_spec,
+ dinfo->cfg.pcie.pcie_res_irq);
+ if (error) {
+ device_printf(dev,
+ "couldn't allocate IRQ resources, %d, ABORTING.\n",
+ error);
+ return;
+ } else {
+ error = bus_setup_intr(pcib,
+ dinfo->cfg.pcie.pcie_res_irq[0],
+ INTR_TYPE_AV | INTR_MPSAFE,
+ pci_hotplug_intr, NULL, dev,
+ &dinfo->cfg.pcie.pcie_intrhand[0]);
+ if (error) {
+ device_printf(dev, "couldn't set up IRQ resources, %d\n",
+ error);
+ }
+ }
+
+ TASK_INIT(&dinfo->cfg.hp.hp_inttask, 0,
+ pci_hotplug_intr_task, dev);
+
+ /* Select and enable events to interrupt upon */
+ slotctl = pci_read_config(pcib, pos + PCIER_SLOT_CTL, 2);
+ slotctl |= PCIEM_SLOT_CTL_PDCE | PCIEM_SLOT_CTL_HPIE;
+ if (cap & PCIEM_SLOT_CAP_MRLSP)
+ slotctl |= PCIEM_SLOT_CTL_MRLSCE;
+ if (pci_read_config(pcib, pos + PCIER_LINK_CAP, 4) &
+ PCIEM_LINK_CAP_DL_ACTIVE)
+ slotctl |= PCIEM_SLOT_CTL_DLLSCE;
+ pci_write_config(pcib, pos + PCIER_SLOT_CTL, slotctl, 2);
+ if (bootverbose) {
+ device_printf(dev, "Enabled interrupts\n");
+ pci_slot_control_print(pcib);
+ pci_slot_status_print(pcib);
+ }
+
+ /* XXX - Do we check for presence? */
+ if (pci_read_config(pcib, pos + PCIER_LINK_STA,
+ 2) & PCIEM_LINK_STA_DL_ACTIVE) {
+ dinfo->cfg.hp.hp_cnt = 1;
+ /* XXX - should we wait 100ms? */
+ }
+}
+
+void
+pci_hotplug_init(device_t dev)
+{
+ device_t pcib = device_get_parent(dev);
+ struct pci_devinfo *dinfo;
+ int cap, flags, pos;
+
+ dinfo = device_get_ivars(pcib);
+ pos = dinfo->cfg.pcie.pcie_location;
+ if (pos != 0) {
+ flags = pci_read_config(pcib, pos + PCIER_FLAGS, 2);
+ cap = pci_read_config(pcib, pos + PCIER_SLOT_CAP, 4);
+ if (bootverbose)
+ pci_slot_cap_print(pcib);
+ if ((flags & PCIEM_FLAGS_SLOT &&
+ cap & PCIEM_SLOT_CAP_HPC)) {
+ pci_hotplug_setup(dev);
+ }
+ }
+}
Index: sys/dev/pci/pcireg.h
===================================================================
--- sys/dev/pci/pcireg.h
+++ sys/dev/pci/pcireg.h
@@ -768,6 +768,7 @@
#define PCIEM_SLOT_CAP_EIP 0x00020000
#define PCIEM_SLOT_CAP_NCCS 0x00040000
#define PCIEM_SLOT_CAP_PSN 0xfff80000
+#define PCIEM_SLOT_CAP_GETPSN(x) (((x) & PCIEM_SLOT_CAP_PSN) >> 19)
#define PCIER_SLOT_CTL 0x18
#define PCIEM_SLOT_CTL_ABPE 0x0001
#define PCIEM_SLOT_CTL_PFDE 0x0002
@@ -790,6 +791,7 @@
#define PCIEM_SLOT_STA_PDS 0x0040
#define PCIEM_SLOT_STA_EIS 0x0080
#define PCIEM_SLOT_STA_DLLSC 0x0100
+#define PCIEM_SLOT_STA_EMASK 0x011f
#define PCIER_ROOT_CTL 0x1c
#define PCIEM_ROOT_CTL_SERR_CORR 0x0001
#define PCIEM_ROOT_CTL_SERR_NONFATAL 0x0002
Index: sys/dev/pci/pcivar.h
===================================================================
--- sys/dev/pci/pcivar.h
+++ sys/dev/pci/pcivar.h
@@ -30,7 +30,11 @@
#ifndef _PCIVAR_H_
#define _PCIVAR_H_
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
#include <sys/queue.h>
+#include <sys/taskqueue.h>
/* some PCI bus constants */
#define PCI_MAXMAPS_0 6 /* max. no. of memory/port maps */
@@ -133,6 +137,8 @@
uint64_t ht_msiaddr; /* MSI mapping base address */
};
+#define PCIE_MSI_MESSAGES 2
+
/* Interesting values for PCI-express */
struct pcicfg_pcie {
uint8_t pcie_location; /* Offset of PCI-e capability registers. */
@@ -145,6 +151,9 @@
uint16_t pcie_device_ctl2; /* Second device control register. */
uint16_t pcie_link_ctl2; /* Second link control register. */
uint16_t pcie_slot_ctl2; /* Second slot control register. */
+ struct resource_spec *pcie_irq_spec;
+ struct resource *pcie_res_irq[PCIE_MSI_MESSAGES];
+ void *pcie_intrhand[PCIE_MSI_MESSAGES];
};
struct pcicfg_pcix {
@@ -156,6 +165,14 @@
int index;
};
+/* Interesting values for PCIe Hotplug */
+struct pcicfg_hp {
+ struct task hp_inttask;
+ struct callout hp_co;
+ int hp_cnt; /* Giant locked */
+ uint32_t hp_slotcap; /* cache this */
+};
+
#define PCICFG_VF 0x0001 /* Device is an SR-IOV Virtual Function */
/* config header information common to all header types */
@@ -207,6 +224,7 @@
struct pcicfg_pcix pcix; /* PCI-X */
struct pcicfg_iov *iov; /* SR-IOV */
struct pcicfg_vf vf; /* SR-IOV Virtual Function */
+ struct pcicfg_hp hp; /* Hotplug */
} pcicfgregs;
/* additional type 1 device config header information (PCI to PCI bridge) */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 12, 8:49 PM (12 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17125903
Default Alt Text
D3932.diff (27 KB)
Attached To
Mode
D3932: PCIe HotPlug support
Attached
Detach File
Event Timeline
Log In to Comment