svn commit: r326080 - in head/sys: dev/bhnd dev/bhnd/bhndb dev/bhnd/cores/pci dev/bhnd/cores/pcie2 mips/broadcom

Landon J. Fuller landonf at FreeBSD.org
Tue Nov 21 23:25:25 UTC 2017


Author: landonf
Date: Tue Nov 21 23:25:22 2017
New Revision: 326080
URL: https://svnweb.freebsd.org/changeset/base/326080

Log:
  bhnd(4): Add support for querying DMA address translation parameters
  
  BHND Wi-Fi chipsets and SoCs share a common DMA engine, operating within
  backplane address space. To support host DMA on Wi-Fi chipsets, the bridge
  core maps host address space onto the backplane; any host addresses must
  be translated to their corresponding backplane address.
  
  
  - Defines a new bhnd_get_dma_translation(9) API to support querying DMA
    address translation parameters from the bhnd(4) bus.
  - Extends bhndb(4) to provide DMA translation descriptors from a DMA
    address translation table defined in the host bridge-specific
    bhndb_hwcfg.
  - Defines bhndb(4) DMA address translation tables for all supported host
    bridge cores.
  - Extends mips/broadcom's bhnd_nexus driver to return an identity (no-op)
    DMA translation descriptor; no translation is required when addressing
    the SoC backplane.
  
  Approved by:	adrian (mentor)
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D12582

Modified:
  head/sys/dev/bhnd/bhnd.h
  head/sys/dev/bhnd/bhnd_bus_if.m
  head/sys/dev/bhnd/bhnd_subr.c
  head/sys/dev/bhnd/bhndb/bhndb.c
  head/sys/dev/bhnd/bhndb/bhndb.h
  head/sys/dev/bhnd/bhndb/bhndb_pci.c
  head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
  head/sys/dev/bhnd/bhndb/bhndb_subr.c
  head/sys/dev/bhnd/bhndb/bhndbvar.h
  head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h
  head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h
  head/sys/mips/broadcom/bhnd_nexus.c

