Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Nov 2017 00:12:14 +0000 (UTC)
From:      "Landon J. Fuller" <landonf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r326297 - head/sys/dev/bhnd/bhndb
Message-ID:  <201711280012.vAS0CEjO051249@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201711280012.vAS0CEjO051249>