diff options
Diffstat (limited to 'freebsd/sys/dev/pci/pci_pci.c')
-rw-r--r-- | freebsd/sys/dev/pci/pci_pci.c | 1353 |
1 files changed, 1212 insertions, 141 deletions
diff --git a/freebsd/sys/dev/pci/pci_pci.c b/freebsd/sys/dev/pci/pci_pci.c index 6c159aec..7d763dd9 100644 --- a/freebsd/sys/dev/pci/pci_pci.c +++ b/freebsd/sys/dev/pci/pci_pci.c @@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$"); * PCI:PCI bridge support. */ +#include <rtems/bsd/local/opt_pci.h> + #include <rtems/bsd/sys/param.h> #include <sys/bus.h> #include <sys/kernel.h> @@ -45,6 +47,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> @@ -58,17 +61,35 @@ static int pcib_suspend(device_t dev); static int pcib_resume(device_t dev); static int pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate); +static int pcib_ari_get_id(device_t pcib, device_t dev, + enum pci_id_type type, uintptr_t *id); +static uint32_t 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); +static int pcib_ari_maxslots(device_t dev); +static int pcib_ari_maxfuncs(device_t dev); +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 */ DEVMETHOD(device_probe, pcib_probe), DEVMETHOD(device_attach, pcib_attach), - DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_detach, pcib_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, pcib_suspend), 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), @@ -85,7 +106,8 @@ static device_method_t pcib_methods[] = { DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ - DEVMETHOD(pcib_maxslots, pcib_maxslots), + DEVMETHOD(pcib_maxslots, pcib_ari_maxslots), + DEVMETHOD(pcib_maxfuncs, pcib_ari_maxfuncs), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), @@ -95,6 +117,10 @@ static device_method_t pcib_methods[] = { DEVMETHOD(pcib_release_msix, pcib_release_msix), DEVMETHOD(pcib_map_msi, pcib_map_msi), DEVMETHOD(pcib_power_for_sleep, pcib_power_for_sleep), + DEVMETHOD(pcib_get_id, pcib_ari_get_id), + DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari), + DEVMETHOD(pcib_ari_enabled, pcib_ari_enabled), + DEVMETHOD(pcib_decode_rid, pcib_ari_decode_rid), DEVMETHOD_END }; @@ -104,11 +130,12 @@ static devclass_t pcib_devclass; DEFINE_CLASS_0(pcib, pcib_driver, pcib_methods, sizeof(struct pcib_softc)); DRIVER_MODULE(pcib, pci, pcib_driver, pcib_devclass, NULL, NULL); -#ifdef NEW_PCIB +#if defined(NEW_PCIB) || defined(PCI_HP) SYSCTL_DECL(_hw_pci); +#endif +#ifdef NEW_PCIB static int pci_clear_pcib; -TUNABLE_INT("hw.pci.clear_pcib", &pci_clear_pcib); SYSCTL_INT(_hw_pci, OID_AUTO, clear_pcib, CTLFLAG_RDTUN, &pci_clear_pcib, 0, "Clear firmware-assigned resources for PCI-PCI bridge I/O windows."); @@ -121,6 +148,10 @@ pcib_is_resource_managed(struct pcib_softc *sc, int type, struct resource *r) { switch (type) { +#ifdef PCI_RES_BUS + case PCI_RES_BUS: + return (rman_is_region_manager(r, &sc->bus.rman)); +#endif case SYS_RES_IOPORT: return (rman_is_region_manager(r, &sc->io.rman)); case SYS_RES_MEMORY: @@ -195,9 +226,10 @@ pcib_write_windows(struct pcib_softc *sc, int mask) * ISA alias range. */ static int -pcib_is_isa_range(struct pcib_softc *sc, u_long start, u_long end, u_long count) +pcib_is_isa_range(struct pcib_softc *sc, rman_res_t start, rman_res_t end, + rman_res_t count) { - u_long next_alias; + rman_res_t next_alias; if (!(sc->bridgectl & PCIB_BCR_ISA_ENABLE)) return (0); @@ -229,7 +261,7 @@ pcib_is_isa_range(struct pcib_softc *sc, u_long start, u_long end, u_long count) alias: if (bootverbose) device_printf(sc->dev, - "I/O range %#lx-%#lx overlaps with an ISA alias\n", start, + "I/O range %#jx-%#jx overlaps with an ISA alias\n", start, end); return (1); } @@ -249,7 +281,7 @@ pcib_add_window_resources(struct pcib_window *w, struct resource **res, free(w->res, M_DEVBUF); w->res = newarray; w->count += count; - + for (i = 0; i < count; i++) { error = rman_manage_region(&w->rman, rman_get_start(res[i]), rman_get_end(res[i])); @@ -258,13 +290,13 @@ pcib_add_window_resources(struct pcib_window *w, struct resource **res, } } -typedef void (nonisa_callback)(u_long start, u_long end, void *arg); +typedef void (nonisa_callback)(rman_res_t start, rman_res_t end, void *arg); static void -pcib_walk_nonisa_ranges(u_long start, u_long end, nonisa_callback *cb, +pcib_walk_nonisa_ranges(rman_res_t start, rman_res_t end, nonisa_callback *cb, void *arg) { - u_long next_end; + rman_res_t next_end; /* * If start is within an ISA alias range, move up to the start @@ -292,7 +324,7 @@ pcib_walk_nonisa_ranges(u_long start, u_long end, nonisa_callback *cb, } static void -count_ranges(u_long start, u_long end, void *arg) +count_ranges(rman_res_t start, rman_res_t end, void *arg) { int *countp; @@ -307,7 +339,7 @@ struct alloc_state { }; static void -alloc_ranges(u_long start, u_long end, void *arg) +alloc_ranges(rman_res_t start, rman_res_t end, void *arg) { struct alloc_state *as; struct pcib_window *w; @@ -321,7 +353,7 @@ alloc_ranges(u_long start, u_long end, void *arg) rid = w->reg; if (bootverbose) device_printf(as->sc->dev, - "allocating non-ISA range %#lx-%#lx\n", start, end); + "allocating non-ISA range %#jx-%#jx\n", start, end); as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT, &rid, start, end, end - start + 1, 0); if (as->res[as->count] == NULL) @@ -331,7 +363,7 @@ alloc_ranges(u_long start, u_long end, void *arg) } static int -pcib_alloc_nonisa_ranges(struct pcib_softc *sc, u_long start, u_long end) +pcib_alloc_nonisa_ranges(struct pcib_softc *sc, rman_res_t start, rman_res_t end) { struct alloc_state as; int i, new_count; @@ -370,8 +402,8 @@ pcib_alloc_window(struct pcib_softc *sc, struct pcib_window *w, int type, char buf[64]; int error, rid; - if (max_address != (u_long)max_address) - max_address = ~0ul; + if (max_address != (rman_res_t)max_address) + max_address = ~0; w->rman.rm_start = 0; w->rman.rm_end = max_address; w->rman.rm_type = RMAN_ARRAY; @@ -425,16 +457,7 @@ pcib_probe_windows(struct pcib_softc *sc) dev = sc->dev; if (pci_clear_pcib) { - pci_write_config(dev, PCIR_IOBASEL_1, 0xff, 1); - pci_write_config(dev, PCIR_IOBASEH_1, 0xffff, 2); - pci_write_config(dev, PCIR_IOLIMITL_1, 0, 1); - pci_write_config(dev, PCIR_IOLIMITH_1, 0, 2); - pci_write_config(dev, PCIR_MEMBASE_1, 0xffff, 2); - pci_write_config(dev, PCIR_MEMLIMIT_1, 0, 2); - pci_write_config(dev, PCIR_PMBASEL_1, 0xffff, 2); - pci_write_config(dev, PCIR_PMBASEH_1, 0xffffffff, 4); - pci_write_config(dev, PCIR_PMLIMITL_1, 0, 2); - pci_write_config(dev, PCIR_PMLIMITH_1, 0, 4); + pcib_bridge_init(dev); } /* Determine if the I/O port window is implemented. */ @@ -525,6 +548,231 @@ pcib_probe_windows(struct pcib_softc *sc) } } +static void +pcib_release_window(struct pcib_softc *sc, struct pcib_window *w, int type) +{ + device_t dev; + int error, i; + + if (!w->valid) + return; + + dev = sc->dev; + error = rman_fini(&w->rman); + if (error) { + device_printf(dev, "failed to release %s rman\n", w->name); + return; + } + free(__DECONST(char *, w->rman.rm_descr), M_DEVBUF); + + for (i = 0; i < w->count; i++) { + error = bus_free_resource(dev, type, w->res[i]); + if (error) + device_printf(dev, + "failed to release %s resource: %d\n", w->name, + error); + } + free(w->res, M_DEVBUF); +} + +static void +pcib_free_windows(struct pcib_softc *sc) +{ + + pcib_release_window(sc, &sc->pmem, SYS_RES_MEMORY); + pcib_release_window(sc, &sc->mem, SYS_RES_MEMORY); + pcib_release_window(sc, &sc->io, SYS_RES_IOPORT); +} + +#ifdef PCI_RES_BUS +/* + * Allocate a suitable secondary bus for this bridge if needed and + * initialize the resource manager for the secondary bus range. Note + * that the minimum count is a desired value and this may allocate a + * smaller range. + */ +void +pcib_setup_secbus(device_t dev, struct pcib_secbus *bus, int min_count) +{ + char buf[64]; + int error, rid, sec_reg; + + switch (pci_read_config(dev, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) { + case PCIM_HDRTYPE_BRIDGE: + sec_reg = PCIR_SECBUS_1; + bus->sub_reg = PCIR_SUBBUS_1; + break; + case PCIM_HDRTYPE_CARDBUS: + sec_reg = PCIR_SECBUS_2; + bus->sub_reg = PCIR_SUBBUS_2; + break; + default: + panic("not a PCI bridge"); + } + bus->sec = pci_read_config(dev, sec_reg, 1); + bus->sub = pci_read_config(dev, bus->sub_reg, 1); + bus->dev = dev; + bus->rman.rm_start = 0; + bus->rman.rm_end = PCI_BUSMAX; + bus->rman.rm_type = RMAN_ARRAY; + snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev)); + bus->rman.rm_descr = strdup(buf, M_DEVBUF); + error = rman_init(&bus->rman); + if (error) + panic("Failed to initialize %s bus number rman", + device_get_nameunit(dev)); + + /* + * Allocate a bus range. This will return an existing bus range + * if one exists, or a new bus range if one does not. + */ + rid = 0; + bus->res = bus_alloc_resource_anywhere(dev, PCI_RES_BUS, &rid, + min_count, 0); + if (bus->res == NULL) { + /* + * Fall back to just allocating a range of a single bus + * number. + */ + bus->res = bus_alloc_resource_anywhere(dev, PCI_RES_BUS, &rid, + 1, 0); + } else if (rman_get_size(bus->res) < min_count) + /* + * Attempt to grow the existing range to satisfy the + * minimum desired count. + */ + (void)bus_adjust_resource(dev, PCI_RES_BUS, bus->res, + rman_get_start(bus->res), rman_get_start(bus->res) + + min_count - 1); + + /* + * Add the initial resource to the rman. + */ + if (bus->res != NULL) { + error = rman_manage_region(&bus->rman, rman_get_start(bus->res), + rman_get_end(bus->res)); + if (error) + panic("Failed to add resource to rman"); + bus->sec = rman_get_start(bus->res); + bus->sub = rman_get_end(bus->res); + } +} + +void +pcib_free_secbus(device_t dev, struct pcib_secbus *bus) +{ + int error; + + error = rman_fini(&bus->rman); + if (error) { + device_printf(dev, "failed to release bus number rman\n"); + return; + } + free(__DECONST(char *, bus->rman.rm_descr), M_DEVBUF); + + error = bus_free_resource(dev, PCI_RES_BUS, bus->res); + if (error) + device_printf(dev, + "failed to release bus numbers resource: %d\n", error); +} + +static struct resource * +pcib_suballoc_bus(struct pcib_secbus *bus, device_t child, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource *res; + + res = rman_reserve_resource(&bus->rman, start, end, count, flags, + child); + if (res == NULL) + return (NULL); + + if (bootverbose) + device_printf(bus->dev, + "allocated bus range (%ju-%ju) for rid %d of %s\n", + rman_get_start(res), rman_get_end(res), *rid, + pcib_child_name(child)); + rman_set_rid(res, *rid); + return (res); +} + +/* + * Attempt to grow the secondary bus range. This is much simpler than + * for I/O windows as the range can only be grown by increasing + * subbus. + */ +static int +pcib_grow_subbus(struct pcib_secbus *bus, rman_res_t new_end) +{ + rman_res_t old_end; + int error; + + old_end = rman_get_end(bus->res); + KASSERT(new_end > old_end, ("attempt to shrink subbus")); + error = bus_adjust_resource(bus->dev, PCI_RES_BUS, bus->res, + rman_get_start(bus->res), new_end); + if (error) + return (error); + if (bootverbose) + device_printf(bus->dev, "grew bus range to %ju-%ju\n", + rman_get_start(bus->res), rman_get_end(bus->res)); + error = rman_manage_region(&bus->rman, old_end + 1, + rman_get_end(bus->res)); + if (error) + panic("Failed to add resource to rman"); + bus->sub = rman_get_end(bus->res); + pci_write_config(bus->dev, bus->sub_reg, bus->sub, 1); + return (0); +} + +struct resource * +pcib_alloc_subbus(struct pcib_secbus *bus, device_t child, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource *res; + rman_res_t start_free, end_free, new_end; + + /* + * First, see if the request can be satisified by the existing + * bus range. + */ + res = pcib_suballoc_bus(bus, child, rid, start, end, count, flags); + if (res != NULL) + return (res); + + /* + * Figure out a range to grow the bus range. First, find the + * first bus number after the last allocated bus in the rman and + * enforce that as a minimum starting point for the range. + */ + if (rman_last_free_region(&bus->rman, &start_free, &end_free) != 0 || + end_free != bus->sub) + start_free = bus->sub + 1; + if (start_free < start) + start_free = start; + new_end = start_free + count - 1; + + /* + * See if this new range would satisfy the request if it + * succeeds. + */ + if (new_end > end) + return (NULL); + + /* Finally, attempt to grow the existing resource. */ + if (bootverbose) { + device_printf(bus->dev, + "attempting to grow bus range for %ju buses\n", count); + printf("\tback candidate range: %ju-%ju\n", start_free, + new_end); + } + if (pcib_grow_subbus(bus, new_end) == 0) + return (pcib_suballoc_bus(bus, child, rid, start, end, count, + flags)); + return (NULL); +} +#endif + #else /* @@ -604,7 +852,7 @@ pcib_get_mem_decode(struct pcib_softc *sc) sc->pmembase = PCI_PPBMEMBASE(0, pmemlow); pmemlow = pci_read_config(dev, PCIR_PMLIMITL_1, 2); - if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) + if ((pmemlow & PCIM_BRPM_MASK) == PCIM_BRPM_64) sc->pmemlimit = PCI_PPBMEMLIMIT( pci_read_config(dev, PCIR_PMLIMITH_1, 4), pmemlow); else @@ -659,26 +907,564 @@ pcib_set_mem_decode(struct pcib_softc *sc) } #endif +#ifdef PCI_HP +/* + * PCI-express HotPlug support. + */ +static int pci_enable_pcie_hp = 1; +SYSCTL_INT(_hw_pci, OID_AUTO, enable_pcie_hp, CTLFLAG_RDTUN, + &pci_enable_pcie_hp, 0, + "Enable support for native PCI-express HotPlug."); + +static void +pcib_probe_hotplug(struct pcib_softc *sc) +{ + device_t dev; + uint16_t link_sta, slot_sta; + + if (!pci_enable_pcie_hp) + return; + + 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) == 0) + return; + + /* + * Some devices report that they have an MRL when they actually + * do not. Since they always report that the MRL is open, child + * devices would be ignored. Try to detect these devices and + * ignore their claim of HotPlug support. + * + * If there is an open MRL but the Data Link Layer is active, + * the MRL is not real. + */ + if ((sc->pcie_slot_cap & PCIEM_SLOT_CAP_MRLSP) != 0 && + (sc->pcie_link_cap & PCIEM_LINK_CAP_DL_ACTIVE) != 0) { + link_sta = pcie_read_config(dev, PCIER_LINK_STA, 2); + slot_sta = pcie_read_config(dev, PCIER_SLOT_STA, 2); + if ((slot_sta & PCIEM_SLOT_STA_MRLSS) != 0 && + (link_sta & PCIEM_LINK_STA_DL_ACTIVE) != 0) { + return; + } + } + + sc->flags |= PCIB_HOTPLUG; +} + +/* + * Send a HotPlug command to the slot control register. If this slot + * uses command completion interrupts and a previous command is still + * in progress, then the command is dropped. Once the previous + * command completes or times out, pcib_pcie_hotplug_update() will be + * invoked to post a new command based on the slot's state at that + * time. + */ +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->flags & PCIB_HOTPLUG_CMD_PENDING) + return; + + ctl = pcie_read_config(dev, PCIER_SLOT_CTL, 2); + new = (ctl & ~mask) | val; + if (new == ctl) + return; + if (bootverbose) + device_printf(dev, "HotPlug command: %04x -> %04x\n", ctl, new); + pcie_write_config(dev, PCIER_SLOT_CTL, new, 2); + if (!(sc->pcie_slot_cap & PCIEM_SLOT_CAP_NCCS) && + (ctl & new) & PCIEM_SLOT_CTL_CCIE) { + 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; + + dev = sc->dev; + + if (bootverbose) + device_printf(dev, "Command Completed\n"); + if (!(sc->flags & PCIB_HOTPLUG_CMD_PENDING)) + return; + callout_stop(&sc->pcie_cc_timer); + sc->flags &= ~PCIB_HOTPLUG_CMD_PENDING; + wakeup(sc); +} + +/* + * 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) +{ + + /* 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, ei_engaged; + + /* Clear DETACHING if Presence 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) { + mask |= PCIEM_SLOT_CTL_EIC; + ei_engaged = (sc->pcie_slot_sta & PCIEM_SLOT_STA_EIS) != 0; + if (card_inserted != ei_engaged) + 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 or MRL Sensor + * 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_MRLSC | 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 synchronously; + * 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 (bootverbose) + device_printf(dev, "HotPlug interrupt: %#x\n", + sc->pcie_slot_sta); + + 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, "Presence 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; + uint16_t sta; + + sc = arg; + dev = sc->dev; + mtx_assert(&Giant, MA_OWNED); + sta = pcie_read_config(dev, PCIER_SLOT_STA, 2); + if (!(sta & PCIEM_SLOT_STA_CC)) { + 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); + } else { + device_printf(dev, + "Missed HotPlug interrupt waiting for Command Completion\n"); + pcib_pcie_intr(sc); + } +} + +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 int +pcib_release_pcie_irq(struct pcib_softc *sc) +{ + device_t dev; + int error; + + dev = sc->dev; + error = bus_teardown_intr(dev, sc->pcie_irq, sc->pcie_ihand); + if (error) + return (error); + error = bus_free_resource(dev, SYS_RES_IRQ, sc->pcie_irq); + if (error) + return (error); + return (pci_release_msi(dev)); +} + +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); + + /* Clear any events previously pending. */ + pcie_write_config(dev, PCIER_SLOT_STA, sc->pcie_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); +} + +static int +pcib_detach_hotplug(struct pcib_softc *sc) +{ + uint16_t mask, val; + int error; + + /* Disable the card in the slot and force it to detach. */ + if (sc->flags & PCIB_DETACH_PENDING) { + sc->flags &= ~PCIB_DETACH_PENDING; + callout_stop(&sc->pcie_ab_timer); + } + sc->flags |= PCIB_DETACHING; + + if (sc->flags & PCIB_HOTPLUG_CMD_PENDING) { + callout_stop(&sc->pcie_cc_timer); + tsleep(sc, 0, "hpcmd", hz); + sc->flags &= ~PCIB_HOTPLUG_CMD_PENDING; + } + + /* Disable 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 = 0; + + /* 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); + + error = pcib_release_pcie_irq(sc); + if (error) + return (error); + taskqueue_drain(taskqueue_thread, &sc->pcie_hp_task); + callout_drain(&sc->pcie_ab_timer); + callout_drain(&sc->pcie_cc_timer); + callout_drain(&sc->pcie_dll_timer); + return (0); +} +#endif + /* * Get current bridge configuration. */ static void pcib_cfg_save(struct pcib_softc *sc) { +#ifndef NEW_PCIB device_t dev; + uint16_t command; dev = sc->dev; - sc->command = pci_read_config(dev, PCIR_COMMAND, 2); - sc->pribus = pci_read_config(dev, PCIR_PRIBUS_1, 1); - sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1); - sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1); - sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2); - sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1); -#ifndef NEW_PCIB - if (sc->command & PCIM_CMD_PORTEN) + command = pci_read_config(dev, PCIR_COMMAND, 2); + if (command & PCIM_CMD_PORTEN) pcib_get_io_decode(sc); - if (sc->command & PCIM_CMD_MEMEN) + if (command & PCIM_CMD_MEMEN) pcib_get_mem_decode(sc); #endif } @@ -690,21 +1476,18 @@ static void pcib_cfg_restore(struct pcib_softc *sc) { device_t dev; - +#ifndef NEW_PCIB + uint16_t command; +#endif dev = sc->dev; - pci_write_config(dev, PCIR_COMMAND, sc->command, 2); - pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1); - pci_write_config(dev, PCIR_SECBUS_1, sc->secbus, 1); - pci_write_config(dev, PCIR_SUBBUS_1, sc->subbus, 1); - pci_write_config(dev, PCIR_BRIDGECTL_1, sc->bridgectl, 2); - pci_write_config(dev, PCIR_SECLAT_1, sc->seclat, 1); #ifdef NEW_PCIB pcib_write_windows(sc, WIN_IO | WIN_MEM | WIN_PMEM); #else - if (sc->command & PCIM_CMD_PORTEN) + command = pci_read_config(dev, PCIR_COMMAND, 2); + if (command & PCIM_CMD_PORTEN) pcib_set_io_decode(sc); - if (sc->command & PCIM_CMD_MEMEN) + if (command & PCIM_CMD_MEMEN) pcib_set_mem_decode(sc); #endif } @@ -738,10 +1521,21 @@ pcib_attach_common(device_t dev) * Get current bridge configuration. */ sc->domain = pci_get_domain(dev); - sc->secstat = pci_read_config(dev, PCIR_SECSTAT_1, 2); +#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) + sc->bus.sec = pci_read_config(dev, PCIR_SECBUS_1, 1); + sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1); +#endif + sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2); pcib_cfg_save(sc); /* + * The primary bus register should always be the bus of the + * parent. + */ + sc->pribus = pci_get_bus(dev); + pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1); + + /* * Setup sysctl reporting nodes */ sctx = device_get_sysctl_ctx(dev); @@ -751,65 +1545,68 @@ pcib_attach_common(device_t dev) SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus", CTLFLAG_RD, &sc->pribus, 0, "Primary bus number"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus", - CTLFLAG_RD, &sc->secbus, 0, "Secondary bus number"); + CTLFLAG_RD, &sc->bus.sec, 0, "Secondary bus number"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus", - CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number"); + CTLFLAG_RD, &sc->bus.sub, 0, "Subordinate bus number"); /* * Quirk handling. */ switch (pci_get_devid(dev)) { +#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) case 0x12258086: /* Intel 82454KX/GX (Orion) */ { uint8_t supbus; supbus = pci_read_config(dev, 0x41, 1); if (supbus != 0xff) { - sc->secbus = supbus + 1; - sc->subbus = supbus + 1; + sc->bus.sec = supbus + 1; + sc->bus.sub = supbus + 1; } break; } +#endif /* * The i82380FB mobile docking controller is a PCI-PCI bridge, * and it is a subtractive bridge. However, the ProgIf is wrong * so the normal setting of PCIB_SUBTRACTIVE bit doesn't - * happen. There's also a Toshiba bridge that behaves this - * way. + * happen. There are also Toshiba and Cavium ThunderX bridges + * that behave this way. */ + case 0xa002177d: /* Cavium ThunderX */ case 0x124b8086: /* Intel 82380FB Mobile */ case 0x060513d7: /* Toshiba ???? */ sc->flags |= PCIB_SUBTRACTIVE; break; -#ifndef __rtems__ +#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS)) /* Compaq R3000 BIOS sets wrong subordinate bus number. */ case 0x00dd10de: { char *cp; - if ((cp = getenv("smbios.planar.maker")) == NULL) + if ((cp = kern_getenv("smbios.planar.maker")) == NULL) break; if (strncmp(cp, "Compal", 6) != 0) { freeenv(cp); break; } freeenv(cp); - if ((cp = getenv("smbios.planar.product")) == NULL) + if ((cp = kern_getenv("smbios.planar.product")) == NULL) break; if (strncmp(cp, "08A0", 4) != 0) { freeenv(cp); break; } freeenv(cp); - if (sc->subbus < 0xa) { + if (sc->bus.sub < 0xa) { pci_write_config(dev, PCIR_SUBBUS_1, 0xa, 1); - sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1); + sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1); } break; } -#endif /* __rtems__ */ +#endif } if (pci_msi_device_blacklisted(dev)) @@ -830,13 +1627,23 @@ 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->secbus); - device_printf(dev, " subordinate bus %d\n", sc->subbus); + device_printf(dev, " secondary bus %d\n", sc->bus.sec); + device_printf(dev, " subordinate bus %d\n", sc->bus.sub); #ifdef NEW_PCIB if (pcib_is_window_open(&sc->io)) device_printf(dev, " I/O decode 0x%jx-0x%jx\n", @@ -877,20 +1684,6 @@ pcib_attach_common(device_t dev) } /* - * XXX If the secondary bus number is zero, we should assign a bus number - * since the BIOS hasn't, then initialise the bridge. A simple - * bus_alloc_resource with the a couple of busses seems like the right - * approach, but we don't know what busses the BIOS might have already - * assigned to other bridges on this bus that probe later than we do. - * - * If the subordinate bus number is less than the secondary bus number, - * we should pick a better value. One sensible alternative would be to - * pick 255; the only tradeoff here is that configuration transactions - * would be more widely routed than absolutely necessary. We could - * then do a walk of the tree later and fix it. - */ - - /* * Always enable busmastering on bridges so that transactions * initiated on the secondary bus are passed through to the * primary bus. @@ -898,66 +1691,138 @@ 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) +{ + struct pcib_softc *sc; + + sc = device_get_softc(dev); + if (sc->bus.sec == 0) { + /* no secondary bus; we should have fixed this */ + 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)); +} + int pcib_attach(device_t dev) { - struct pcib_softc *sc; - device_t child; pcib_attach_common(dev); - sc = device_get_softc(dev); - if (sc->secbus != 0) { - child = device_add_child(dev, "pci", sc->secbus); - if (child != NULL) - return(bus_generic_attach(dev)); - } + return (pcib_attach_child(dev)); +} - /* no secondary bus; we should have fixed this */ - return(0); +int +pcib_detach(device_t dev) +{ +#if defined(PCI_HP) || defined(NEW_PCIB) + struct pcib_softc *sc; +#endif + int error; + +#if defined(PCI_HP) || defined(NEW_PCIB) + sc = device_get_softc(dev); +#endif + error = bus_generic_detach(dev); + if (error) + return (error); +#ifdef PCI_HP + if (sc->flags & PCIB_HOTPLUG) { + error = pcib_detach_hotplug(sc); + if (error) + return (error); + } +#endif + error = device_delete_children(dev); + if (error) + return (error); +#ifdef NEW_PCIB + pcib_free_windows(sc); +#ifdef PCI_RES_BUS + pcib_free_secbus(dev, &sc->bus); +#endif +#endif + return (0); } int pcib_suspend(device_t dev) { - device_t pcib; - int dstate, error; pcib_cfg_save(device_get_softc(dev)); - error = bus_generic_suspend(dev); - if (error == 0 && pci_do_power_suspend) { - dstate = PCI_POWERSTATE_D3; - pcib = device_get_parent(device_get_parent(dev)); - if (PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0) - pci_set_powerstate(dev, dstate); - } - return (error); + return (bus_generic_suspend(dev)); } int pcib_resume(device_t dev) { - device_t pcib; - if (pci_do_power_resume) { - pcib = device_get_parent(device_get_parent(dev)); - if (PCIB_POWER_FOR_SLEEP(pcib, dev, NULL) == 0) - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - } pcib_cfg_restore(device_get_softc(dev)); return (bus_generic_resume(dev)); } +void +pcib_bridge_init(device_t dev) +{ + pci_write_config(dev, PCIR_IOBASEL_1, 0xff, 1); + pci_write_config(dev, PCIR_IOBASEH_1, 0xffff, 2); + pci_write_config(dev, PCIR_IOLIMITL_1, 0, 1); + pci_write_config(dev, PCIR_IOLIMITH_1, 0, 2); + pci_write_config(dev, PCIR_MEMBASE_1, 0xffff, 2); + pci_write_config(dev, PCIR_MEMLIMIT_1, 0, 2); + pci_write_config(dev, PCIR_PMBASEL_1, 0xffff, 2); + pci_write_config(dev, PCIR_PMBASEH_1, 0xffffffff, 4); + pci_write_config(dev, PCIR_PMLIMITL_1, 0, 2); + pci_write_config(dev, PCIR_PMLIMITH_1, 0, 4); +} + +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); - + switch (which) { case PCIB_IVAR_DOMAIN: *result = sc->domain; return(0); case PCIB_IVAR_BUS: - *result = sc->secbus; + *result = sc->bus.sec; return(0); } return(ENOENT); @@ -966,14 +1831,12 @@ 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 pcib_softc *sc = device_get_softc(dev); switch (which) { case PCIB_IVAR_DOMAIN: return(EINVAL); case PCIB_IVAR_BUS: - sc->secbus = value; - return(0); + return(EINVAL); } return(ENOENT); } @@ -985,8 +1848,8 @@ pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) */ static struct resource * pcib_suballoc_resource(struct pcib_softc *sc, struct pcib_window *w, - device_t child, int type, int *rid, u_long start, u_long end, u_long count, - u_int flags) + device_t child, int type, int *rid, rman_res_t start, rman_res_t end, + rman_res_t count, u_int flags) { struct resource *res; @@ -1000,7 +1863,7 @@ pcib_suballoc_resource(struct pcib_softc *sc, struct pcib_window *w, if (bootverbose) device_printf(sc->dev, - "allocated %s range (%#lx-%#lx) for rid %x of %s\n", + "allocated %s range (%#jx-%#jx) for rid %x of %s\n", w->name, rman_get_start(res), rman_get_end(res), *rid, pcib_child_name(child)); rman_set_rid(res, *rid); @@ -1023,10 +1886,10 @@ pcib_suballoc_resource(struct pcib_softc *sc, struct pcib_window *w, /* Allocate a fresh resource range for an unconfigured window. */ static int pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct resource *res; - u_long base, limit, wmask; + rman_res_t base, limit, wmask; int rid; /* @@ -1070,17 +1933,17 @@ pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type, return (0); } } - return (ENOSPC); + return (ENOSPC); } - - wmask = (1ul << w->step) - 1; + + wmask = ((rman_res_t)1 << w->step) - 1; if (RF_ALIGNMENT(flags) < w->step) { flags &= ~RF_ALIGNMENT_MASK; flags |= RF_ALIGNMENT_LOG2(w->step); } start &= ~wmask; end |= wmask; - count = roundup2(count, 1ul << w->step); + count = roundup2(count, (rman_res_t)1 << w->step); rid = w->reg; res = bus_alloc_resource(sc->dev, type, &rid, start, end, count, flags & ~RF_ACTIVE); @@ -1096,7 +1959,7 @@ pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type, /* Try to expand an existing window to the requested base and limit. */ static int pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type, - u_long base, u_long limit) + rman_res_t base, rman_res_t limit) { struct resource *res; int error, i, force_64k_base; @@ -1164,7 +2027,7 @@ pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type, KASSERT(w->base == rman_get_start(res), ("existing resource mismatch")); force_64k_base = 0; - } + } error = bus_adjust_resource(sc->dev, type, res, force_64k_base ? rman_get_start(res) : base, limit); @@ -1194,9 +2057,9 @@ pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ static int pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { - u_long align, start_free, end_free, front, back, wmask; + rman_res_t align, start_free, end_free, front, back, wmask; int error; /* @@ -1215,7 +2078,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, end = w->rman.rm_end; if (start + count - 1 > end || start + count < start) return (EINVAL); - wmask = (1ul << w->step) - 1; + wmask = ((rman_res_t)1 << w->step) - 1; /* * If there is no resource at all, just try to allocate enough @@ -1227,7 +2090,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, if (error) { if (bootverbose) device_printf(sc->dev, - "failed to allocate initial %s window (%#lx-%#lx,%#lx)\n", + "failed to allocate initial %s window (%#jx-%#jx,%#jx)\n", w->name, start, end, count); return (error); } @@ -1259,9 +2122,9 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (bootverbose) device_printf(sc->dev, - "attempting to grow %s window for (%#lx-%#lx,%#lx)\n", + "attempting to grow %s window for (%#jx-%#jx,%#jx)\n", w->name, start, end, count); - align = 1ul << RF_ALIGNMENT(flags); + align = (rman_res_t)1 << RF_ALIGNMENT(flags); if (start < w->base) { if (rman_first_free_region(&w->rman, &start_free, &end_free) != 0 || start_free != w->base) @@ -1283,7 +2146,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (front >= start && front <= end_free) { if (bootverbose) - printf("\tfront candidate range: %#lx-%#lx\n", + printf("\tfront candidate range: %#jx-%#jx\n", front, end_free); front &= ~wmask; front = w->base - front; @@ -1311,7 +2174,7 @@ pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type, */ if (back <= end && start_free <= back) { if (bootverbose) - printf("\tback candidate range: %#lx-%#lx\n", + printf("\tback candidate range: %#jx-%#jx\n", start_free, back); back |= wmask; back -= w->limit; @@ -1361,7 +2224,7 @@ updatewin: */ struct resource * pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pcib_softc *sc; struct resource *r; @@ -1383,6 +2246,11 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, } switch (type) { +#ifdef PCI_RES_BUS + case PCI_RES_BUS: + return (pcib_alloc_subbus(&sc->bus, child, rid, start, end, + count, flags)); +#endif case SYS_RES_IOPORT: if (pcib_is_isa_range(sc, start, end, count)) return (NULL); @@ -1445,7 +2313,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, int pcib_adjust_resource(device_t bus, device_t child, int type, struct resource *r, - u_long start, u_long end) + rman_res_t start, rman_res_t end) { struct pcib_softc *sc; @@ -1479,8 +2347,8 @@ pcib_release_resource(device_t dev, device_t child, int type, int rid, * is set up to, or capable of handling them. */ struct resource * -pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, - u_long start, u_long end, u_long count, u_int flags) +pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) { struct pcib_softc *sc = device_get_softc(dev); const char *name, *suffix; @@ -1530,7 +2398,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif } if (end < start) { - device_printf(dev, "ioport: end (%lx) < start (%lx)\n", + device_printf(dev, "ioport: end (%jx) < start (%jx)\n", end, start); start = 0; end = 0; @@ -1538,13 +2406,13 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, } if (!ok) { device_printf(dev, "%s%srequested unsupported I/O " - "range 0x%lx-0x%lx (decoding 0x%x-0x%x)\n", + "range 0x%jx-0x%jx (decoding 0x%x-0x%x)\n", name, suffix, start, end, sc->iobase, sc->iolimit); return (NULL); } if (bootverbose) device_printf(dev, - "%s%srequested I/O range 0x%lx-0x%lx: in range\n", + "%s%srequested I/O range 0x%jx-0x%jx: in range\n", name, suffix, start, end); break; @@ -1599,7 +2467,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif } if (end < start) { - device_printf(dev, "memory: end (%lx) < start (%lx)\n", + device_printf(dev, "memory: end (%jx) < start (%jx)\n", end, start); start = 0; end = 0; @@ -1607,7 +2475,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, } if (!ok && bootverbose) device_printf(dev, - "%s%srequested unsupported memory range %#lx-%#lx " + "%s%srequested unsupported memory range %#jx-%#jx " "(decoding %#jx-%#jx, %#jx-%#jx)\n", name, suffix, start, end, (uintmax_t)sc->membase, (uintmax_t)sc->memlimit, @@ -1616,7 +2484,7 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, return (NULL); if (bootverbose) device_printf(dev,"%s%srequested memory range " - "0x%lx-0x%lx: good\n", + "0x%jx-0x%jx: good\n", name, suffix, start, end); break; @@ -1632,27 +2500,132 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif /* + * If ARI is enabled on this downstream port, translate the function number + * to the non-ARI slot/function. The downstream port will convert it back in + * hardware. If ARI is not enabled slot and func are not modified. + */ +static __inline void +pcib_xlate_ari(device_t pcib, int bus, int *slot, int *func) +{ + struct pcib_softc *sc; + int ari_func; + + sc = device_get_softc(pcib); + ari_func = *func; + + if (sc->flags & PCIB_ENABLE_ARI) { + KASSERT(*slot == 0, + ("Non-zero slot number with ARI enabled!")); + *slot = PCIE_ARI_SLOT(ari_func); + *func = PCIE_ARI_FUNC(ari_func); + } +} + + +static void +pcib_enable_ari(struct pcib_softc *sc, uint32_t pcie_pos) +{ + uint32_t ctl2; + + ctl2 = pci_read_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, 4); + ctl2 |= PCIEM_CTL2_ARI; + pci_write_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, ctl2, 4); + + sc->flags |= PCIB_ENABLE_ARI; +} + +/* * PCIB interface. */ int pcib_maxslots(device_t dev) { - return(PCI_SLOTMAX); + return (PCI_SLOTMAX); +} + +static int +pcib_ari_maxslots(device_t dev) +{ + struct pcib_softc *sc; + + sc = device_get_softc(dev); + + if (sc->flags & PCIB_ENABLE_ARI) + return (PCIE_ARI_SLOTMAX); + else + return (PCI_SLOTMAX); +} + +static int +pcib_ari_maxfuncs(device_t dev) +{ + struct pcib_softc *sc; + + sc = device_get_softc(dev); + + if (sc->flags & PCIB_ENABLE_ARI) + return (PCIE_ARI_FUNCMAX); + else + return (PCI_FUNCMAX); +} + +static void +pcib_ari_decode_rid(device_t pcib, uint16_t rid, int *bus, int *slot, + int *func) +{ + struct pcib_softc *sc; + + sc = device_get_softc(pcib); + + *bus = PCI_RID2BUS(rid); + if (sc->flags & PCIB_ENABLE_ARI) { + *slot = PCIE_ARI_RID2SLOT(rid); + *func = PCIE_ARI_RID2FUNC(rid); + } else { + *slot = PCI_RID2SLOT(rid); + *func = PCI_RID2FUNC(rid); + } } /* * Since we are a child of a PCI bus, its parent must support the pcib interface. */ -uint32_t +static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width) { - return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, 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)); } -void +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) { - PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, val, 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); } /* @@ -1665,7 +2638,7 @@ pcib_route_interrupt(device_t pcib, device_t dev, int pin) int parent_intpin; int intnum; - /* + /* * * The PCI standard defines a swizzle of the child-side device/intpin to * the parent-side intpin as follows. @@ -1764,3 +2737,101 @@ pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate) bus = device_get_parent(pcib); return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate)); } + +static int +pcib_ari_enabled(device_t pcib) +{ + struct pcib_softc *sc; + + sc = device_get_softc(pcib); + + return ((sc->flags & PCIB_ENABLE_ARI) != 0); +} + +static int +pcib_ari_get_id(device_t pcib, device_t dev, enum pci_id_type type, + uintptr_t *id) +{ + struct pcib_softc *sc; + device_t bus_dev; + uint8_t bus, slot, func; + + if (type != PCI_ID_RID) { + bus_dev = device_get_parent(pcib); + return (PCIB_GET_ID(device_get_parent(bus_dev), dev, type, id)); + } + + sc = device_get_softc(pcib); + + if (sc->flags & PCIB_ENABLE_ARI) { + bus = pci_get_bus(dev); + func = pci_get_function(dev); + + *id = (PCI_ARI_RID(bus, func)); + } else { + bus = pci_get_bus(dev); + slot = pci_get_slot(dev); + func = pci_get_function(dev); + + *id = (PCI_RID(bus, slot, func)); + } + + return (0); +} + +/* + * Check that the downstream port (pcib) and the endpoint device (dev) both + * support ARI. If so, enable it and return 0, otherwise return an error. + */ +static int +pcib_try_enable_ari(device_t pcib, device_t dev) +{ + struct pcib_softc *sc; + int error; + uint32_t cap2; + int ari_cap_off; + uint32_t ari_ver; + uint32_t pcie_pos; + + sc = device_get_softc(pcib); + + /* + * ARI is controlled in a register in the PCIe capability structure. + * If the downstream port does not have the PCIe capability structure + * then it does not support ARI. + */ + error = pci_find_cap(pcib, PCIY_EXPRESS, &pcie_pos); + if (error != 0) + return (ENODEV); + + /* Check that the PCIe port advertises ARI support. */ + cap2 = pci_read_config(pcib, pcie_pos + PCIER_DEVICE_CAP2, 4); + if (!(cap2 & PCIEM_CAP2_ARI)) + return (ENODEV); + + /* + * Check that the endpoint device advertises ARI support via the ARI + * extended capability structure. + */ + error = pci_find_extcap(dev, PCIZ_ARI, &ari_cap_off); + if (error != 0) + return (ENODEV); + + /* + * Finally, check that the endpoint device supports the same version + * of ARI that we do. + */ + ari_ver = pci_read_config(dev, ari_cap_off, 4); + if (PCI_EXTCAP_VER(ari_ver) != PCIB_SUPPORTED_ARI_VER) { + if (bootverbose) + device_printf(pcib, + "Unsupported version of ARI (%d) detected\n", + PCI_EXTCAP_VER(ari_ver)); + + return (ENXIO); + } + + pcib_enable_ari(sc, pcie_pos); + + return (0); +} |