dm: Introduce xxx_get_dma_range()

Add the following functions to get a specific device's DMA ranges:
 - dev_get_dma_range()
 - ofnode_get_dma_range()
 - of_get_dma_range()
 - fdt_get_dma_range()
They are specially useful in oder to be able validate a physical address
space range into a bus's and to convert addresses from and to address
spaces.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
Tested-by: Peter Robinson <pbrobinson@gmail.com>
Signed-off-by: Matthias Brugger <mbrugger@suse.com>
This commit is contained in:
Nicolas Saenz Julienne 2021-01-12 13:55:22 +01:00 committed by Matthias Brugger
parent c709243ee0
commit 51bdb50904
8 changed files with 234 additions and 0 deletions

View File

@ -1344,6 +1344,79 @@ u64 fdt_translate_dma_address(const void *blob, int node_offset,
return __of_translate_address(blob, node_offset, in_addr, "dma-ranges");
}
int fdt_get_dma_range(const void *blob, int node, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size)
{
bool found_dma_ranges = false;
struct of_bus *bus_node;
const fdt32_t *ranges;
int na, ns, pna, pns;
int parent = node;
int ret = 0;
int len;
/* Find the closest dma-ranges property */
while (parent >= 0) {
ranges = fdt_getprop(blob, parent, "dma-ranges", &len);
/* Ignore empty ranges, they imply no translation required */
if (ranges && len > 0)
break;
/* Once we find 'dma-ranges', then a missing one is an error */
if (found_dma_ranges && !ranges) {
ret = -EINVAL;
goto out;
}
if (ranges)
found_dma_ranges = true;
parent = fdt_parent_offset(blob, parent);
}
if (!ranges || parent < 0) {
debug("no dma-ranges found for node %s\n",
fdt_get_name(blob, node, NULL));
ret = -ENOENT;
goto out;
}
/* switch to that node */
node = parent;
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
printf("Found dma-ranges in root node, shoudln't happen\n");
ret = -EINVAL;
goto out;
}
/* Get the address sizes both for the bus and its parent */
bus_node = of_match_bus(blob, node);
bus_node->count_cells(blob, node, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
printf("%s: Bad cell count for %s\n", __FUNCTION__,
fdt_get_name(blob, node, NULL));
return -EINVAL;
goto out;
}
bus_node = of_match_bus(blob, parent);
bus_node->count_cells(blob, parent, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
printf("%s: Bad cell count for %s\n", __FUNCTION__,
fdt_get_name(blob, parent, NULL));
return -EINVAL;
goto out;
}
*bus = fdt_read_number(ranges, na);
*cpu = fdt_translate_dma_address(blob, node, ranges + na);
*size = fdt_read_number(ranges + na + pna, ns);
out:
return ret;
}
/**
* fdt_node_offset_by_compat_reg: Find a node that matches compatiable and
* who's reg property matches a physical cpu address

View File

@ -318,6 +318,84 @@ u64 of_translate_dma_address(const struct device_node *dev, const __be32 *in_add
return __of_translate_address(dev, in_addr, "dma-ranges");
}
int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size)
{
bool found_dma_ranges = false;
struct device_node *parent;
struct of_bus *bus_node;
int na, ns, pna, pns;
const __be32 *ranges;
int ret = 0;
int len;
/* Find the closest dma-ranges property */
dev = of_node_get(dev);
while (dev) {
ranges = of_get_property(dev, "dma-ranges", &len);
/* Ignore empty ranges, they imply no translation required */
if (ranges && len > 0)
break;
/* Once we find 'dma-ranges', then a missing one is an error */
if (found_dma_ranges && !ranges) {
ret = -EINVAL;
goto out;
}
if (ranges)
found_dma_ranges = true;
parent = of_get_parent(dev);
of_node_put(dev);
dev = parent;
}
if (!dev || !ranges) {
debug("no dma-ranges found for node %s\n",
of_node_full_name(dev));
ret = -ENOENT;
goto out;
}
/* switch to that node */
parent = of_get_parent(dev);
if (!parent) {
printf("Found dma-ranges in root node, shoudln't happen\n");
ret = -EINVAL;
goto out;
}
/* Get the address sizes both for the bus and its parent */
bus_node = of_match_bus((struct device_node*)dev);
bus_node->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
printf("Bad cell count for %s\n", of_node_full_name(dev));
return -EINVAL;
goto out_parent;
}
bus_node = of_match_bus(parent);
bus_node->count_cells(parent, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
printf("Bad cell count for %s\n", of_node_full_name(parent));
return -EINVAL;
goto out_parent;
}
*bus = of_read_number(ranges, na);
*cpu = of_translate_dma_address(dev, ranges + na);
*size = of_read_number(ranges + na + pna, ns);
out_parent:
of_node_put(parent);
out:
of_node_put(dev);
return ret;
}
static int __of_address_to_resource(const struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
const char *name, struct resource *r)

