Author: landonf
Date: Tue Nov 28 00:12:14 2017
New Revision: 326297
URL: https://svnweb.freebsd.org/changeset/base/326297

Log:
  bhndb(4): Implement bridge support for the BCM4312 and other PCI_V0 chipsets.
  
  Very early (PCI_V0) Broadcom PCI Wi-Fi chipsets have a few quirks when
  compared to later PCI(e) core revisions:
  
  - The standard static BAR0 mapping of the PCI core registers is discontiguous,
    with siba's cfg0 register block mapped distinctly from the other core
    registers.
  - No dedicated ChipCommon register mapping is provided; instead, the
    single configurable register window must be used to access both
    ChipCommon and D11 core registers. The D11 core's operational semantics
    guarantee the safety of -- after disabling interrupts -- borrowing
    the single dynamic register window to perform the few ChipCommon
    operations required by a driver.
  
  To support these early PCI devices:
  
  - Allow defining multiple discontiguous BHNDB_REGWIN_T_CORE register
    windows that map a single port/region, and producing bridged resource
    allocations backed by those discontiguous windows.
  - Support stealing existing register window allocations to fulfill indirect
    bhnd(4) bus I/O requests within address ranges tagged with
    BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT.
  - Fix an inverted test of bhndb_is_pcie_attached() that disabled
    PCI-only clock bring-up required by these devices.
  
  Approved by:  adrian (mentor, implicit)
  Sponsored by: The FreeBSD Foundation

Modified:
  head/sys/dev/bhnd/bhndb/bhndb.c
  head/sys/dev/bhnd/bhndb/bhndb.h
  head/sys/dev/bhnd/bhndb/bhndb_hwdata.c
  head/sys/dev/bhnd/bhndb/bhndb_pci.c
  head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
  head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
  head/sys/dev/bhnd/bhndb/bhndb_private.h
  head/sys/dev/bhnd/bhndb/bhndb_subr.c
  head/sys/dev/bhnd/bhndb/bhndbvar.h

Modified: head/sys/dev/bhnd/bhndb/bhndb.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.c     Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb.c     Tue Nov 28 00:12:14 2017        
(r326297)
@@ -115,9 +115,9 @@ static int                   bhndb_try_activate_resource(
 
 static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc,
                                        bus_addr_t addr, bus_size_t size,
-                                       bus_size_t *offset);
+                                       bus_size_t *offset, bool *stolen,
+                                       bus_addr_t *restore);
 
-
 /**
  * Default bhndb(4) implementation of DEVICE_PROBE().
  * 
@@ -270,6 +270,9 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
                for (regw = br->cfg->register_windows;
                    regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
                {
+                       const struct bhndb_port_priority        *pp;
+                       uint32_t                                 alloc_flags;
+
                        /* Only core windows are supported */
                        if (regw->win_type != BHNDB_REGWIN_T_CORE)
                                continue;
@@ -295,6 +298,18 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
                        }
 
                        /*
+                        * Apply the register window's region offset, if any.
+                        */
+                       if (regw->d.core.offset > size) {
+                               device_printf(sc->dev, "invalid register "
+                                   "window offset %#jx for region %#jx+%#jx\n",
+                                   regw->d.core.offset, addr, size);
+                               return (EINVAL);
+                       }
+
+                       addr += regw->d.core.offset;
+
+                       /*
                         * Always defer to the register window's size.
                         * 
                         * If the port size is smaller than the window size,
@@ -307,14 +322,25 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
                         */
                        size = regw->win_size;
 
+                       /* Fetch allocation flags from the corresponding port
+                        * priority entry, if any */
+                       pp = bhndb_hw_priorty_find_port(table, core,
+                           regw->d.core.port_type, regw->d.core.port,
+                           regw->d.core.region);
+                       if (pp != NULL) {
+                               alloc_flags = pp->alloc_flags;
+                       } else {
+                               alloc_flags = 0;
+                       }
+
                        /*
                         * Add to the bus region list.
                         * 
-                        * The window priority for a statically mapped
-                        * region is always HIGH.
+                        * The window priority for a statically mapped region is
+                        * always HIGH.
                         */
                        error = bhndb_add_resource_region(br, addr, size,
-                           BHNDB_PRIORITY_HIGH, regw);
+                           BHNDB_PRIORITY_HIGH, 0, regw);
                        if (error)
                                return (error);
                }
@@ -325,7 +351,6 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
         * ports defined in the priority table
         */
        for (u_int i = 0; i < ncores; i++) {
-               struct bhndb_region     *region;
                struct bhnd_core_info   *core;
                struct bhnd_core_match   md;
 
@@ -369,13 +394,12 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
                        }
 
                        /* Skip ports with an existing static mapping */
