summaryrefslogtreecommitdiff
path: root/drivers/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/core')
-rw-r--r--drivers/core/Kconfig10
-rw-r--r--drivers/core/device-remove.c97
-rw-r--r--drivers/core/device.c42
-rw-r--r--drivers/core/fdtaddr.c1
-rw-r--r--drivers/core/of_access.c1
-rw-r--r--drivers/core/of_addr.c78
-rw-r--r--drivers/core/ofnode.c10
-rw-r--r--drivers/core/read.c7
-rw-r--r--drivers/core/regmap.c1
-rw-r--r--drivers/core/root.c3
-rw-r--r--drivers/core/uclass.c1
11 files changed, 222 insertions, 29 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-remove.c b/drivers/core/device-remove.c
index 7e8f3afb2d6..11d3959d20f 100644
--- a/drivers/core/device-remove.c
+++ b/drivers/core/device-remove.c
@@ -8,6 +8,8 @@
* Pavel Herrmann <morpheus.ibis@gmail.com>
*/
+#define LOG_CATEGORY LOGC_DM
+
#include <common.h>
#include <errno.h>
#include <log.h>
@@ -18,6 +20,7 @@
#include <dm/uclass-internal.h>
#include <dm/util.h>
#include <power-domain.h>
+#include <asm/global_data.h>
int device_chld_unbind(struct udevice *dev, struct driver *drv)
{
@@ -45,20 +48,24 @@ int device_chld_remove(struct udevice *dev, struct driver *drv,
uint flags)
{
struct udevice *pos, *n;
- int ret;
+ int result = 0;
assert(dev);
list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+ int ret;
+
if (drv && (pos->driver != drv))
continue;
ret = device_remove(pos, flags);
- if (ret)
+ if (ret == -EPROBE_DEFER)
+ result = ret;
+ else if (ret && ret != -EKEYREJECTED)
return ret;
}
- return 0;
+ return result;
}
int device_unbind(struct udevice *dev)
@@ -149,13 +156,43 @@ void device_free(struct udevice *dev)
devres_release_probe(dev);
}
-static bool flags_remove(uint flags, uint drv_flags)
+/**
+ * flags_remove() - Figure out whether to remove a device
+ *
+ * If this is called with @flags == DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_DMA,
+ * then it returns 0 (=go head and remove) if the device is not matked vital
+ * but is marked DM_REMOVE_ACTIVE_DMA.
+ *
+ * If this is called with @flags == DM_REMOVE_ACTIVE_DMA,
+ * then it returns 0 (=go head and remove) if the device is marked
+ * DM_REMOVE_ACTIVE_DMA, regardless of whether it is marked vital.
+ *
+ * @flags: Flags passed to device_remove()
+ * @drv_flags: Driver flags
+ * @return 0 if the device should be removed,
+ * -EKEYREJECTED if @flags includes a flag in DM_REMOVE_ACTIVE_ALL but
+ * @drv_flags does not (indicates that this device has nothing to do for
+ * DMA shutdown or OS prepare)
+ * -EPROBE_DEFER if @flags is DM_REMOVE_NON_VITAL but @drv_flags contains
+ * DM_FLAG_VITAL (indicates the device is vital and should not be removed)
+ */
+static int flags_remove(uint flags, uint drv_flags)
{
- if ((flags & DM_REMOVE_NORMAL) ||
- (flags && (drv_flags & (DM_FLAG_ACTIVE_DMA | DM_FLAG_OS_PREPARE))))
- return true;
+ if (!(flags & DM_REMOVE_NORMAL)) {
+ bool vital_match;
+ bool active_match;
+
+ active_match = !(flags & DM_REMOVE_ACTIVE_ALL) ||
+ (drv_flags & flags);
+ vital_match = !(flags & DM_REMOVE_NON_VITAL) ||
+ !(drv_flags & DM_FLAG_VITAL);
+ if (!vital_match)
+ return -EPROBE_DEFER;
+ if (!active_match)
+ return -EKEYREJECTED;
+ }
- return false;
+ return 0;
}
int device_remove(struct udevice *dev, uint flags)
@@ -169,22 +206,32 @@ int device_remove(struct udevice *dev, uint flags)
if (!(dev_get_flags(dev) & DM_FLAG_ACTIVATED))
return 0;
+ /*
+ * If the child returns EKEYREJECTED, continue. It just means that it
+ * didn't match the flags.
+ */
+ ret = device_chld_remove(dev, NULL, flags);
+ if (ret && ret != -EKEYREJECTED)
+ return ret;
+
+ /*
+ * Remove the device if called with the "normal" remove flag set,
+ * or if the remove flag matches any of the drivers remove flags
+ */
drv = dev->driver;
assert(drv);
+ ret = flags_remove(flags, drv->flags);
+ if (ret) {
+ log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n",
+ dev->name, flags, drv->flags, ret);
+ return ret;
+ }
ret = uclass_pre_remove_device(dev);
if (ret)
return ret;
- ret = device_chld_remove(dev, NULL, flags);
- if (ret)
- goto err;
-
- /*
- * Remove the device if called with the "normal" remove flag set,
- * or if the remove flag matches any of the drivers remove flags
- */
- if (drv->remove && flags_remove(flags, drv->flags)) {
+ if (drv->remove) {
ret = drv->remove(dev);
if (ret)
goto err_remove;
@@ -200,28 +247,20 @@ int device_remove(struct udevice *dev, uint flags)
if (!(flags & DM_REMOVE_NO_PD) &&
!(drv->flags &
- (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_REMOVE_WITH_PD_ON)) &&
+ (DM_FLAG_DEFAULT_PD_CTRL_OFF | DM_FLAG_LEAVE_PD_ON)) &&
dev != gd->cur_serial_dev)
dev_power_domain_off(dev);
- if (flags_remove(flags, drv->flags)) {
- device_free(dev);
+ device_free(dev);
- dev_bic_flags(dev, DM_FLAG_ACTIVATED);
- }
+ dev_bic_flags(dev, DM_FLAG_ACTIVATED);
- return ret;
+ return 0;
err_remove:
/* We can't put the children back */
dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
__func__, dev->name);
-err:
- ret = uclass_post_probe_device(dev);
- if (ret) {
- dm_warn("%s: Device '%s' failed to post_probe on error path\n",
- __func__, dev->name);
- }
return ret;
}
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 8629df8defb..625134921d6 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -11,6 +11,7 @@
#include <common.h>
#include <cpu_func.h>
#include <log.h>
+#include <asm/global_data.h>
#include <asm/io.h>
#include <clk.h>
#include <fdtdec.h>
@@ -421,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;
@@ -483,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/fdtaddr.c b/drivers/core/fdtaddr.c
index ed55f69de12..83a50b6a3a7 100644
--- a/drivers/core/fdtaddr.c
+++ b/drivers/core/fdtaddr.c
@@ -12,6 +12,7 @@
#include <dm.h>
#include <fdt_support.h>
#include <log.h>
+#include <asm/global_data.h>
#include <asm/io.h>
#include <dm/device-internal.h>
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index 0a12e9b26f8..9960e6b310b 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -22,6 +22,7 @@
#include <common.h>
#include <log.h>
#include <malloc.h>
+#include <asm/global_data.h>
#include <linux/bug.h>
#include <linux/libfdt.h>
#include <dm/of_access.h>
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 7a5f4c0a73d..fa0bd2a9c44 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -16,6 +16,7 @@
#include <dm/ofnode.h>
#include <linux/err.h>
#include <linux/ioport.h>
+#include <asm/global_data.h>
int ofnode_read_u32(ofnode node, const char *propname, u32 *outp)
{
@@ -926,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 4d9b5dd0384..4307ca45799 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <dm/of_access.h>
#include <mapmem.h>
+#include <asm/global_data.h>
#include <asm/types.h>
#include <asm/io.h>
#include <linux/ioport.h>
@@ -340,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())
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c
index 4baacabd015..b51ce108c14 100644
--- a/drivers/core/regmap.c
+++ b/drivers/core/regmap.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <errno.h>
#include <log.h>
+#include <asm/global_data.h>
#include <linux/libfdt.h>
#include <malloc.h>
#include <mapmem.h>
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 2bfa75b4725..9bc682cffea 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -11,6 +11,7 @@
#include <fdtdec.h>
#include <log.h>
#include <malloc.h>
+#include <asm/global_data.h>
#include <linux/libfdt.h>
#include <dm/acpi.h>
#include <dm/device.h>
@@ -162,6 +163,8 @@ int dm_init(bool of_live)
int dm_uninit(void)
{
+ /* Remove non-vital devices first */
+ device_remove(dm_root(), DM_REMOVE_NON_VITAL);
device_remove(dm_root(), DM_REMOVE_NORMAL);
device_unbind(dm_root());
gd->dm_root = NULL;
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index f38122d54b5..1a4ea7a57a6 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -13,6 +13,7 @@
#include <errno.h>
#include <log.h>
#include <malloc.h>
+#include <asm/global_data.h>
#include <dm/device.h>
#include <dm/device-internal.h>
#include <dm/lists.h>