Modified: head/sys/dev/bhnd/bhnd.h
==============================================================================
--- head/sys/dev/bhnd/bhnd.h	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhnd.h	Tue Nov 21 23:25:22 2017	(r326080)
@@ -220,6 +220,94 @@ struct bhnd_core_info {
 };
 
 /**
+ * bhnd(4) DMA address widths.
+ */
+typedef enum {
+	BHND_DMA_ADDR_30BIT	= 30,	/**< 30-bit DMA */
+	BHND_DMA_ADDR_32BIT	= 32,	/**< 32-bit DMA */
+	BHND_DMA_ADDR_64BIT	= 64,	/**< 64-bit DMA */
+} bhnd_dma_addrwidth;
+
+/**
+ * Convert an address width (in bits) to its corresponding mask.
+ */
+#define	BHND_DMA_ADDR_BITMASK(_width)	\
+	((_width >= 64) ? ~0ULL :	\
+	 (_width == 0) ? 0x0 :		\
+	 ((1ULL << (_width)) - 1))	\
+
+/**
+ * bhnd(4) DMA address translation descriptor.
+ */
+struct bhnd_dma_translation {
+	/**
+	 * Host-to-device physical address translation.
+	 * 
+	 * This may be added to the host physical address to produce a device
+	 * DMA address.
+	 */
+	bhnd_addr_t	base_addr;
+
+	/**
+	 * Device-addressable address mask.
+	 * 
+	 * This defines the device's DMA address range, excluding any bits
+	 * reserved for mapping the address to the base_addr.
+	 */
+	bhnd_addr_t	addr_mask;
+
+	/**
+	 * Device-addressable extended address mask.
+	 *
+	 * If a per-core bhnd(4) DMA engine supports the 'addrext' control
+	 * field, it can be used to provide address bits excluded by addr_mask.
+	 *
+	 * Support for DMA extended address changes – including coordination
+	 * with the core providing DMA translation – is handled transparently by
+	 * the DMA engine. For example, on PCI(e) Wi-Fi chipsets, the Wi-Fi
+	 * core DMA engine will (in effect) update the PCI core's DMA
+	 * sbtopcitranslation base address to map the full address prior to
+	 * performing a DMA transaction.
+	 */
+	bhnd_addr_t	addrext_mask;
+
+	/**
+	 * Translation flags (see bhnd_dma_translation_flags)
+	 */
+	uint32_t	flags;
+};
+
+#define	BHND_DMA_TRANSLATION_TABLE_END	{ 0, 0, 0, 0 }
+
+#define	BHND_DMA_IS_TRANSLATION_TABLE_END(_dt)			\
+	((_dt)->base_addr == 0 && (_dt)->addr_mask == 0 &&	\
+	 (_dt)->addrext_mask == 0 && (_dt)->flags == 0)
+
+/**
+ * bhnd(4) DMA address translation flags.
+ */
+enum bhnd_dma_translation_flags {
+	/**
+	 * The translation remaps the device's physical address space.
+	 * 
+	 * This is used in conjunction with BHND_DMA_TRANSLATION_BYTESWAPPED to
+	 * define a DMA translation that provides byteswapped access to
+	 * physical memory on big-endian MIPS SoCs.
+	 */
+	BHND_DMA_TRANSLATION_PHYSMAP		= (1<<0),
+
+	/**
+	 * Provides a byte-swapped mapping; write requests will be byte-swapped
+	 * before being written to memory, and read requests will be
+	 * byte-swapped before being returned.
+	 *
+	 * This is primarily used to perform efficient byte swapping of DMA
+	 * data on embedded MIPS SoCs executing in big-endian mode.
+	 */
+	BHND_DMA_TRANSLATION_BYTESWAPPED	= (1<<1),	
+};
+
+/**
 * A bhnd(4) bus resource.
 * 
 * This provides an abstract interface to per-core resources that may require
@@ -512,6 +600,10 @@ int				 bhnd_bus_generic_get_nvram_var(device_t dev,
 				     bhnd_nvram_type type);
 const struct bhnd_chipid	*bhnd_bus_generic_get_chipid(device_t dev,
 				     device_t child);
+int				 bhnd_bus_generic_get_dma_translation(
+				     device_t dev, device_t child, u_int width,
+				     uint32_t flags, bus_dma_tag_t *dmat,
+				     struct bhnd_dma_translation *translation);
 int				 bhnd_bus_generic_read_board_info(device_t dev,
 				     device_t child,
 				     struct bhnd_board_info *info);
@@ -840,6 +932,38 @@ bhnd_pwrctl_ungate_clock(device_t dev, bhnd_clock cloc
 static inline bhnd_attach_type
 bhnd_get_attach_type (device_t dev) {
 	return (BHND_BUS_GET_ATTACH_TYPE(device_get_parent(dev), dev));
+}
+
+/**
+ * Find the best available DMA address translation capable of mapping a
+ * physical host address to a BHND DMA device address of @p width with
+ * @p flags.
+ *
+ * @param dev A bhnd bus child device.
+ * @param width The address width within which the translation window must
+ * reside (see BHND_DMA_ADDR_*).
+ * @param flags Required translation flags (see BHND_DMA_TRANSLATION_*).
+ * @param[out] dmat On success, will be populated with a DMA tag specifying the
+ * @p translation DMA address restrictions. This argment may be NULL if the DMA
+ * tag is not desired.
+ * the set of valid host DMA addresses reachable via @p translation.
+ * @param[out] translation On success, will be populated with a DMA address
+ * translation descriptor for @p child. This argment may be NULL if the
+ * descriptor is not desired.
+ *
+ * @retval 0 success
+ * @retval ENODEV If DMA is not supported.
+ * @retval ENOENT If no DMA translation matching @p width and @p flags is
+ * available.
+ * @retval non-zero If determining the DMA address translation for @p child
+ * otherwise fails, a regular unix error code will be returned.
+ */
+static inline int
+bhnd_get_dma_translation(device_t dev, u_int width, uint32_t flags,
+    bus_dma_tag_t *dmat, struct bhnd_dma_translation *translation)
+{
+	return (BHND_BUS_GET_DMA_TRANSLATION(device_get_parent(dev), dev, width,
+	    flags, dmat, translation));
 }
 
 /**

Modified: head/sys/dev/bhnd/bhnd_bus_if.m
==============================================================================
--- head/sys/dev/bhnd/bhnd_bus_if.m	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhnd_bus_if.m	Tue Nov 21 23:25:22 2017	(r326080)
@@ -46,6 +46,7 @@ HEADER {
 	struct bhnd_board_info;
 	struct bhnd_core_info;
 	struct bhnd_chipid;
+	struct bhnd_dma_translation;
 	struct bhnd_devinfo;
 	struct bhnd_resource;
 }
@@ -112,7 +113,7 @@ CODE {
 	{
 		panic("bhnd_bus_get_attach_type unimplemented");
 	}
-	
+
 	static bhnd_clksrc
 	bhnd_bus_null_pwrctl_get_clksrc(device_t dev, device_t child,
 	    bhnd_clock clock)
@@ -478,6 +479,41 @@ METHOD bhnd_attach_type get_attach_type {
 	device_t dev;
 	device_t child;
 } DEFAULT bhnd_bus_null_get_attach_type;
+
+
+/**
+ * Find the best available DMA address translation capable of mapping a
+ * physical host address to a BHND DMA device address of @p width with
+ * @p flags.
+ *
+ * @param dev The parent of @p child.
+ * @param child The bhnd device requesting the DMA address translation.
+ * @param width The address width within which the translation window must
+ * reside (see BHND_DMA_ADDR_*).
+ * @param flags Required translation flags (see BHND_DMA_TRANSLATION_*).
+ * @param[out] dmat On success, will be populated with a DMA tag specifying the
+ * @p translation DMA address restrictions. This argment may be NULL if the DMA
+ * tag is not desired.
+ * the set of valid host DMA addresses reachable via @p translation.
+ * @param[out] translation On success, will be populated with a DMA address
+ * translation descriptor for @p child. This argment may be NULL if the
+ * descriptor is not desired.
+ *
+ * @retval 0 success
+ * @retval ENODEV If DMA is not supported.
+ * @retval ENOENT If no DMA translation matching @p width and @p flags is
+ * available.
+ * @retval non-zero If determining the DMA address translation for @p child
+ * otherwise fails, a regular unix error code will be returned.
+ */
+METHOD int get_dma_translation {
+	device_t dev;
+	device_t child;
+	u_int width;
+	uint32_t flags;
+	bus_dma_tag_t *dmat;
+	struct bhnd_dma_translation *translation;
+} DEFAULT bhnd_bus_generic_get_dma_translation;
 
 /**
  * Attempt to read the BHND board identification from the parent bus.

Modified: head/sys/dev/bhnd/bhnd_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhnd_subr.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhnd_subr.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -2104,6 +2104,27 @@ bhnd_bus_generic_get_chipid(device_t dev, device_t chi
 	panic("missing BHND_BUS_GET_CHIPID()");
 }
 
+/**
+ * Helper function for implementing BHND_BUS_GET_DMA_TRANSLATION().
+ * 
+ * If a parent device is available, this implementation delegates the
+ * request to the BHND_BUS_GET_DMA_TRANSLATION() method on the parent of @p dev.
+ *
+ * If no parent device is available, this implementation will panic.
+ */
+int
+bhnd_bus_generic_get_dma_translation(device_t dev, device_t child, u_int width,
+    uint32_t flags, bus_dma_tag_t *dmat,
+    struct bhnd_dma_translation *translation)
+{
+	if (device_get_parent(dev) != NULL) {
+		return (BHND_BUS_GET_DMA_TRANSLATION(device_get_parent(dev),
+		    child, width, flags, dmat, translation));
+	}
+
+	panic("missing BHND_BUS_GET_DMA_TRANSLATION()");
+}
+
 /* nvram board_info population macros for bhnd_bus_generic_read_board_info() */
 #define	BHND_GV(_dest, _name)	\
 	bhnd_nvram_getvar_uint(child, BHND_NVAR_ ## _name, &_dest,	\

Modified: head/sys/dev/bhnd/bhndb/bhndb.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndb.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -2040,13 +2040,93 @@ bhndb_remap_intr(device_t dev, device_t child, u_int i
 }
 
 /**
+ * Default bhndb(4) implementation of BHND_BUS_GET_DMA_TRANSLATION().
+ */
+static inline int
+bhndb_get_dma_translation(device_t dev, device_t child, u_int width,
+    uint32_t flags, bus_dma_tag_t *dmat,
+    struct bhnd_dma_translation *translation)
+{
+	struct bhndb_softc			*sc;
+	const struct bhndb_hwcfg		*hwcfg;
+	const struct bhnd_dma_translation	*match;
+	bus_dma_tag_t				 match_dmat;
+	bhnd_addr_t				 addr_mask, match_addr_mask;
+
+	sc = device_get_softc(dev);
+	hwcfg = sc->bus_res->cfg;
+
+	/* Is DMA supported? */
+	if (sc->bus_res->res->dma_tags == NULL)
+		return (ENODEV);
+
+	/* Find the best matching descriptor for the requested type */
+	addr_mask = BHND_DMA_ADDR_BITMASK(width);
+
+	match = NULL;
+	match_addr_mask = 0x0;
+	match_dmat = NULL;
+
+	for (size_t i = 0; i < sc->bus_res->res->num_dma_tags; i++) {
+		const struct bhnd_dma_translation	*dwin;
+		bhnd_addr_t				 masked;
+
+		dwin = &hwcfg->dma_translations[i];
+
+		/* The base address must be device addressable */
+		if ((dwin->base_addr & addr_mask) != dwin->base_addr)
+			continue;
+
+		/* The flags must match */
+		if ((dwin->flags & flags) != flags)
+			continue;
+
+		/* The window must cover at least part of our addressable
+		 * range */
+		masked = (dwin->addr_mask | dwin->addrext_mask) & addr_mask;
+		if (masked == 0)
+			continue;
+	
+		/* Is this a better match? */
+		if (match == NULL || masked > match_addr_mask) {
+			match = dwin;
+			match_addr_mask = masked;
+			match_dmat = sc->bus_res->res->dma_tags[i];
+		}
+	}
+
+	if (match == NULL || match_addr_mask == 0)
+		return (ENOENT);
+
+	if (dmat != NULL)
+		*dmat = match_dmat;
+
+	if (translation != NULL)
+		*translation = *match;
+
+	return (0);
+}
+
+/**
  * Default bhndb(4) implementation of BUS_GET_DMA_TAG().
  */
 static bus_dma_tag_t
 bhndb_get_dma_tag(device_t dev, device_t child)
 {
-	// TODO
-	return (NULL);
+	struct bhndb_softc *sc = device_get_softc(dev);
+
+	/*
+	 * A bridge may have multiple DMA translation descriptors, each with
+	 * their own incompatible restrictions; drivers should in general call
+	 * BHND_BUS_GET_DMA_TRANSLATION() to fetch both the best available DMA
+	 * translation, and its corresponding DMA tag.
+	 *
+	 * Child drivers that do not use BHND_BUS_GET_DMA_TRANSLATION() are
+	 * responsible for creating their own restricted DMA tag; since we
+	 * cannot do this for them in BUS_GET_DMA_TAG(), we simply return the
+	 * bridge parent's DMA tag directly; 
+	 */
+	return (bus_get_dma_tag(sc->parent_dev));
 }
 
 static device_method_t bhndb_methods[] = {
@@ -2102,6 +2182,7 @@ static device_method_t bhndb_methods[] = {
 	DEVMETHOD(bhnd_bus_get_nvram_var,	bhnd_bus_generic_get_nvram_var),
 	DEVMETHOD(bhnd_bus_map_intr,		bhndb_bhnd_map_intr),
 	DEVMETHOD(bhnd_bus_unmap_intr,		bhndb_bhnd_unmap_intr),
+	DEVMETHOD(bhnd_bus_get_dma_translation,	bhndb_get_dma_translation),
 
 	DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry),
 	DEVMETHOD(bhnd_bus_register_provider,	bhnd_bus_generic_sr_register_provider),

Modified: head/sys/dev/bhnd/bhndb/bhndb.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb.h	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndb.h	Tue Nov 21 23:25:22 2017	(r326080)
@@ -107,12 +107,13 @@ struct bhndb_regwin {
 /**
  * Bridge hardware configuration.
  * 
- * Provides the bridge's register/address mappings, and the resources
- * via which those mappings may be accessed.
+ * Provides the bridge's DMA address translation descriptions, register/address
+ * mappings, and the resources via which those mappings may be accessed.
  */
 struct bhndb_hwcfg {
-	const struct resource_spec	*resource_specs;
-	const struct bhndb_regwin	*register_windows;
+	const struct resource_spec		*resource_specs;	/**< resources required by our register windows */
+	const struct bhndb_regwin		*register_windows;	/**< register window table */
+	const struct bhnd_dma_translation	*dma_translations;	/**< DMA address translation table, or NULL if DMA is not supported */
 };
 
 /**

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -509,7 +509,7 @@ bhndb_pci_read_core_table(device_t dev, struct bhnd_ch
 	hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev);
 
 	/* Allocate our host resources */
-	if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr)))
+	if ((error = bhndb_alloc_host_resources(&hr, dev, parent_dev, cfg)))
 		return (error);
 
 	/* Initialize our erom I/O state */

