diff options
Diffstat (limited to 'drivers/core')
| -rw-r--r-- | drivers/core/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/core/device.c | 41 | ||||
| -rw-r--r-- | drivers/core/of_addr.c | 78 | ||||
| -rw-r--r-- | drivers/core/ofnode.c | 9 | ||||
| -rw-r--r-- | drivers/core/read.c | 6 | 
5 files changed, 144 insertions, 0 deletions
| diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig index dbfe51c6e87..00554af4995 100644 --- a/drivers/core/Kconfig +++ b/drivers/core/Kconfig @@ -129,6 +129,16 @@ config TPL_DM_INLINE_OFNODE  	  This applies to several ofnode functions (see ofnode.h) which are  	  seldom used. Inlining them can help reduce code size. +config DM_DMA +	bool "Support per-device DMA constraints" +	depends on DM +	default n +	help +	  Enable this to extract per-device DMA constraints, only supported on +	  device-tree systems for now. This is needed in order translate +	  addresses on systems where different buses have different views of +	  the physical address space. +  config REGMAP  	bool "Support register maps"  	depends on DM diff --git a/drivers/core/device.c b/drivers/core/device.c index 82a00989604..625134921d6 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -422,6 +422,43 @@ fail:  	return ret;  } +/** + * device_get_dma_constraints() - Populate device's DMA constraints + * + * Gets a device's DMA constraints from firmware. This information is later + * used by drivers to translate physcal addresses to the device's bus address + * space. For now only device-tree is supported. + * + * @dev: Pointer to target device + * Return: 0 if OK or if no DMA constraints were found, error otherwise + */ +static int device_get_dma_constraints(struct udevice *dev) +{ +	struct udevice *parent = dev->parent; +	phys_addr_t cpu = 0; +	dma_addr_t bus = 0; +	u64 size = 0; +	int ret; + +	if (!CONFIG_IS_ENABLED(DM_DMA) || !parent || !dev_has_ofnode(parent)) +		return 0; + +	/* +	 * We start parsing for dma-ranges from the device's bus node. This is +	 * specially important on nested buses. +	 */ +	ret = dev_get_dma_range(parent, &cpu, &bus, &size); +	/* Don't return an error if no 'dma-ranges' were found */ +	if (ret && ret != -ENOENT) { +		dm_warn("%s: failed to get DMA range, %d\n", dev->name, ret); +		return ret; +	} + +	dev_set_dma_offset(dev, cpu - bus); + +	return 0; +} +  int device_probe(struct udevice *dev)  {  	const struct driver *drv; @@ -484,6 +521,10 @@ int device_probe(struct udevice *dev)  			goto fail;  	} +	ret = device_get_dma_constraints(dev); +	if (ret) +		goto fail; +  	ret = uclass_pre_probe_device(dev);  	if (ret)  		goto fail; diff --git a/drivers/core/of_addr.c b/drivers/core/of_addr.c index bbe80136ba8..5bc6ca1de01 100644 --- a/drivers/core/of_addr.c +++ b/drivers/core/of_addr.c @@ -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) diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 26c9d040564..fa0bd2a9c44 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -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)) diff --git a/drivers/core/read.c b/drivers/core/read.c index 14fd1214d62..4307ca45799 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -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()) | 
