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
This commit is contained in:
parent
caeff9a3c2
commit
9ed453245b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=326080
@ -219,6 +219,94 @@ struct bhnd_core_info {
|
||||
int unit; /**< bus-assigned core unit */
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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);
|
||||
@ -842,6 +934,38 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to read the BHND board identification from the bhnd bus.
|
||||
*
|
||||
|
@ -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)
|
||||
@ -479,6 +480,41 @@ METHOD bhnd_attach_type get_attach_type {
|
||||
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.
|
||||
*
|
||||
|
@ -2104,6 +2104,27 @@ bhnd_bus_generic_get_chipid(device_t dev, device_t child)
|
||||
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, \
|
||||
|
@ -2039,14 +2039,94 @@ bhndb_remap_intr(device_t dev, device_t child, u_int irq)
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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),
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -509,7 +509,7 @@ bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid,
|
||||
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 */
|
||||
|
@ -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_pcie = {
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
|
@ -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_dev,
|
||||
}
|
||||
|
||||
/* 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",
|
||||
@ -493,6 +497,65 @@ bhndb_alloc_resources(device_t dev, device_t parent_dev,
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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 bhndb_hwcfg *hwcfg,
|
||||
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 @@ bhndb_alloc_host_resources(device_t dev, const struct bhndb_hwcfg *hwcfg,
|
||||
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_resources *hr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -90,9 +90,11 @@ struct bhndb_intr_isrc *bhndb_alloc_intr_isrc(device_t owner, int rid,
|
||||
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 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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_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_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_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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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, rman_res_t irq)
|
||||
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),
|
||||
|
Loading…
Reference in New Issue
Block a user