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 #include @@ -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 + * 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 +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" +#include "opt_kdtrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__) +#include +#endif + +#include +#include +#include +#include + +#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" + "\002" + "\003" + "\004" + "\005" + "\006" + "\007" + "\010" + "\011" + "\012" + "\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" + "\011" + "\012" + "\013" + "\014" + "\015" + "\016" + "\017" + "\020" + "\021" +#endif + "\022ElecMechInt" + "\023NoCCS" +#if 0 + "\024" + "\025" + "\026" + "\027" +#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" + "\017" + "\020" +#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" + "\013" + "\014" + "\015" + "\016" + "\017" + "\020" +#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 +#include +#include #include +#include /* 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) */