-                       region = bhndb_find_resource_region(br, addr, size);
-                       if (region != NULL && region->static_regwin != NULL)
+                       if (bhndb_has_static_region_mapping(br, addr, size))
                                continue;
 
                        /* Define a dynamic region for this port */
                        error = bhndb_add_resource_region(br, addr, size,
-                           pp->priority, NULL);
+                           pp->priority, pp->alloc_flags, NULL);
                        if (error)
                                return (error);
 
@@ -416,22 +440,29 @@ bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_ero
                struct bhndb_region     *region;
                const char              *direct_msg, *type_msg;
                bhndb_priority_t         prio, prio_min;
+               uint32_t                 flags;
 
                prio_min = br->min_prio;
                device_printf(sc->dev, "min_prio: %d\n", prio_min);
 
                STAILQ_FOREACH(region, &br->bus_regions, link) {
                        prio = region->priority;
+                       flags = region->alloc_flags;
 
                        direct_msg = prio >= prio_min ? "direct" : "indirect";
                        type_msg = region->static_regwin ? "static" : "dynamic";
        
                        device_printf(sc->dev, "region 0x%llx+0x%llx priority "
-                           "%u %s/%s\n",
+                           "%u %s/%s",
                            (unsigned long long) region->addr, 
                            (unsigned long long) region->size,
                            region->priority,
                            direct_msg, type_msg);
+
+                       if (flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
+                               printf(" [overcommit]\n");
+                       else
+                               printf("\n");
                }
        }
 
@@ -1605,11 +1636,12 @@ bhndb_deactivate_bhnd_resource(device_t dev, device_t 
  * in-use region; the first matching region is returned.
  */
 static struct bhndb_dw_alloc *
-bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr,
-    bus_size_t size, bus_size_t *offset)
+bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t 
size,
+    bus_size_t *offset, bool *stolen, bus_addr_t *restore)
 {
        struct bhndb_resources  *br;
        struct bhndb_dw_alloc   *dwa;
+       struct bhndb_region     *region;
 
        BHNDB_LOCK_ASSERT(sc, MA_OWNED);
 
@@ -1638,10 +1670,25 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
                *offset = dwa->win->win_offset;
                *offset += addr - dwa->target;
 
+               *stolen = false;
                return (dwa);
        }
 
-       /* not found */
+       /* No existing dynamic mapping found. We'll need to check for a defined
+        * region to determine whether we can fulfill this request by
+        * stealing from an existing allocated register window */
+       region = bhndb_find_resource_region(br, addr, size);
+       if (region == NULL)
+               return (NULL);
+
+       if ((region->alloc_flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) == 0)
+               return (NULL);
+
+       if ((dwa = bhndb_dw_steal(br, restore)) != NULL) {
+               *stolen = true;
+               return (dwa);
+       }
+
        return (NULL);
 }
 
@@ -1649,9 +1696,8 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
  * Return a borrowed reference to a bridge resource allocation record capable
  * of handling bus I/O requests of @p size at @p addr.
  * 
- * This will either return a  reference to an existing allocation
- * record mapping the requested space, or will configure and return a free
- * allocation record.
+ * This will either return a reference to an existing allocation record mapping
+ * the requested space, or will configure and return a free allocation record.
  * 
  * Will panic if a usable record cannot be found.
  * 
