diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-12-09 14:19:03 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-01-10 09:53:34 +0100 |
commit | 75b706fde4cbf82bcd41a1cec319778aa0f8eb2d (patch) | |
tree | ea39a351a1f6337b5a5dd6036314693adef5ffe6 /freebsd/sys/dev/pci | |
parent | VMSTAT(8): Port to RTEMS (diff) | |
download | rtems-libbsd-75b706fde4cbf82bcd41a1cec319778aa0f8eb2d.tar.bz2 |
Update to FreeBSD head 2016-12-10
Git mirror commit 80c55f08a05ab3b26a73b226ccb56adc3122a55c.
Diffstat (limited to 'freebsd/sys/dev/pci')
-rw-r--r-- | freebsd/sys/dev/pci/pci.c | 180 | ||||
-rw-r--r-- | freebsd/sys/dev/pci/pcireg.h | 21 | ||||
-rw-r--r-- | freebsd/sys/dev/pci/pcivar.h | 13 |
3 files changed, 209 insertions, 5 deletions
diff --git a/freebsd/sys/dev/pci/pci.c b/freebsd/sys/dev/pci/pci.c index 789825dc..211562f0 100644 --- a/freebsd/sys/dev/pci/pci.c +++ b/freebsd/sys/dev/pci/pci.c @@ -3981,7 +3981,7 @@ pci_rescan_method(device_t dev) if (hdrtype & PCIM_MFDEV) pcifunchigh = PCIB_MAXFUNCS(pcib); for (f = 0; f <= pcifunchigh; f++) { - if (REG(PCIR_VENDOR, 2) == 0xfff) + if (REG(PCIR_VENDOR, 2) == 0xffff) continue; /* @@ -4081,6 +4081,7 @@ pci_add_child(device_t bus, struct pci_devinfo *dinfo) pci_print_verbose(dinfo); pci_add_resources(bus, dinfo->cfg.dev, 0, 0); pci_child_added(dinfo->cfg.dev); + EVENTHANDLER_INVOKE(pci_add_device, dinfo->cfg.dev); } void @@ -4617,6 +4618,9 @@ static const struct {PCIC_CRYPTO, PCIS_CRYPTO_ENTERTAIN, 1, "entertainment crypto"}, {PCIC_DASP, -1, 0, "dasp"}, {PCIC_DASP, PCIS_DASP_DPIO, 1, "DPIO module"}, + {PCIC_DASP, PCIS_DASP_PERFCNTRS, 1, "performance counters"}, + {PCIC_DASP, PCIS_DASP_COMM_SYNC, 1, "communication synchronizer"}, + {PCIC_DASP, PCIS_DASP_MGMT_CARD, 1, "signal processing management"}, {0, 0, 0, NULL} }; @@ -5009,6 +5013,7 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, struct resource_list *rl = &dinfo->resources; struct resource *res; struct pci_map *pm; + uint16_t cmd; pci_addr_t map, testval; int mapsize; @@ -5107,8 +5112,17 @@ pci_reserve_map(device_t dev, device_t child, int type, int *rid, device_printf(child, "Lazy allocation of %#jx bytes rid %#x type %d at %#jx\n", count, *rid, type, rman_get_start(res)); + + /* Disable decoding via the CMD register before updating the BAR */ + cmd = pci_read_config(child, PCIR_COMMAND, 2); + pci_write_config(child, PCIR_COMMAND, + cmd & ~(PCI_BAR_MEM(map) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 2); + map = rman_get_start(res); pci_write_bar(child, pm, map); + + /* Restore the original value of the CMD register */ + pci_write_config(child, PCIR_COMMAND, cmd, 2); out: return (res); } @@ -5331,6 +5345,8 @@ pci_child_deleted(device_t dev, device_t child) dinfo = device_get_ivars(child); rl = &dinfo->resources; + EVENTHANDLER_INVOKE(pci_delete_device, child); + /* Turn off access to resources we're about to free */ if (bus_child_present(child) != 0) { pci_write_config(child, PCIR_COMMAND, pci_read_config(child, @@ -5908,3 +5924,165 @@ pci_find_pcie_root_port(device_t dev) dev = pcib; } } + +/* + * Wait for pending transactions to complete on a PCI-express function. + * + * The maximum delay is specified in milliseconds in max_delay. Note + * that this function may sleep. + * + * Returns true if the function is idle and false if the timeout is + * exceeded. If dev is not a PCI-express function, this returns true. + */ +bool +pcie_wait_for_pending_transactions(device_t dev, u_int max_delay) +{ + struct pci_devinfo *dinfo = device_get_ivars(dev); + uint16_t sta; + int cap; + + cap = dinfo->cfg.pcie.pcie_location; + if (cap == 0) + return (true); + + sta = pci_read_config(dev, cap + PCIER_DEVICE_STA, 2); + while (sta & PCIEM_STA_TRANSACTION_PND) { + if (max_delay == 0) + return (false); + + /* Poll once every 100 milliseconds up to the timeout. */ + if (max_delay > 100) { + pause_sbt("pcietp", 100 * SBT_1MS, 0, C_HARDCLOCK); + max_delay -= 100; + } else { + pause_sbt("pcietp", max_delay * SBT_1MS, 0, + C_HARDCLOCK); + max_delay = 0; + } + sta = pci_read_config(dev, cap + PCIER_DEVICE_STA, 2); + } + + return (true); +} + +/* + * Determine the maximum Completion Timeout in microseconds. + * + * For non-PCI-express functions this returns 0. + */ +int +pcie_get_max_completion_timeout(device_t dev) +{ + struct pci_devinfo *dinfo = device_get_ivars(dev); + int cap; + + cap = dinfo->cfg.pcie.pcie_location; + if (cap == 0) + return (0); + + /* + * Functions using the 1.x spec use the default timeout range of + * 50 microseconds to 50 milliseconds. Functions that do not + * support programmable timeouts also use this range. + */ + if ((dinfo->cfg.pcie.pcie_flags & PCIEM_FLAGS_VERSION) < 2 || + (pci_read_config(dev, cap + PCIER_DEVICE_CAP2, 4) & + PCIEM_CAP2_COMP_TIMO_RANGES) == 0) + return (50 * 1000); + + switch (pci_read_config(dev, cap + PCIER_DEVICE_CTL2, 2) & + PCIEM_CTL2_COMP_TIMO_VAL) { + case PCIEM_CTL2_COMP_TIMO_100US: + return (100); + case PCIEM_CTL2_COMP_TIMO_10MS: + return (10 * 1000); + case PCIEM_CTL2_COMP_TIMO_55MS: + return (55 * 1000); + case PCIEM_CTL2_COMP_TIMO_210MS: + return (210 * 1000); + case PCIEM_CTL2_COMP_TIMO_900MS: + return (900 * 1000); + case PCIEM_CTL2_COMP_TIMO_3500MS: + return (3500 * 1000); + case PCIEM_CTL2_COMP_TIMO_13S: + return (13 * 1000 * 1000); + case PCIEM_CTL2_COMP_TIMO_64S: + return (64 * 1000 * 1000); + default: + return (50 * 1000); + } +} + +/* + * Perform a Function Level Reset (FLR) on a device. + * + * This function first waits for any pending transactions to complete + * within the timeout specified by max_delay. If transactions are + * still pending, the function will return false without attempting a + * reset. + * + * If dev is not a PCI-express function or does not support FLR, this + * function returns false. + * + * Note that no registers are saved or restored. The caller is + * responsible for saving and restoring any registers including + * PCI-standard registers via pci_save_state() and + * pci_restore_state(). + */ +bool +pcie_flr(device_t dev, u_int max_delay, bool force) +{ + struct pci_devinfo *dinfo = device_get_ivars(dev); + uint16_t cmd, ctl; + int compl_delay; + int cap; + + cap = dinfo->cfg.pcie.pcie_location; + if (cap == 0) + return (false); + + if (!(pci_read_config(dev, cap + PCIER_DEVICE_CAP, 4) & PCIEM_CAP_FLR)) + return (false); + + /* + * Disable busmastering to prevent generation of new + * transactions while waiting for the device to go idle. If + * the idle timeout fails, the command register is restored + * which will re-enable busmastering. + */ + cmd = pci_read_config(dev, PCIR_COMMAND, 2); + pci_write_config(dev, PCIR_COMMAND, cmd & ~(PCIM_CMD_BUSMASTEREN), 2); + if (!pcie_wait_for_pending_transactions(dev, max_delay)) { + if (!force) { + pci_write_config(dev, PCIR_COMMAND, cmd, 2); + return (false); + } + pci_printf(&dinfo->cfg, + "Resetting with transactions pending after %d ms\n", + max_delay); + + /* + * Extend the post-FLR delay to cover the maximum + * Completion Timeout delay of anything in flight + * during the FLR delay. Enforce a minimum delay of + * at least 10ms. + */ + compl_delay = pcie_get_max_completion_timeout(dev) / 1000; + if (compl_delay < 10) + compl_delay = 10; + } else + compl_delay = 0; + + /* Initiate the reset. */ + ctl = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2); + pci_write_config(dev, cap + PCIER_DEVICE_CTL, ctl | + PCIEM_CTL_INITIATE_FLR, 2); + + /* Wait for 100ms. */ + pause_sbt("pcieflr", (100 + compl_delay) * SBT_1MS, 0, C_HARDCLOCK); + + if (pci_read_config(dev, cap + PCIER_DEVICE_STA, 2) & + PCIEM_STA_TRANSACTION_PND) + pci_printf(&dinfo->cfg, "Transactions pending after FLR!\n"); + return (true); +} diff --git a/freebsd/sys/dev/pci/pcireg.h b/freebsd/sys/dev/pci/pcireg.h index d463b7a5..291bb2ea 100644 --- a/freebsd/sys/dev/pci/pcireg.h +++ b/freebsd/sys/dev/pci/pcireg.h @@ -885,10 +885,25 @@ #define PCIEM_ROOT_STA_PME_STATUS 0x00010000 #define PCIEM_ROOT_STA_PME_PEND 0x00020000 #define PCIER_DEVICE_CAP2 0x24 -#define PCIEM_CAP2_ARI 0x20 +#define PCIEM_CAP2_COMP_TIMO_RANGES 0x0000000f +#define PCIEM_CAP2_COMP_TIMO_RANGE_A 0x00000001 +#define PCIEM_CAP2_COMP_TIMO_RANGE_B 0x00000002 +#define PCIEM_CAP2_COMP_TIMO_RANGE_C 0x00000004 +#define PCIEM_CAP2_COMP_TIMO_RANGE_D 0x00000008 +#define PCIEM_CAP2_COMP_TIMO_DISABLE 0x00000010 +#define PCIEM_CAP2_ARI 0x00000020 #define PCIER_DEVICE_CTL2 0x28 -#define PCIEM_CTL2_COMP_TIMEOUT_VAL 0x000f -#define PCIEM_CTL2_COMP_TIMEOUT_DIS 0x0010 +#define PCIEM_CTL2_COMP_TIMO_VAL 0x000f +#define PCIEM_CTL2_COMP_TIMO_50MS 0x0000 +#define PCIEM_CTL2_COMP_TIMO_100US 0x0001 +#define PCIEM_CTL2_COMP_TIMO_10MS 0x0002 +#define PCIEM_CTL2_COMP_TIMO_55MS 0x0005 +#define PCIEM_CTL2_COMP_TIMO_210MS 0x0006 +#define PCIEM_CTL2_COMP_TIMO_900MS 0x0009 +#define PCIEM_CTL2_COMP_TIMO_3500MS 0x000a +#define PCIEM_CTL2_COMP_TIMO_13S 0x000d +#define PCIEM_CTL2_COMP_TIMO_64S 0x000e +#define PCIEM_CTL2_COMP_TIMO_DISABLE 0x0010 #define PCIEM_CTL2_ARI 0x0020 #define PCIEM_CTL2_ATOMIC_REQ_ENABLE 0x0040 #define PCIEM_CTL2_ATOMIC_EGR_BLOCK 0x0080 diff --git a/freebsd/sys/dev/pci/pcivar.h b/freebsd/sys/dev/pci/pcivar.h index fc3fa5fb..4652715a 100644 --- a/freebsd/sys/dev/pci/pcivar.h +++ b/freebsd/sys/dev/pci/pcivar.h @@ -31,6 +31,7 @@ #define _PCIVAR_H_ #include <sys/queue.h> +#include <sys/eventhandler.h> /* some PCI bus constants */ #define PCI_MAXMAPS_0 6 /* max. no. of memory/port maps */ @@ -594,7 +595,9 @@ uint32_t pcie_read_config(device_t dev, int reg, int width); void pcie_write_config(device_t dev, int reg, uint32_t value, int width); uint32_t pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value, int width); - +bool pcie_flr(device_t dev, u_int max_delay, bool force); +int pcie_get_max_completion_timeout(device_t dev); +bool pcie_wait_for_pending_transactions(device_t dev, u_int max_delay); #ifdef BUS_SPACE_MAXADDR #if (BUS_SPACE_MAXADDR > 0xFFFFFFFF) @@ -631,4 +634,12 @@ void * vga_pci_map_bios(device_t dev, size_t *size); void vga_pci_unmap_bios(device_t dev, void *bios); int vga_pci_repost(device_t dev); +/** + * Global eventhandlers invoked when PCI devices are added or removed + * from the system. + */ +typedef void (*pci_event_fn)(void *arg, device_t dev); +EVENTHANDLER_DECLARE(pci_add_device, pci_event_fn); +EVENTHANDLER_DECLARE(pci_delete_device, pci_event_fn); + #endif /* _PCIVAR_H_ */ |