Modified: head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndb_pci_hwdata.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -45,6 +45,9 @@ __FBSDID("$FreeBSD$");
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 
+#include <dev/bhnd/cores/pci/bhnd_pcireg.h>
+#include <dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h>
+
 #include "bhndbvar.h"
 #include "bhndb_pcireg.h"
 
@@ -100,6 +103,9 @@ const struct bhndb_hwcfg bhndb_pci_siba_generic_hwcfg 
 		},
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	/* DMA unsupported under generic configuration */
+	.dma_translations = NULL,
 };
 
 
@@ -147,6 +153,9 @@ const struct bhndb_hwcfg bhndb_pci_bcma_generic_hwcfg 
 
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	/* DMA unsupported under generic configuration */
+	.dma_translations = NULL,
 };
 
 /**
@@ -319,6 +328,15 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v0 = {
 		},
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	.dma_translations = (const struct bhnd_dma_translation[]) {
+		{
+			.base_addr	= BHND_PCI_DMA32_TRANSLATION,
+			.addr_mask	= ~BHND_PCI_DMA32_MASK,
+			.addrext_mask	= BHND_PCI_DMA32_MASK
+		},
+		BHND_DMA_TRANSLATION_TABLE_END
+	}
 };
 
 /**
@@ -385,6 +403,15 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pci
 
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	.dma_translations = (const struct bhnd_dma_translation[]) {
+		{
+			.base_addr	= BHND_PCI_DMA32_TRANSLATION,
+			.addr_mask	= ~BHND_PCI_DMA32_MASK,
+			.addrext_mask	= BHND_PCI_DMA32_MASK
+		},
+		BHND_DMA_TRANSLATION_TABLE_END
+	}
 };
 
 /**
@@ -451,6 +478,20 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v1_pci
 
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	.dma_translations = (const struct bhnd_dma_translation[]) {
+		{
+			.base_addr	= BHND_PCIE_DMA32_TRANSLATION,
+			.addr_mask	= ~BHND_PCIE_DMA32_MASK,
+			.addrext_mask	= BHND_PCIE_DMA32_MASK
+		},
+		{
+			.base_addr	= BHND_PCIE_DMA64_TRANSLATION,
+			.addr_mask	= ~BHND_PCIE_DMA64_MASK,
+			.addrext_mask	= 0
+		},
+		BHND_DMA_TRANSLATION_TABLE_END
+	}
 };
 
 /**
@@ -520,6 +561,20 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v2 = {
 
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	.dma_translations = (const struct bhnd_dma_translation[]) {
+		{
+			.base_addr	= BHND_PCIE_DMA32_TRANSLATION,
+			.addr_mask	= ~BHND_PCIE_DMA32_MASK,
+			.addrext_mask	= BHND_PCIE_DMA32_MASK
+		},
+		{
+			.base_addr	= BHND_PCIE_DMA64_TRANSLATION,
+			.addr_mask	= ~BHND_PCIE_DMA64_MASK,
+			.addrext_mask	= 0
+		},
+		BHND_DMA_TRANSLATION_TABLE_END
+	}
 };
 
 /**
@@ -589,4 +644,13 @@ static const struct bhndb_hwcfg bhndb_pci_hwcfg_v3 = {
 
 		BHNDB_REGWIN_TABLE_END
 	},
+
+	.dma_translations = (const struct bhnd_dma_translation[]) {
+		{
+			.base_addr	= BHND_PCIE2_DMA64_TRANSLATION,
+			.addr_mask	= ~BHND_PCIE2_DMA64_MASK,
+			.addrext_mask	= 0
+		},
+		BHND_DMA_TRANSLATION_TABLE_END
+	}
 };

Modified: head/sys/dev/bhnd/bhndb/bhndb_subr.c
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndb_subr.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndb_subr.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -41,6 +41,10 @@ __FBSDID("$FreeBSD$");
 #include "bhndb_private.h"
 #include "bhndbvar.h"
 
+static int	bhndb_dma_tag_create(device_t dev, bus_dma_tag_t parent_dmat,
+		    const struct bhnd_dma_translation *translation,
+		    bus_dma_tag_t *dmat);
+
 /**
  * Attach a BHND bridge device to @p parent.
  * 
@@ -402,7 +406,7 @@ bhndb_alloc_resources(device_t dev, device_t parent_de
 	}
 
 	/* Allocate host resources */
