Author: rstone
Date: Tue Apr  1 16:02:02 2014
New Revision: 264011
URL: http://svnweb.freebsd.org/changeset/base/264011

Log:
  Add support for PCIe ARI
  
  PCIe Alternate RID Interpretation (ARI) is an optional feature that
  allows devices to have up to 256 different functions.  It is
  implemented by always setting the PCI slot number to 0 and
  re-purposing the 5 bits used to encode the slot number to instead
  contain the function number.  Combined with the original 3 bits
  allocated for the function number, this allows for 256 functions.
  
  This is enabled by default, but it's expected to be a no-op on currently
  supported hardware.  It's a prerequisite for supporting PCI SR-IOV, and
  I want the ARI support to go in early to help shake out any bugs in it.
  ARI can be disabled by setting the tunable hw.pci.enable_ari=0.
  
  Reviewed by:  kib
  MFC after:    2 months
  Sponsored by: Sandvine Inc.

Modified:
  head/sys/dev/pci/pci.c
  head/sys/dev/pci/pci_pci.c
  head/sys/dev/pci/pcib_if.m
  head/sys/dev/pci/pcib_private.h
  head/sys/dev/pci/pcib_support.c
  head/sys/dev/pci/pcireg.h

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c      Tue Apr  1 15:56:31 2014        (r264010)
+++ head/sys/dev/pci/pci.c      Tue Apr  1 16:02:02 2014        (r264011)
@@ -354,6 +354,11 @@ SYSCTL_INT(_hw_pci, OID_AUTO, clear_buse
     "Ignore firmware-assigned bus numbers.");
 #endif
 