@@ -1660,10 +1706,16 @@ bhndb_io_resource_slow(struct bhndb_softc *sc, bus_add
  * @param size The size of the I/O operation to be performed at @p addr. 
  * @param[out] offset The offset within the returned resource at which
  * to perform the I/O request.
+ * @param[out] stolen Set to true if the allocation record was stolen to 
fulfill
+ * this request. If a stolen allocation record is returned,
+ * bhndb_io_resource_restore() must be called upon completion of the bus I/O
+ * request.
+ * @param[out] restore If the allocation record was stolen, this will be set
+ * to the target that must be restored.
  */
 static inline struct bhndb_dw_alloc *
 bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
-    bus_size_t *offset)
+    bus_size_t *offset, bool *stolen, bus_addr_t *restore)
 {
        struct bhndb_resources  *br;
        struct bhndb_dw_alloc   *dwa;
@@ -1691,7 +1743,8 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
         * current operation.
         */
        if (dwa == NULL) {
-               dwa = bhndb_io_resource_slow(sc, addr, size, offset);
+               dwa = bhndb_io_resource_slow(sc, addr, size, offset, stolen,
+                   restore);
                if (dwa == NULL) {
                        panic("register windows exhausted attempting to map "
                            "0x%llx-0x%llx\n", 
@@ -1720,6 +1773,7 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
 
        /* Calculate the offset and return */
        *offset = (addr - dwa->target) + dwa->win->win_offset;
+       *stolen = false;
        return (dwa);
 }
 
@@ -1733,12 +1787,14 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
        struct bhndb_dw_alloc   *dwa;                           \
        struct resource         *io_res;                        \
        bus_size_t               io_offset;                     \
+       bus_addr_t               restore;               \
+       bool                     stolen;                        \
                                                                \
        sc = device_get_softc(dev);                             \
                                                                \
        BHNDB_LOCK(sc);                                         \
        dwa = bhndb_io_resource(sc, rman_get_start(r->res) +    \
-           offset, _io_size, &io_offset);                      \
+           offset, _io_size, &io_offset, &stolen, &restore);   \
        io_res = dwa->parent_res;                               \
                                                                \
        KASSERT(!r->direct,                                     \
@@ -1748,6 +1804,10 @@ bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t a
            ("i/o resource is not active"));
 
 #define        BHNDB_IO_COMMON_TEARDOWN()                              \
+       if (stolen) {                                           \
+               bhndb_dw_return_stolen(sc->dev, sc->bus_res,    \
+                   dwa, restore);                              \
+       }                                                       \
        BHNDB_UNLOCK(sc);
 
 /* Defines a bhndb_bus_read_* method implementation */

Modified: head/sys/dev/bhnd/bhndb/bhndb.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.h     Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb.h     Tue Nov 28 00:12:14 2017        
(r326297)
@@ -90,6 +90,7 @@ struct bhndb_regwin {
                        bhnd_port_type  port_type;      /**< mapped port type */
                        u_int           port;           /**< mapped port number 
*/
                        u_int           region;         /**< mapped region 
number */
+                       bhnd_size_t     offset;         /**< mapped offset 
within the region */
                } core;
 
                /** SPROM register window (BHNDB_REGWIN_T_SPROM). */
@@ -150,6 +151,26 @@ typedef enum {
 } bhndb_priority_t;
 
 /**
+ * bhndb resource allocation flags.
+ */
+enum bhndb_alloc_flags {
+       /**
+        * If resource overcommit prevents fulfilling a request for this
+        * resource, an in-use resource should be be borrowed to fulfill the
+        * request.
+        * 
+        * The only known use case is to support accessing the ChipCommon core
+        * during Wi-Fi driver operation on early PCI Wi-Fi devices
+        * (PCI_V0, SSB) that do not provide a dedicated ChipCommon register
+        * window mapping; on such devices, device and firmware semantics
+        * guarantee the safety of -- after disabling interrupts -- borrowing
+        * the single dynamic register window that's been assigned to the D11
+        * core to perform the few ChipCommon operations required by the driver.
+        */
+       BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT       = (1<<0),
+};
+
+/**
  * Port resource priority descriptor.
  */
 struct bhndb_port_priority {
@@ -157,6 +178,7 @@ struct bhndb_port_priority {
        u_int                   port;           /**< port */
        u_int                   region;         /**< region */
        bhndb_priority_t        priority;       /**< port priority */
+       uint32_t                alloc_flags;    /**< port allocation flags 
(@see bhndb_alloc_flags) */
 };
 
 /**

Modified: head/sys/dev/bhnd/bhndb/bhndb_hwdata.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_hwdata.c      Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_hwdata.c      Tue Nov 28 00:12:14 2017        
(r326297)
@@ -76,19 +76,24 @@ __FBSDID("$FreeBSD$");
        BHNDB_PORTS(__VA_ARGS__)                \
 }
 
-/* Define a port priority record for the type/port/region
- * triplet. */
-#define        BHNDB_PORT_PRIO(_type, _port, _region, _priority) {     \
+/* Define a port priority record for the type/port/region triplet, optionally
+ * specifying port allocation flags as the final argument */
+#define        BHNDB_PORT_PRIO(_type, _port, _region, _priority, ...)  \
+       _BHNDB_PORT_PRIO(_type, _port, _region, _priority, ## __VA_ARGS__, 0)
+
+#define        _BHNDB_PORT_PRIO(_type, _port, _region, _priority, _flags, ...) 
\
+{                                                              \
        .type           = (BHND_PORT_ ## _type),                \
        .port           = _port,                                \
        .region         = _region,                              \
-       .priority       = (BHNDB_PRIORITY_ ## _priority)        \
+       .priority       = (BHNDB_PRIORITY_ ## _priority),       \
+       .alloc_flags    = (_flags)                              \
 }
 
 /* Define a port priority record for the default (_type, 0, 0) type/port/region
  * triplet. */
-#define        BHNDB_PORT0_PRIO(_type, _priority)      \
-       BHNDB_PORT_PRIO(_type, 0, 0, _priority)
+#define        BHNDB_PORT0_PRIO(_type, _priority, ...) \
+       BHNDB_PORT_PRIO(_type, 0, 0, _priority, ## __VA_ARGS__, 0)
 
 /**
  * Generic resource priority configuration usable with all currently supported
@@ -170,10 +175,14 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab
         * Agent ports are marked as 'NONE' on siba(4) devices, as they
         * will be fully mappable via register windows shared with the
         * device0.0 port.
+        * 
+        * To support early PCI_V0 devices, we enable FULFILL_ON_OVERCOMMIT for
+        * ChipCommon.
         */
        BHNDB_CLASS_PRIO(CC,            LOW,
                /* Device Block */
-               BHNDB_PORT_PRIO(DEVICE, 0,      0,      LOW)
+               BHNDB_PORT_PRIO(DEVICE, 0,      0,      LOW,
+                   BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
        ),
 
        BHNDB_CLASS_PRIO(PMU,           LOW,
@@ -193,4 +202,4 @@ const struct bhndb_hw_priority bhndb_siba_priority_tab
        ),
 
        BHNDB_HW_PRIORITY_TABLE_END
-};
\ No newline at end of file
+};

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c Tue Nov 28 00:12:14 2017        
(r326297)
@@ -707,26 +707,28 @@ bhndb_pci_sprom_size(struct bhndb_pci_softc *sc)
  * Return the host resource providing a static mapping of the PCI core's
  * registers.
  * 
- * @param      sc      bhndb PCI driver state.
- * @param[out] res     On success, the host resource containing our PCI
- *                     core's register window.
- * @param[out] offset  On success, the offset of the PCI core registers within
- *                     @p res.
+ * @param      sc              bhndb PCI driver state.
+ * @param      offset          The required readable offset within the PCI core
+ *                             register block.
+ * @param      size            The required readable size at @p offset.
+ * @param[out] res             On success, the host resource containing our PCI
+ *                             core's register window.
+ * @param[out] res_offset      On success, the @p offset relative to @p res.
  *
  * @retval 0           success
  * @retval ENXIO       if a valid static register window mapping the PCI core
  *                     registers is not available.
  */
 static int
-bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res,
-    bus_size_t *offset)
+bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset,
+    bus_size_t size, struct resource **res, bus_size_t *res_offset)
 {
        const struct bhndb_regwin       *win;
        struct resource                 *r;
 
-       /* Locate the static register window mapping the PCI core */
+       /* Locate the static register window mapping the requested offset */
        win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows,
-           sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0);
+           sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size);
        if (win == NULL) {
                device_printf(sc->dev, "missing PCI core register window\n");
                return (ENXIO);
@@ -739,8 +741,11 @@ bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, st
                return (ENXIO);
        }
 
+       KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of "
+           "register window", (uintmax_t)offset));
+
        *res = r;
-       *offset = win->win_offset;
+       *res_offset = win->win_offset + (offset - win->d.core.offset);
 
        return (0);
 }
@@ -761,18 +766,21 @@ bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_s
        bus_size_t       r_offset;
        int              error;
 
-       if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
-               panic("no PCI core registers: %d", error);
+       error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
+       if (error) {
+               panic("no PCI register window mapping %#jx+%#x: %d",
+                   (uintmax_t)offset, width, error);
+       }
 
        switch (width) {
        case 1:
-               bus_write_1(r, r_offset + offset, value);
+               bus_write_1(r, r_offset, value);
                break;
        case 2:
-               bus_write_2(r, r_offset + offset, value);
+               bus_write_2(r, r_offset, value);
                break;
        case 4:
-               bus_write_4(r, r_offset + offset, value);
+               bus_write_4(r, r_offset, value);
                break;
        default:
                panic("invalid width: %u", width);
@@ -794,16 +802,19 @@ bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_si
        bus_size_t       r_offset;
        int              error;
 
-       if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset)))
-               panic("no PCI core registers: %d", error);
+       error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset);
+       if (error) {
+               panic("no PCI register window mapping %#jx+%#x: %d",
+                   (uintmax_t)offset, width, error);
+       }
 
        switch (width) {
        case 1:
-               return (bus_read_1(r, r_offset + offset));
+               return (bus_read_1(r, r_offset));
        case 2:
-               return (bus_read_2(r, r_offset + offset));
+               return (bus_read_2(r, r_offset));
        case 4:
-               return (bus_read_4(r, r_offset + offset));
+               return (bus_read_4(r, r_offset));
        default:
                panic("invalid width: %u", width);
        }