-	error = bhndb_alloc_host_resources(parent_dev, r->cfg, &r->res);
+	error = bhndb_alloc_host_resources(&r->res, dev, parent_dev, r->cfg);
 	if (error) {
 		device_printf(r->dev,
 		    "could not allocate host resources on %s: %d\n",
@@ -494,6 +498,65 @@ failed:
 }
 
 /**
+ * Create a new DMA tag for the given @p translation.
+ *
+ * @param	dev		The bridge device.
+ * @param	parent_dmat	The parent DMA tag, or NULL if none.
+ * @param	translation	The DMA translation for which a DMA tag will
+ *				be created.
+ * @param[out]	dmat		On success, the newly created DMA tag.
+ * 
+ * @retval 0		success
+ * @retval non-zero	if creating the new DMA tag otherwise fails, a regular
+ *			unix error code will be returned.
+ */
+static int
+bhndb_dma_tag_create(device_t dev, bus_dma_tag_t parent_dmat,
+    const struct bhnd_dma_translation *translation, bus_dma_tag_t *dmat)
+{
+	bus_dma_tag_t	translation_tag;
+	bhnd_addr_t	dt_mask;
+	bus_addr_t	boundary;
+	bus_addr_t	lowaddr, highaddr;
+	int		error;
+
+	highaddr = BUS_SPACE_MAXADDR;
+	boundary = 0;
+
+	/* Determine full addressable mask */
+	dt_mask = (translation->addr_mask | translation->addrext_mask);
+	KASSERT(dt_mask != 0, ("DMA addr_mask invalid: %#jx",
+		(uintmax_t)dt_mask));
+
+	/* (addr_mask|addrext_mask) is our maximum supported address */
+	lowaddr = MIN(dt_mask, BUS_SPACE_MAXADDR);
+
+	/* Do we need to to avoid crossing a DMA translation window boundary? */
+	if (translation->addr_mask < BUS_SPACE_MAXADDR) {
+		/* round down to nearest power of two */
+		boundary = translation->addr_mask & (~1ULL);
+	}
+
+	/* Create our DMA tag */
+	error = bus_dma_tag_create(parent_dmat,
+	    1,				/* alignment */
+	    boundary, lowaddr, highaddr,
+	    NULL, NULL,			/* filter, filterarg */
+	    BUS_SPACE_MAXSIZE, 0,	/* maxsize, nsegments */
+	    BUS_SPACE_MAXSIZE, 0,	/* maxsegsize, flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &translation_tag);
+	if (error) {
+		device_printf(dev, "failed to create bridge DMA tag: %d\n",
+		    error);
+		return (error);
+	}
+
+	*dmat = translation_tag;
+	return (0);
+}
+
+/**
  * Deallocate the given bridge resource structure and any associated resources.
  * 
  * @param br Resource state to be deallocated.
@@ -571,31 +634,80 @@ bhndb_free_resources(struct bhndb_resources *br)
  * On success, the caller assumes ownership of the allocated host resources,
  * which must be freed via bhndb_release_host_resources().
  *
- * @param	dev		The device to be used when allocating resources
- *				(e.g. via bus_alloc_resources()).
+ * @param[out]	resources	On success, the allocated host resources.
+ * @param	dev		The bridge device.
+ * @param	parent_dev	The parent device from which host resources
+ *				should be allocated (e.g. via
+ *				bus_alloc_resources()).
  * @param	hwcfg		The hardware configuration defining the host
  *				resources to be allocated
- * @param[out]	resources	On success, the allocated host resources.
  */
 int
-bhndb_alloc_host_resources(device_t dev, const struct bhndb_hwcfg *hwcfg,
-    struct bhndb_host_resources **resources)
+bhndb_alloc_host_resources(struct bhndb_host_resources **resources,
+    device_t dev, device_t parent_dev, const struct bhndb_hwcfg *hwcfg)
 {
-	struct bhndb_host_resources	*hr;
-	size_t				 nres;
-	int				 error;
+	struct bhndb_host_resources		*hr;
+	const struct bhnd_dma_translation	*dt;
+	bus_dma_tag_t				 parent_dmat;
+	size_t					 nres, ndt;
+	int					 error;
 
+	parent_dmat = bus_get_dma_tag(parent_dev);
+
 	hr = malloc(sizeof(*hr), M_BHND, M_WAITOK);
-	hr->owner = dev;
+	hr->owner = parent_dev;
 	hr->cfg = hwcfg;
 	hr->resource_specs = NULL;
 	hr->resources = NULL;
+	hr->dma_tags = NULL;
+	hr->num_dma_tags = 0;
 
 	/* Determine our bridge resource count from the hardware config. */
 	nres = 0;
 	for (size_t i = 0; hwcfg->resource_specs[i].type != -1; i++)
 		nres++;
 
+	/* Determine the total count and validate our DMA translation table. */
+	ndt = 0;
+	for (dt = hwcfg->dma_translations; dt != NULL &&
+	    !BHND_DMA_IS_TRANSLATION_TABLE_END(dt); dt++)
+	{
+		/* Validate the defined translation */
+		if ((dt->base_addr & dt->addr_mask) != 0) {
+			device_printf(dev, "invalid DMA translation; base "
+			    "address %#jx overlaps address mask %#jx",
+			    (uintmax_t)dt->base_addr, (uintmax_t)dt->addr_mask);
+
+			error = EINVAL;
+			goto failed;
+		}
+
+		if ((dt->addrext_mask & dt->addr_mask) != 0) {
+			device_printf(dev, "invalid DMA translation; addrext "
+			    "mask %#jx overlaps address mask %#jx",
+			    (uintmax_t)dt->addrext_mask,
+			    (uintmax_t)dt->addr_mask);
+
+			error = EINVAL;
+			goto failed;
+		}
+
+		/* Increment our entry count */
+		ndt++;
+	}
+
+	/* Allocate our DMA tags */
+	hr->dma_tags = malloc(sizeof(*hr->dma_tags) * ndt, M_BHND,
+	    M_WAITOK|M_ZERO);
+	for (size_t i = 0; i < ndt; i++) {
+		error = bhndb_dma_tag_create(dev, parent_dmat,
+		    &hwcfg->dma_translations[i], &hr->dma_tags[i]);
+		if (error)
+			goto failed;
+
+		hr->num_dma_tags++;
+	}
+
 	/* Allocate space for a non-const copy of our resource_spec
 	 * table; this will be updated with the RIDs assigned by
 	 * bus_alloc_resources. */
@@ -617,7 +729,7 @@ bhndb_alloc_host_resources(device_t dev, const struct 
 	    hr->resources);
 	if (error) {
 		device_printf(dev, "could not allocate bridge resources via "
-		    "%s: %d\n", device_get_nameunit(dev), error);
+		    "%s: %d\n", device_get_nameunit(parent_dev), error);
 		goto failed;
 	}
 
@@ -631,6 +743,12 @@ failed:
 	if (hr->resources != NULL)
 		free(hr->resources, M_BHND);
 
+	for (size_t i = 0; i < hr->num_dma_tags; i++)
+		bus_dma_tag_destroy(hr->dma_tags[i]);
+
+	if (hr->dma_tags != NULL)
+		free(hr->dma_tags, M_BHND);
+
 	free(hr, M_BHND);
 
 	return (error);
@@ -646,8 +764,12 @@ bhndb_release_host_resources(struct bhndb_host_resourc
 {
 	bus_release_resources(hr->owner, hr->resource_specs, hr->resources);
 
+	for (size_t i = 0; i < hr->num_dma_tags; i++)
+		bus_dma_tag_destroy(hr->dma_tags[i]);
+
 	free(hr->resources, M_BHND);
 	free(hr->resource_specs, M_BHND);
+	free(hr->dma_tags, M_BHND);
 	free(hr, M_BHND);
 }
 

Modified: head/sys/dev/bhnd/bhndb/bhndbvar.h
==============================================================================
--- head/sys/dev/bhnd/bhndb/bhndbvar.h	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/bhndb/bhndbvar.h	Tue Nov 21 23:25:22 2017	(r326080)
@@ -90,9 +90,11 @@ struct bhndb_intr_isrc		*bhndb_alloc_intr_isrc(device_
 void				 bhndb_free_intr_isrc(
 				     struct bhndb_intr_isrc *isrc);
 
-int				 bhndb_alloc_host_resources(device_t dev,
-				     const struct bhndb_hwcfg *hwcfg,
-				     struct bhndb_host_resources **resources);
+int				 bhndb_alloc_host_resources(
+				     struct bhndb_host_resources **resources,
+				     device_t dev, device_t parent_dev,
+				     const struct bhndb_hwcfg *hwcfg);
+
 void				 bhndb_release_host_resources(
 				     struct bhndb_host_resources *resources);
 struct resource			*bhndb_host_resource_for_range(
@@ -161,6 +163,9 @@ struct bhndb_host_resources {
 	const struct bhndb_hwcfg	*cfg;			/**< bridge hardware configuration */
 	struct resource_spec		*resource_specs;	/**< resource specification table */
 	struct resource			**resources;		/**< allocated resource table */
+	bus_dma_tag_t			*dma_tags;		/**< DMA tags for all hwcfg DMA translations, or NULL
+								     if DMA is not supported */
+	size_t				 num_dma_tags;		/**< DMA tag count */
 };
 
 /**

Modified: head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h
==============================================================================
--- head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h	Tue Nov 21 23:25:22 2017	(r326080)
@@ -29,15 +29,15 @@
  * PCI/PCIe-Gen1 DMA Constants
  */
 
-#define	BHND_PCI_DMA32_TRANSLATION	0x40000000	/* Client Mode sb2pcitranslation2 (1 GB) */
-#define	BHND_PCI_DMA32_SZ		0x40000000	/* Client Mode sb2pcitranslation2 size in bytes */
+#define	BHND_PCI_DMA32_TRANSLATION	0x40000000			/**< PCI DMA32 address translation (sbtopci2) */
+#define	BHND_PCI_DMA32_MASK		BHND_PCI_SBTOPCI2_MASK		/**< PCI DMA32 translation mask */
 
-#define	BHND_PCIE_DMA32_TRANSLATION	BHND_PCI_DMA32_TRANSLATION
-#define	BHND_PCIE_DMA32_SZ		BHND_PCI_DMA32_SZ
+#define	BHND_PCIE_DMA32_TRANSLATION	0x80000000			/**< PCIe-Gen1 DMA32 address translation (sb2pcitranslation2) */
+#define	BHND_PCIE_DMA32_MASK		BHND_PCIE_SBTOPCI2_MASK		/**< PCIe-Gen1 DMA32 translation mask */
 
-#define	BHND_PCIE_DMA64_L32		0x00000000	/**< 64-bit client mode sb2pcitranslation2 (2 ZettaBytes, low 32 bits) */
-#define	BHND_PCIE_DMA64_H32		0x80000000	/**< 64-bit client mode sb2pcitranslation2 (2 ZettaBytes, high 32 bits) */
-
+#define	BHND_PCIE_DMA64_TRANSLATION	_BHND_PCIE_DMA64(TRANSLATION)	/**< PCIe-Gen1 DMA64 address translation (sb2pcitranslation2) */
+#define	BHND_PCIE_DMA64_MASK		_BHND_PCIE_DMA64(MASK)		/**< PCIe-Gen1 DMA64 translation mask */
+#define	_BHND_PCIE_DMA64(_x)		((uint64_t)BHND_PCIE_DMA32_ ## _x << 32)
 /*
  * PCI Core Registers
  */

Modified: head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h
==============================================================================
--- head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/dev/bhnd/cores/pcie2/bhnd_pcie2_reg.h	Tue Nov 21 23:25:22 2017	(r326080)
@@ -24,6 +24,17 @@
 #ifndef _BHND_CORES_PCIE2_BHND_PCIE2_REG_H_
 #define _BHND_CORES_PCIE2_BHND_PCIE2_REG_H_
 
+/*
+ * PCIe-Gen2 DMA Constants
+ */
+
+#define	BHND_PCIE2_DMA64_TRANSLATION	0x8000000000000000	/**< PCIe-Gen2 DMA64 address translation */
+#define	BHND_PCIE2_DMA64_MASK		0xc000000000000000	/**< PCIe-Gen2 DMA64 translation mask */
+
+/*
+ * PCIe-Gen2 Core Registers
+ */
+
 #define	BHND_PCIE2_CLK_CONTROL		0x000
 
 #define	BHND_PCIE2_RC_PM_CONTROL	0x004

Modified: head/sys/mips/broadcom/bhnd_nexus.c
==============================================================================
--- head/sys/mips/broadcom/bhnd_nexus.c	Tue Nov 21 23:15:20 2017	(r326079)
+++ head/sys/mips/broadcom/bhnd_nexus.c	Tue Nov 21 23:25:22 2017	(r326080)
@@ -55,6 +55,8 @@ __FBSDID("$FreeBSD$");
 #include <dev/bhnd/bhndvar.h>
 #include <dev/bhnd/bhnd_ids.h>
 
+#include <dev/bhnd/cores/chipc/chipcreg.h>
+
 #include "bcm_machdep.h"
 #include "bcm_mipsvar.h"
 
@@ -194,6 +196,44 @@ bhnd_nexus_unmap_intr(device_t dev, device_t child, rm
 	intr_unmap_irq(irq);
 }
 
+/**
+ * Default bhnd_nexus implementation of BHND_BUS_GET_DMA_TRANSLATION().
+ */
+static int
+bhnd_nexus_get_dma_translation(device_t dev, device_t child,
+    u_int width, uint32_t flags, bus_dma_tag_t *dmat,
+    struct bhnd_dma_translation *translation)
+{
+	struct bcm_platform *bp = bcm_get_platform();
+
+	/* We don't (currently) support any flags */
+	if (flags != 0x0)
+		return (ENOENT);
+
+	KASSERT(width > 0 && width <= BHND_DMA_ADDR_64BIT,
+	    ("invalid width %u", width));
+
+	if (width > BHND_DMA_ADDR_32BIT) {
+		/* Backplane must support 64-bit addressing */
+		if (!(bp->cc_caps & CHIPC_CAP_BKPLN64))
+			return (ENOENT);
+	}
+
+	/* No DMA address translation required */
+	if (dmat != NULL)
+		*dmat = bus_get_dma_tag(dev);
+
+	if (translation != NULL) {
+		*translation = (struct bhnd_dma_translation) {
+			.base_addr	= 0x0,
+			.addr_mask	= BHND_DMA_ADDR_BITMASK(width),
+			.addrext_mask	= 0
+		};
+	}
+
+	return (0);
+}
+
 static device_method_t bhnd_nexus_methods[] = {
 	/* bhnd interface */
 	DEVMETHOD(bhnd_bus_get_service_registry,bhnd_nexus_get_service_registry),
@@ -206,6 +246,7 @@ static device_method_t bhnd_nexus_methods[] = {
 	DEVMETHOD(bhnd_bus_is_hw_disabled,	bhnd_nexus_is_hw_disabled),
 	DEVMETHOD(bhnd_bus_get_attach_type,	bhnd_nexus_get_attach_type),
 	DEVMETHOD(bhnd_bus_get_chipid,		bhnd_nexus_get_chipid),
+	DEVMETHOD(bhnd_bus_get_dma_translation,	bhnd_nexus_get_dma_translation),
 	DEVMETHOD(bhnd_bus_get_intr_domain,	bhnd_bus_generic_get_intr_domain),
 	DEVMETHOD(bhnd_bus_map_intr,		bhnd_nexus_map_intr),
 	DEVMETHOD(bhnd_bus_unmap_intr,		bhnd_nexus_unmap_intr),


More information about the svn-src-head mailing list