Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/bhnd/bhndb/bhndb_subr.c
Show All 35 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/limits.h> | #include <sys/limits.h> | ||||
#include "bhndb_private.h" | #include "bhndb_private.h" | ||||
#include "bhndbvar.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. | * Attach a BHND bridge device to @p parent. | ||||
* | * | ||||
* @param parent A parent PCI device. | * @param parent A parent PCI device. | ||||
* @param[out] bhndb On success, the probed and attached bhndb bridge device. | * @param[out] bhndb On success, the probed and attached bhndb bridge device. | ||||
* @param unit The device unit number, or -1 to select the next available unit | * @param unit The device unit number, or -1 to select the next available unit | ||||
* number. | * number. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 345 Lines • ▼ Show 20 Lines | for (win = cfg->register_windows; | ||||
dwa->rnid = rnid; | dwa->rnid = rnid; | ||||
dwa->target = 0x0; | dwa->target = 0x0; | ||||
LIST_INIT(&dwa->refs); | LIST_INIT(&dwa->refs); | ||||
rnid++; | rnid++; | ||||
} | } | ||||
/* Allocate host resources */ | /* 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) { | if (error) { | ||||
device_printf(r->dev, | device_printf(r->dev, | ||||
"could not allocate host resources on %s: %d\n", | "could not allocate host resources on %s: %d\n", | ||||
device_get_nameunit(parent_dev), error); | device_get_nameunit(parent_dev), error); | ||||
goto failed; | goto failed; | ||||
} | } | ||||
/* Populate (and validate) parent resource references for all | /* Populate (and validate) parent resource references for all | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | if (r->res != NULL) | ||||
bhndb_release_host_resources(r->res); | bhndb_release_host_resources(r->res); | ||||
free(r, M_BHND); | free(r, M_BHND); | ||||
return (NULL); | 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. | * Deallocate the given bridge resource structure and any associated resources. | ||||
* | * | ||||
* @param br Resource state to be deallocated. | * @param br Resource state to be deallocated. | ||||
*/ | */ | ||||
void | void | ||||
bhndb_free_resources(struct bhndb_resources *br) | bhndb_free_resources(struct bhndb_resources *br) | ||||
{ | { | ||||
struct bhndb_region *region, *r_next; | struct bhndb_region *region, *r_next; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
/** | /** | ||||
* Allocate host bus resources defined by @p hwcfg. | * Allocate host bus resources defined by @p hwcfg. | ||||
* | * | ||||
* On success, the caller assumes ownership of the allocated host resources, | * On success, the caller assumes ownership of the allocated host resources, | ||||
* which must be freed via bhndb_release_host_resources(). | * which must be freed via bhndb_release_host_resources(). | ||||
* | * | ||||
* @param dev The device to be used when allocating resources | * @param[out] resources On success, the allocated host resources. | ||||
* (e.g. via bus_alloc_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 | * @param hwcfg The hardware configuration defining the host | ||||
* resources to be allocated | * resources to be allocated | ||||
* @param[out] resources On success, the allocated host resources. | |||||
*/ | */ | ||||
int | int | ||||
bhndb_alloc_host_resources(device_t dev, const struct bhndb_hwcfg *hwcfg, | bhndb_alloc_host_resources(struct bhndb_host_resources **resources, | ||||
struct bhndb_host_resources **resources) | device_t dev, device_t parent_dev, const struct bhndb_hwcfg *hwcfg) | ||||
{ | { | ||||
struct bhndb_host_resources *hr; | struct bhndb_host_resources *hr; | ||||
size_t nres; | const struct bhnd_dma_translation *dt; | ||||
bus_dma_tag_t parent_dmat; | |||||
size_t nres, ndt; | |||||
int error; | int error; | ||||
parent_dmat = bus_get_dma_tag(parent_dev); | |||||
hr = malloc(sizeof(*hr), M_BHND, M_WAITOK); | hr = malloc(sizeof(*hr), M_BHND, M_WAITOK); | ||||
hr->owner = dev; | hr->owner = parent_dev; | ||||
hr->cfg = hwcfg; | hr->cfg = hwcfg; | ||||
hr->resource_specs = NULL; | hr->resource_specs = NULL; | ||||
hr->resources = NULL; | hr->resources = NULL; | ||||
hr->dma_tags = NULL; | |||||
hr->num_dma_tags = 0; | |||||
/* Determine our bridge resource count from the hardware config. */ | /* Determine our bridge resource count from the hardware config. */ | ||||
nres = 0; | nres = 0; | ||||
for (size_t i = 0; hwcfg->resource_specs[i].type != -1; i++) | for (size_t i = 0; hwcfg->resource_specs[i].type != -1; i++) | ||||
nres++; | 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 | /* Allocate space for a non-const copy of our resource_spec | ||||
* table; this will be updated with the RIDs assigned by | * table; this will be updated with the RIDs assigned by | ||||
* bus_alloc_resources. */ | * bus_alloc_resources. */ | ||||
hr->resource_specs = malloc(sizeof(hr->resource_specs[0]) * (nres + 1), | hr->resource_specs = malloc(sizeof(hr->resource_specs[0]) * (nres + 1), | ||||
M_BHND, M_WAITOK); | M_BHND, M_WAITOK); | ||||
/* Initialize and terminate the table */ | /* Initialize and terminate the table */ | ||||
for (size_t i = 0; i < nres; i++) | for (size_t i = 0; i < nres; i++) | ||||
hr->resource_specs[i] = hwcfg->resource_specs[i]; | hr->resource_specs[i] = hwcfg->resource_specs[i]; | ||||
hr->resource_specs[nres].type = -1; | hr->resource_specs[nres].type = -1; | ||||
/* Allocate space for our resource references */ | /* Allocate space for our resource references */ | ||||
hr->resources = malloc(sizeof(hr->resources[0]) * nres, M_BHND, | hr->resources = malloc(sizeof(hr->resources[0]) * nres, M_BHND, | ||||
M_WAITOK); | M_WAITOK); | ||||
/* Allocate host resources */ | /* Allocate host resources */ | ||||
error = bus_alloc_resources(hr->owner, hr->resource_specs, | error = bus_alloc_resources(hr->owner, hr->resource_specs, | ||||
hr->resources); | hr->resources); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "could not allocate bridge resources via " | 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; | goto failed; | ||||
} | } | ||||
*resources = hr; | *resources = hr; | ||||
return (0); | return (0); | ||||
failed: | failed: | ||||
if (hr->resource_specs != NULL) | if (hr->resource_specs != NULL) | ||||
free(hr->resource_specs, M_BHND); | free(hr->resource_specs, M_BHND); | ||||
if (hr->resources != NULL) | if (hr->resources != NULL) | ||||
free(hr->resources, M_BHND); | 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); | free(hr, M_BHND); | ||||
return (error); | return (error); | ||||
} | } | ||||
/** | /** | ||||
* Deallocate a set of bridge host resources. | * Deallocate a set of bridge host resources. | ||||
* | * | ||||
* @param hr The resources to be freed. | * @param hr The resources to be freed. | ||||
*/ | */ | ||||
void | void | ||||
bhndb_release_host_resources(struct bhndb_host_resources *hr) | bhndb_release_host_resources(struct bhndb_host_resources *hr) | ||||
{ | { | ||||
bus_release_resources(hr->owner, hr->resource_specs, hr->resources); | 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->resources, M_BHND); | ||||
free(hr->resource_specs, M_BHND); | free(hr->resource_specs, M_BHND); | ||||
free(hr->dma_tags, M_BHND); | |||||
free(hr, M_BHND); | free(hr, M_BHND); | ||||
} | } | ||||
/** | /** | ||||
* Search @p cores for the core serving as the bhnd host bridge. | * Search @p cores for the core serving as the bhnd host bridge. | ||||
* | * | ||||
* This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged | * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged | ||||
▲ Show 20 Lines • Show All 721 Lines • Show Last 20 Lines |