+static int pci_enable_ari = 1;
+TUNABLE_INT("hw.pci.enable_ari", &pci_enable_ari);
+SYSCTL_INT(_hw_pci, OID_AUTO, enable_ari, CTLFLAG_RDTUN, &pci_enable_ari,
+    0, "Enable support for PCIe Alternative RID Interpretation");
+
 static int
 pci_has_quirk(uint32_t devid, int quirk)
 {
@@ -3454,6 +3459,19 @@ pci_add_resources(device_t bus, device_t
 #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)
 {
@@ -3463,6 +3481,24 @@ pci_add_children(device_t dev, int domai
        int maxslots;
        int s, f, pcifunchigh;
        uint8_t hdrtype;
+       int first_func;
+
+       /*
+        * 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);
+
+       /*
+        * Start looking for new devices on slot 0 at function 1 because we
+        * just identified the device at slot 0, function 0.
+        */
+       first_func = 1;
 
        KASSERT(dinfo_size >= sizeof(struct pci_devinfo),
            ("dinfo_size too small"));
@@ -3475,14 +3511,13 @@ pci_add_children(device_t dev, int domai
                if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
                        continue;
                if (hdrtype & PCIM_MFDEV)
-                       pcifunchigh = PCI_FUNCMAX;
-               for (f = 0; f <= pcifunchigh; f++) {
-                       dinfo = pci_read_device(pcib, domain, busno, s, f,
+                       pcifunchigh = PCIB_MAXFUNCS(pcib);
+               for (f = first_func; f <= pcifunchigh; f++)
+                       pci_identify_function(pcib, dev, domain, busno, s, f,
                            dinfo_size);
-                       if (dinfo != NULL) {
-                               pci_add_child(dev, dinfo);
-                       }
-               }
+
+               /* For slots after slot 0 we need to check for function 0. */
+               first_func = 0;
        }
 #undef REG
 }

Modified: head/sys/dev/pci/pci_pci.c
==============================================================================
--- head/sys/dev/pci/pci_pci.c  Tue Apr  1 15:56:31 2014        (r264010)
+++ head/sys/dev/pci/pci_pci.c  Tue Apr  1 16:02:02 2014        (r264011)
@@ -56,6 +56,14 @@ 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 uint16_t                pcib_ari_get_rid(device_t pcib, device_t dev);
+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 device_method_t pcib_methods[] = {
     /* Device interface */
@@ -83,7 +91,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),
@@ -93,7 +102,8 @@ 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_rid,            pcib_get_rid),
+    DEVMETHOD(pcib_get_rid,            pcib_ari_get_rid),
+    DEVMETHOD(pcib_try_enable_ari,     pcib_try_enable_ari),
 
     DEVMETHOD_END
 };
@@ -1803,27 +1813,94 @@ pcib_alloc_resource(device_t dev, device
 #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);
 }
 
 /*
  * 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));
+
+       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);
+
+       pcib_xlate_ari(dev, b, &s, &f);
+       PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f,
+           reg, val, width);
 }
 
 /*
@@ -1936,3 +2013,82 @@ pcib_power_for_sleep(device_t pcib, devi
        return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate));
 }
 
+static uint16_t
+pcib_ari_get_rid(device_t pcib, device_t dev)
+{
+       struct pcib_softc *sc;
+       uint8_t bus, slot, func;
+
+       sc = device_get_softc(pcib);
+
+       if (sc->flags & PCIB_ENABLE_ARI) {
+               bus = pci_get_bus(dev);
+               func = pci_get_function(dev);
+
+               return (PCI_ARI_RID(bus, func));
+       } else {
+               bus = pci_get_bus(dev);
+               slot = pci_get_slot(dev);
+               func = pci_get_function(dev);
+
+               return (PCI_RID(bus, slot, func));
+       }
+}
+
+/*
+ * 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);
+}
+

Modified: head/sys/dev/pci/pcib_if.m
==============================================================================
--- head/sys/dev/pci/pcib_if.m  Tue Apr  1 15:56:31 2014        (r264010)
+++ head/sys/dev/pci/pcib_if.m  Tue Apr  1 16:02:02 2014        (r264011)
@@ -27,7 +27,9 @@
 #
 
 #include <sys/bus.h>
+#include <sys/rman.h>
 #include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
 
 INTERFACE pcib;
 
@@ -47,6 +49,14 @@ METHOD int maxslots {
 };
 
 #
+#
+# Return the number of functions on the attached PCI bus.
+#
+METHOD int maxfuncs {
+       device_t        dev;
+} DEFAULT pcib_maxfuncs;
+
+#
 # Read configuration space on the PCI bus. The bus, slot and func
 # arguments determine the device which is being read and the reg
 # argument is a byte offset into configuration space for that
@@ -161,5 +171,14 @@ METHOD int power_for_sleep {
 METHOD uint16_t get_rid {
        device_t pcib;
        device_t dev;
+} DEFAULT pcib_get_rid;
+
+#
+# Enable Alternative RID Interpretation if both the downstream port (pcib)
+# and the endpoint device (dev) both support it.
+#
+METHOD int try_enable_ari {
+       device_t        pcib;
+       device_t        dev;
 };
 

Modified: head/sys/dev/pci/pcib_private.h
==============================================================================
--- head/sys/dev/pci/pcib_private.h     Tue Apr  1 15:56:31 2014        
(r264010)
+++ head/sys/dev/pci/pcib_private.h     Tue Apr  1 16:02:02 2014        
(r264011)
@@ -105,6 +105,7 @@ struct pcib_softc 
 #define        PCIB_SUBTRACTIVE        0x1
 #define        PCIB_DISABLE_MSI        0x2
 #define        PCIB_DISABLE_MSIX       0x4
+#define        PCIB_ENABLE_ARI         0x8
     uint16_t   command;        /* command register */
     u_int      domain;         /* domain number */
     u_int      pribus;         /* primary bus number */
@@ -126,6 +127,8 @@ struct pcib_softc 
     uint8_t    seclat;         /* secondary bus latency timer */
 };
 
+#define        PCIB_SUPPORTED_ARI_VER  1
+
 typedef uint32_t pci_read_config_fn(int b, int s, int f, int reg, int width);
 
 int            host_pcib_get_busno(pci_read_config_fn read_config, int bus,
@@ -159,8 +162,7 @@ int         pcib_release_resource(device_t dev,
     struct resource *r);
 #endif
 int            pcib_maxslots(device_t dev);
-uint32_t       pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int 
reg, int width);
-void           pcib_write_config(device_t dev, u_int b, u_int s, u_int f, 
u_int reg, uint32_t val, int width);
+int            pcib_maxfuncs(device_t dev);
 int            pcib_route_interrupt(device_t pcib, device_t dev, int pin);
 int            pcib_alloc_msi(device_t pcib, device_t dev, int count, int 
maxcount, int *irqs);
 int            pcib_release_msi(device_t pcib, device_t dev, int count, int 
*irqs);
@@ -169,5 +171,4 @@ int         pcib_release_msix(device_t pcib, de
 int            pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t 
*addr, uint32_t *data);
 uint16_t       pcib_get_rid(device_t pcib, device_t dev);
 
-
 #endif

Modified: head/sys/dev/pci/pcib_support.c
==============================================================================
--- head/sys/dev/pci/pcib_support.c     Tue Apr  1 15:56:31 2014        
(r264010)
+++ head/sys/dev/pci/pcib_support.c     Tue Apr  1 16:02:02 2014        
(r264011)
@@ -48,6 +48,12 @@ __FBSDID("$FreeBSD$");
 
 #include "pcib_if.h"
 
+int
+pcib_maxfuncs(device_t dev)
+{
+       return (PCI_FUNCMAX);
+}
+
 uint16_t
 pcib_get_rid(device_t pcib, device_t dev)
 {

Modified: head/sys/dev/pci/pcireg.h
==============================================================================
--- head/sys/dev/pci/pcireg.h   Tue Apr  1 15:56:31 2014        (r264010)
+++ head/sys/dev/pci/pcireg.h   Tue Apr  1 16:02:02 2014        (r264011)
@@ -48,6 +48,9 @@
 #define        PCIE_REGMAX     4095    /* highest supported config register 
addr. */
 #define        PCI_MAXHDRTYPE  2
 
+#define        PCIE_ARI_SLOTMAX 0
+#define        PCIE_ARI_FUNCMAX 255
+
 #define        PCI_RID_BUS_SHIFT       8
 #define        PCI_RID_SLOT_SHIFT      3
 #define        PCI_RID_FUNC_SHIFT      0
@@ -65,6 +68,9 @@
 #define PCI_RID2SLOT(rid) (((rid) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX)
 #define PCI_RID2FUNC(rid) (((rid) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX)
 
+#define PCIE_ARI_SLOT(func) (((func) >> PCI_RID_SLOT_SHIFT) & PCI_SLOTMAX)
+#define PCIE_ARI_FUNC(func) (((func) >> PCI_RID_FUNC_SHIFT) & PCI_FUNCMAX)
+
 /* PCI config header registers for all devices */
 
 #define        PCIR_DEVVENDOR  0x00
@@ -791,6 +797,7 @@
 #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        PCIER_DEVICE_CTL2       0x28
 #define        PCIEM_CTL2_COMP_TIMEOUT_VAL     0x000f
 #define        PCIEM_CTL2_COMP_TIMEOUT_DIS     0x0010
@@ -911,3 +918,4 @@
 /* Serial Number definitions */
 #define        PCIR_SERIAL_LOW         0x04
 #define        PCIR_SERIAL_HIGH        0x08
+
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to