@@ -1057,7 +1068,7 @@ bhndb_enable_pci_clocks(device_t dev)
        pci_dev = device_get_parent(dev);
 
        /* Only supported and required on PCI devices */
-       if (!bhndb_is_pcie_attached(dev))
+       if (bhndb_is_pcie_attached(dev))
                return (0);
 
        /* Read state of XTAL pin */

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c  Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c  Tue Nov 28 00:12:14 2017        
(r326297)
@@ -312,7 +312,13 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = {
                        .res            = { SYS_RES_MEMORY, PCIR_BAR(0) }
                },
                
-               /* bar0+0x1800: pci core registers */
+               /*
+                * bar0+0x1800: pci core registers.
+                * 
+                * Does not include the SSB CFG registers found at the end of
+                * the 4K core register block; these are mapped non-contigiously
+                * by the next entry.
+                */
                {
                        .win_type       = BHNDB_REGWIN_T_CORE,
                        .win_offset     = BHNDB_PCI_V0_BAR0_PCIREG_OFFSET,
@@ -322,10 +328,27 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = {
                                .unit   = 0,
                                .port   = 0,
                                .region = 0,
+                               .port_type = BHND_PORT_DEVICE,
+                       },
+                       .res            = { SYS_RES_MEMORY, PCIR_BAR(0) }
+               },
+
+               /* bar0+0x1E00: pci core (SSB CFG registers) */
+               {
+                       .win_type       = BHNDB_REGWIN_T_CORE,
+                       .win_offset     = BHNDB_PCI_V0_BAR0_PCISB_OFFSET        
,
+                       .win_size       = BHNDB_PCI_V0_BAR0_PCISB_SIZE,
+                       .d.core = {
+                               .class  = BHND_DEVCLASS_PCI,
+                               .unit   = 0,
+                               .port   = 0,
+                               .region = 0,
+                               .offset = BHNDB_PCI_V0_BAR0_PCISB_COREOFF,
                                .port_type = BHND_PORT_DEVICE
                        },
                        .res            = { SYS_RES_MEMORY, PCIR_BAR(0) }
                },
