summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/pci
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2016-12-09 14:19:03 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-01-10 09:53:34 +0100
commit75b706fde4cbf82bcd41a1cec319778aa0f8eb2d (patch)
treeea39a351a1f6337b5a5dd6036314693adef5ffe6 /freebsd/sys/dev/pci
parentVMSTAT(8): Port to RTEMS (diff)
downloadrtems-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.c180
-rw-r--r--freebsd/sys/dev/pci/pcireg.h21
-rw-r--r--freebsd/sys/dev/pci/pcivar.h13
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_ */