View File

@ -927,6 +927,15 @@ u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
}
int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 *size)
{
if (ofnode_is_np(node))
return of_get_dma_range(ofnode_to_np(node), cpu, bus, size);
else
return fdt_get_dma_range(gd->fdt_blob, ofnode_to_offset(node),
cpu, bus, size);
}
int ofnode_device_is_compatible(ofnode node, const char *compat)
{
if (ofnode_is_np(node))

View File

@ -341,6 +341,12 @@ u64 dev_translate_dma_address(const struct udevice *dev, const fdt32_t *in_addr)
return ofnode_translate_dma_address(dev_ofnode(dev), in_addr);
}
int dev_get_dma_range(const struct udevice *dev, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size)
{
return ofnode_get_dma_range(dev_ofnode(dev), cpu, bus, size);
}
int dev_read_alias_highest_id(const char *stem)
{
if (of_live_active())

View File

@ -44,6 +44,23 @@ u64 of_translate_address(const struct device_node *no, const __be32 *in_addr);
*/
u64 of_translate_dma_address(const struct device_node *no, const __be32 *in_addr);
/**
* of_get_dma_range() - get dma-ranges for a specific DT node
*
* Get DMA ranges for a specifc node, this is useful to perform bus->cpu and
* cpu->bus address translations
*
* @param blob Pointer to device tree blob
* @param node_offset Node DT offset
* @param cpu Pointer to variable storing the range's cpu address
* @param bus Pointer to variable storing the range's bus address
* @param size Pointer to variable storing the range's size
* @return translated DMA address or OF_BAD_ADDR on error
*/
int of_get_dma_range(const struct device_node *dev, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size);
/**
* of_get_address() - obtain an address from a node
*

View File

@ -998,6 +998,22 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr);
*/
u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr);
/**
* ofnode_get_dma_range() - get dma-ranges for a specific DT node
*
* Get DMA ranges for a specifc node, this is useful to perform bus->cpu and
* cpu->bus address translations
*
* @param blob Pointer to device tree blob
* @param node_offset Node DT offset
* @param cpu Pointer to variable storing the range's cpu address
* @param bus Pointer to variable storing the range's bus address
* @param size Pointer to variable storing the range's size
* @return translated DMA address or OF_BAD_ADDR on error
*/
int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus,
u64 *size);
/**
* ofnode_device_is_compatible() - check if the node is compatible with compat
*

View File

@ -647,6 +647,21 @@ u64 dev_translate_address(const struct udevice *dev, const fdt32_t *in_addr);
u64 dev_translate_dma_address(const struct udevice *dev,
const fdt32_t *in_addr);
/**
* dev_get_dma_range() - Get a device's DMA constraints
*
* Provide the address bases and size of the linear mapping between the CPU and
* a device's BUS address space.
*
* @dev: device giving the context in which to translate the DMA address
* @cpu: base address for CPU's view of memory
* @bus: base address for BUS's view of memory
* @size: size of the address space
* @return 0 if ok, negative on error
*/
int dev_get_dma_range(const struct udevice *dev, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size);
/**
* dev_read_alias_highest_id - Get highest alias id for the given stem
* @stem: Alias stem to be examined
@ -1005,6 +1020,12 @@ static inline u64 dev_translate_dma_address(const struct udevice *dev,
return ofnode_translate_dma_address(dev_ofnode(dev), in_addr);
}
static inline int dev_get_dma_range(const struct udevice *dev, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size)
{
return ofnode_get_dma_range(dev_ofnode(dev), cpu, bus, size);
}
static inline int dev_read_alias_highest_id(const char *stem)
{
if (!CONFIG_IS_ENABLED(OF_LIBFDT) || !gd->fdt_blob)

View File

@ -260,6 +260,20 @@ u64 fdt_translate_address(const void *blob, int node_offset,
u64 fdt_translate_dma_address(const void *blob, int node_offset,
const __be32 *in_addr);
/**
* Get DMA ranges for a specifc node, this is useful to perform bus->cpu and
* cpu->bus address translations
*
* @param blob Pointer to device tree blob
* @param node_offset Node DT offset
* @param cpu Pointer to variable storing the range's cpu address
* @param bus Pointer to variable storing the range's bus address
* @param size Pointer to variable storing the range's size
* @return translated DMA address or OF_BAD_ADDR on error
*/
int fdt_get_dma_range(const void *blob, int node_offset, phys_addr_t *cpu,
dma_addr_t *bus, u64 *size);
int fdt_node_offset_by_compat_reg(void *blob, const char *compat,
phys_addr_t compat_off);
int fdt_alloc_phandle(void *blob);