+               
                BHNDB_REGWIN_TABLE_END
        },
 

Modified: head/sys/dev/bhnd/bhndb/bhndb_pcireg.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pcireg.h      Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_pcireg.h      Tue Nov 28 00:12:14 2017        
(r326297)
@@ -40,7 +40,8 @@
  *     [offset+  size] type    description
  *     [0x0000+0x1000] dynamic mapped backplane address space (window 0).
  *     [0x1000+0x0800] fixed   SPROM shadow
- *     [0x1800+0x0800] fixed   pci core registers
+ *     [0x1800+0x0E00] fixed   pci core device registers
+ *     [0x1E00+0x0200] fixed   pci core siba config registers
  * 
  * == PCI_V1 ==
  * Applies to:
@@ -133,8 +134,11 @@
 #define        BHNDB_PCI_V0_BAR0_WIN0_SIZE     0x1000
 #define        BHNDB_PCI_V0_BAR0_SPROM_OFFSET  0x1000  /* bar0 + 4K accesses 
sprom shadow (in pci core) */
 #define BHNDB_PCI_V0_BAR0_SPROM_SIZE   0x0800
-#define        BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800  /* bar0 + 6K accesses 
pci core registers */
-#define        BHNDB_PCI_V0_BAR0_PCIREG_SIZE   0x0800
+#define        BHNDB_PCI_V0_BAR0_PCIREG_OFFSET 0x1800  /* bar0 + 6K accesses 
pci core registers (not including SSB CFG registers) */
+#define        BHNDB_PCI_V0_BAR0_PCIREG_SIZE   0x0E00
+#define        BHNDB_PCI_V0_BAR0_PCISB_OFFSET  0x1E00  /* bar0 + 7.5K accesses 
pci core's SSB CFG register blocks */
+#define        BHNDB_PCI_V0_BAR0_PCISB_SIZE    0x0200
+#define        BHNDB_PCI_V0_BAR0_PCISB_COREOFF 0xE00   /* mapped offset 
relative to the core base address */
 
 /* PCI_V1 */
 #define        BHNDB_PCI_V1_BAR0_WIN0_CONTROL  0x80    /* backplane address 
space accessed by BAR0/WIN0 */

Modified: head/sys/dev/bhnd/bhndb/bhndb_private.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_private.h     Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_private.h     Tue Nov 28 00:12:14 2017        
(r326297)
@@ -70,6 +70,7 @@ int                            bhndb_add_resource_region(
                                     struct bhndb_resources *br,
                                     bhnd_addr_t addr, bhnd_size_t size,
                                     bhndb_priority_t priority,
+                                    uint32_t alloc_flags,
                                     const struct bhndb_regwin *static_regwin);
 
 int                             bhndb_find_resource_limits(
@@ -93,6 +94,10 @@ struct bhndb_intr_handler    *bhndb_find_intr_handler(
                                     struct bhndb_resources *br,
                                     void *cookiep);
 
+bool                            bhndb_has_static_region_mapping(
+                                    struct bhndb_resources *br,
+                                    bhnd_addr_t addr, bhnd_size_t size);
+
 struct bhndb_region            *bhndb_find_resource_region(
                                     struct bhndb_resources *br,
                                     bhnd_addr_t addr, bhnd_size_t size);
@@ -120,11 +125,25 @@ int                                
bhndb_dw_set_addr(device_t dev,
                                     struct bhndb_dw_alloc *dwa,
                                     bus_addr_t addr, bus_size_t size);
 
+struct bhndb_dw_alloc          *bhndb_dw_steal(struct bhndb_resources *br,
+                                    bus_addr_t *saved);
+
+void                            bhndb_dw_return_stolen(device_t dev,
+                                    struct bhndb_resources *br,
+                                    struct bhndb_dw_alloc *dwa,
+                                    bus_addr_t saved);
+
 const struct bhndb_hw_priority *bhndb_hw_priority_find_core(
                                     const struct bhndb_hw_priority *table,
                                     struct bhnd_core_info *core);
 
+const struct bhndb_port_priority *bhndb_hw_priorty_find_port(
+                                    const struct bhndb_hw_priority *table,
+                                    struct bhnd_core_info *core,
+                                    bhnd_port_type port_type, u_int port,
+                                    u_int region);
 
+
 /**
  * Dynamic register window allocation reference.
  */
@@ -152,6 +171,7 @@ struct bhndb_region {
        bhnd_addr_t                      addr;          /**< start of mapped 
range */
        bhnd_size_t                      size;          /**< size of mapped 
range */
        bhndb_priority_t                 priority;      /**< direct resource 
allocation priority */
+       uint32_t                         alloc_flags;   /**< resource 
allocation flags (@see bhndb_alloc_flags) */
        const struct bhndb_regwin       *static_regwin; /**< fixed mapping 
regwin, if any */
 
        STAILQ_ENTRY(bhndb_region)       link;
@@ -185,6 +205,7 @@ struct bhndb_resources {
 
        STAILQ_HEAD(, bhndb_region)      bus_regions;   /**< bus region 
descriptors */
 
+       struct mtx                       dw_steal_mtx;  /**< spinlock must be 
held when stealing a dynamic window allocation */
        struct bhndb_dw_alloc           *dw_alloc;      /**< dynamic window 
allocation records */
        size_t                           dwa_count;     /**< number of dynamic 
windows available. */
        bitstr_t                        *dwa_freelist;  /**< dynamic window 
free list */

Modified: head/sys/dev/bhnd/bhndb/bhndb_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_subr.c        Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndb_subr.c        Tue Nov 28 00:12:14 2017        
(r326297)
@@ -291,7 +291,10 @@ bhndb_alloc_resources(device_t dev, device_t parent_de
        r->min_prio = BHNDB_PRIORITY_NONE;
        STAILQ_INIT(&r->bus_regions);
        STAILQ_INIT(&r->bus_intrs);
-       
+
+       mtx_init(&r->dw_steal_mtx, device_get_nameunit(dev),
+           "bhndb dwa_steal lock", MTX_SPIN);
+
        /* Initialize host address space resource manager. */
        r->ht_mem_rman.rm_start = 0;
        r->ht_mem_rman.rm_end = ~0;
@@ -492,6 +495,8 @@ failed:
        if (r->res != NULL)
                bhndb_release_host_resources(r->res);
 
+       mtx_destroy(&r->dw_steal_mtx);
+
        free(r, M_BHND);
 
        return (NULL);
@@ -626,6 +631,10 @@ bhndb_free_resources(struct bhndb_resources *br)
 
        free(br->dw_alloc, M_BHND);
        free(br->dwa_freelist, M_BHND);
+
+       mtx_destroy(&br->dw_steal_mtx);
+
+       free(br, M_BHND);
 }
 
 /**
@@ -1054,6 +1063,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br,
  * @param size The size of this region.
  * @param priority The resource priority to be assigned to allocations
  * made within this bus region.
+ * @param alloc_flags resource allocation flags (@see bhndb_alloc_flags)
  * @param static_regwin If available, a static register window mapping this
  * bus region entry. If not available, NULL.
  * 
@@ -1062,7 +1072,7 @@ bhndb_find_resource_limits(struct bhndb_resources *br,
  */
 int
 bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
-    bhnd_size_t size, bhndb_priority_t priority,
+    bhnd_size_t size, bhndb_priority_t priority, uint32_t alloc_flags,
     const struct bhndb_regwin *static_regwin)
 {
        struct bhndb_region     *reg;
@@ -1076,6 +1086,7 @@ bhndb_add_resource_region(struct bhndb_resources *br, 
                .addr = addr,
                .size = size,
                .priority = priority,
+               .alloc_flags = alloc_flags,
                .static_regwin = static_regwin
        };
 
@@ -1084,7 +1095,40 @@ bhndb_add_resource_region(struct bhndb_resources *br, 
        return (0);
 }
 
+/**
+ * Return true if a mapping of @p size bytes at @p addr is provided by either
+ * one contiguous bus region, or by multiple discontiguous regions.
+ *
+ * @param br The resource state to query.
+ * @param addr The requested starting address.
+ * @param size The requested size.
+ */
+bool
+bhndb_has_static_region_mapping(struct bhndb_resources *br,
+    bhnd_addr_t addr, bhnd_size_t size)
+{
+       struct bhndb_region     *region;
+       bhnd_addr_t              r_addr;
 
+       r_addr = addr;
+       while ((region = bhndb_find_resource_region(br, r_addr, 1)) != NULL) {
+               /* Must be backed by a static register window */
+               if (region->static_regwin == NULL)
+                       return (false);
+
+               /* Adjust the search offset */
+               r_addr += region->size;
+
+               /* Have we traversed a complete (if discontiguous) mapping? */
+               if (r_addr == addr + size)
+                       return (true);
+
+       }
+
+       /* No complete mapping found */
+       return (false);
+}
+
 /**
  * Find the bus region that maps @p size bytes at @p addr.
  * 
@@ -1302,7 +1346,7 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources
 
        rw = dwa->win;
 
-       KASSERT(bhndb_dw_is_free(br, dwa),
+       KASSERT(bhndb_dw_is_free(br, dwa) || mtx_owned(&br->dw_steal_mtx),
            ("attempting to set the target address on an in-use window"));
 
        /* Page-align the target address */
@@ -1324,6 +1368,74 @@ bhndb_dw_set_addr(device_t dev, struct bhndb_resources
 }
 
 /**
+ * Steal an in-use allocation record from @p br, returning the record's current
+ * target in @p saved on success.
+ * 
+ * This function acquires a mutex and disables interrupts; callers should
+ * avoid holding a stolen window longer than required to issue an I/O
+ * request.
+ * 
+ * A successful call to bhndb_dw_steal() must be balanced with a call to
+ * bhndb_dw_return_stolen().
+ * 
+ * @param br The resource state from which a window should be stolen.
+ * @param saved The stolen window's saved target address.
+ * 
+ * @retval non-NULL success
+ * @retval NULL no dynamic window regions are defined.
+ */
+struct bhndb_dw_alloc *
+bhndb_dw_steal(struct bhndb_resources *br, bus_addr_t *saved)
+{
+       struct bhndb_dw_alloc *dw_stolen;
+
+       KASSERT(bhndb_dw_next_free(br) == NULL,
+           ("attempting to steal an in-use window while free windows remain"));
+
+       /* Nothing to steal from? */
+       if (br->dwa_count == 0)
+               return (NULL);
+
+       /*
+        * Acquire our steal spinlock; this will be released in
+        * bhndb_dw_return_stolen().
+        * 
+        * Acquiring also disables interrupts, which is required when one is
+        * stealing an in-use existing register window.
+        */
+       mtx_lock_spin(&br->dw_steal_mtx);
+
+       dw_stolen = &br->dw_alloc[0];
+       *saved = dw_stolen->target;
+       return (dw_stolen);
+}
+
+/**
+ * Return an allocation record previously stolen using bhndb_dw_steal().
+ *
+ * @param dev The device on which to issue a BHNDB_SET_WINDOW_ADDR() request.
+ * @param br The resource state owning @p dwa.
+ * @param dwa The allocation record to be returned.
+ * @param saved The original target address provided by bhndb_dw_steal().
+ */
+void
+bhndb_dw_return_stolen(device_t dev, struct bhndb_resources *br,
+    struct bhndb_dw_alloc *dwa, bus_addr_t saved)
+{
+       int error;
+
+       mtx_assert(&br->dw_steal_mtx, MA_OWNED);
+
+       error = bhndb_dw_set_addr(dev, br, dwa, saved, 0);
+       if (error) {
+               panic("failed to restore register window target %#jx: %d\n",
+                   (uintmax_t)saved, error);
+       }
+
+       mtx_unlock_spin(&br->dw_steal_mtx);
+}
+
+/**
  * Return the count of @p type register windows in @p table.
  * 
  * @param table The table to search.
@@ -1380,18 +1492,24 @@ bhndb_regwin_find_type(const struct bhndb_regwin *tabl
  * @param port_type The required port type.
  * @param port The required port.
  * @param region The required region.
+ * @param offset The required readable core register block offset.
+ * @param min_size The required minimum readable size at @p offset.
  *
  * @retval bhndb_regwin The first matching window.
  * @retval NULL If no matching window was found. 
  */
 const struct bhndb_regwin *
 bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class,
-    int unit, bhnd_port_type port_type, u_int port, u_int region)
+    int unit, bhnd_port_type port_type, u_int port, u_int region,
+    bus_size_t offset, bus_size_t min_size)
 {
        const struct bhndb_regwin *rw;
-       
+
        for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++)
        {
+               bus_size_t rw_offset;
+
+               /* Match on core, port, and region attributes */
                if (rw->win_type != BHNDB_REGWIN_T_CORE)
                        continue;
 
@@ -1410,6 +1528,19 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
                if (rw->d.core.region != region)
                        continue;
 
+               /* Verify that the requested range is mapped within
+                * this register window */
+               if (rw->d.core.offset > offset)
+                       continue;
+
+               rw_offset = offset - rw->d.core.offset;
+
+               if (rw->win_size < rw_offset)
+                       continue;
+
+               if (rw->win_size - rw_offset < min_size)
+                       continue;
+
                return (rw);
        }
 
@@ -1429,7 +1560,8 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
  * @param port_type The required port type.
  * @param port The required port.
  * @param region The required region.
- * @param min_size The minimum window size.
+ * @param offset The required readable core register block offset.
+ * @param min_size The required minimum readable size at @p offset.
  *
  * @retval bhndb_regwin The first matching window.
  * @retval NULL If no matching window was found. 
@@ -1437,13 +1569,13 @@ bhndb_regwin_find_core(const struct bhndb_regwin *tabl
 const struct bhndb_regwin *
 bhndb_regwin_find_best(const struct bhndb_regwin *table,
     bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port,
-    u_int region, bus_size_t min_size)
+    u_int region, bus_size_t offset, bus_size_t min_size)
 {
        const struct bhndb_regwin *rw;
 
        /* Prefer a fixed core mapping */
        rw = bhndb_regwin_find_core(table, class, unit, port_type,
-           port, region);
+           port, region, offset, min_size);
        if (rw != NULL)
                return (rw);
 
@@ -1494,6 +1626,45 @@ bhndb_hw_priority_find_core(const struct bhndb_hw_prio
        for (hp = table; hp->ports != NULL; hp++) {
                if (bhnd_core_matches(core, &hp->match))
                        return (hp);
+       }
+
+       /* not found */
+       return (NULL);
+}
+
+
+/**
+ * Search for a port resource priority descriptor in @p table.
+ * 
+ * @param table The table to search.
+ * @param core The core to match against @p table.
+ * @param port_type The required port type.
+ * @param port The required port.
+ * @param region The required region.
+ */
+const struct bhndb_port_priority *
+bhndb_hw_priorty_find_port(const struct bhndb_hw_priority *table,
+    struct bhnd_core_info *core, bhnd_port_type port_type, u_int port,
+    u_int region)
+{
+       const struct bhndb_hw_priority          *hp;
+
+       if ((hp = bhndb_hw_priority_find_core(table, core)) == NULL)
+               return (NULL);
+
+       for (u_int i = 0; i < hp->num_ports; i++) {
+               const struct bhndb_port_priority *pp = &hp->ports[i];
+
+               if (pp->type != port_type)
+                       continue;
+
+               if (pp->port != port)
+                       continue;
+
+               if (pp->region != region)
+                       continue;
+
+               return (pp);
        }
 
        /* not found */

Modified: head/sys/dev/bhnd/bhndb/bhndbvar.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndbvar.h  Mon Nov 27 23:48:21 2017        
(r326296)
+++ head/sys/dev/bhnd/bhndb/bhndbvar.h  Tue Nov 28 00:12:14 2017        
(r326297)
@@ -118,13 +118,15 @@ const struct bhndb_regwin *bhndb_regwin_find_core(
                                     const struct bhndb_regwin *table,
                                     bhnd_devclass_t class, int unit,
                                     bhnd_port_type port_type, u_int port,
-                                    u_int region);
+                                    u_int region, bus_size_t offset,
+                                    bus_size_t min_size);
 
 const struct bhndb_regwin      *bhndb_regwin_find_best(
                                     const struct bhndb_regwin *table,
                                     bhnd_devclass_t class, int unit,
                                     bhnd_port_type port_type, u_int port,
-                                    u_int region, bus_size_t min_size);
+                                    u_int region, bus_size_t offset,
+                                    bus_size_t min_size);
 
 bool                            bhndb_regwin_match_core(
                                     const struct bhndb_regwin *regw,
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to