Browse Source

Merge tag 'dm-pull-8jan20' of git://git.denx.de/u-boot-dm

dm: Increased separation of ofdata_to_platdata() and probe methods
Tom Rini 4 years ago
parent
commit
d6b92b9742

+ 4 - 0
arch/sandbox/dts/test.dts

@@ -201,6 +201,10 @@
 		compatible = "denx,u-boot-fdt-test1";
 	};
 
+	devres-test {
+		compatible = "denx,u-boot-devres-test";
+	};
+
 	clocks {
 		clk_fixed: clk-fixed {
 			compatible = "fixed-clock";

+ 12 - 8
arch/x86/cpu/apollolake/p2sb.c

@@ -106,11 +106,6 @@ int apl_p2sb_ofdata_to_platdata(struct udevice *dev)
 		if (plat->bdf < 0)
 			return log_msg_ret("Cannot get p2sb PCI address",
 					   plat->bdf);
-	} else {
-		plat->mmio_base = dev_read_addr_pci(dev);
-		/* Don't set BDF since it should not be used */
-		if (!plat->mmio_base || plat->mmio_base == FDT_ADDR_T_NONE)
-			return -EINVAL;
 	}
 #else
 	plat->mmio_base = plat->dtplat.early_regs[0];
@@ -124,10 +119,19 @@ int apl_p2sb_ofdata_to_platdata(struct udevice *dev)
 
 static int apl_p2sb_probe(struct udevice *dev)
 {
-	if (spl_phase() == PHASE_TPL)
+	if (spl_phase() == PHASE_TPL) {
 		return apl_p2sb_early_init(dev);
-	else if (spl_phase() == PHASE_SPL)
-		return apl_p2sb_spl_init(dev);
+	} else {
+		struct p2sb_platdata *plat = dev_get_platdata(dev);
+
+		plat->mmio_base = dev_read_addr_pci(dev);
+		/* Don't set BDF since it should not be used */
+		if (!plat->mmio_base || plat->mmio_base == FDT_ADDR_T_NONE)
+			return -EINVAL;
+
+		if (spl_phase() == PHASE_SPL)
+			return apl_p2sb_spl_init(dev);
+	}
 
 	return 0;
 }

+ 17 - 3
arch/x86/cpu/apollolake/pmc.c

@@ -119,8 +119,16 @@ int apl_pmc_ofdata_to_uc_platdata(struct udevice *dev)
 	ret = dev_read_u32_array(dev, "early-regs", base, ARRAY_SIZE(base));
 	if (ret)
 		return log_msg_ret("Missing/short early-regs", ret);
-	upriv->pmc_bar0 = (void *)base[0];
-	upriv->pmc_bar2 = (void *)base[2];
+	if (spl_phase() == PHASE_TPL) {
+		upriv->pmc_bar0 = (void *)base[0];
+		upriv->pmc_bar2 = (void *)base[2];
+
+		/* Since PCI is not enabled, we must get the BDF manually */
+		plat->bdf = pci_get_devfn(dev);
+		if (plat->bdf < 0)
+			return log_msg_ret("Cannot get PMC PCI address",
+					   plat->bdf);
+	}
 	upriv->acpi_base = base[4];
 
 	/* Since PCI is not enabled, we must get the BDF manually */
@@ -187,8 +195,14 @@ static int enable_pmcbar(struct udevice *dev)
 
 static int apl_pmc_probe(struct udevice *dev)
 {
-	if (spl_phase() == PHASE_TPL)
+	if (spl_phase() == PHASE_TPL) {
 		return enable_pmcbar(dev);
+	} else {
+		struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev);
+
+		upriv->pmc_bar0 = (void *)dm_pci_read_bar32(dev, 0);
+		upriv->pmc_bar2 = (void *)dm_pci_read_bar32(dev, 2);
+	}
 
 	return 0;
 }

+ 2 - 2
drivers/clk/aspeed/clk_ast2500.c

@@ -490,7 +490,7 @@ struct clk_ops ast2500_clk_ops = {
 	.enable = ast2500_clk_enable,
 };
 
-static int ast2500_clk_probe(struct udevice *dev)
+static int ast2500_clk_ofdata_to_platdata(struct udevice *dev)
 {
 	struct ast2500_clk_priv *priv = dev_get_priv(dev);
 
@@ -525,5 +525,5 @@ U_BOOT_DRIVER(aspeed_ast2500_scu) = {
 	.priv_auto_alloc_size = sizeof(struct ast2500_clk_priv),
 	.ops		= &ast2500_clk_ops,
 	.bind		= ast2500_clk_bind,
-	.probe		= ast2500_clk_probe,
+	.ofdata_to_platdata		= ast2500_clk_ofdata_to_platdata,
 };

+ 1 - 0
drivers/core/device-remove.c

@@ -140,6 +140,7 @@ void device_free(struct udevice *dev)
 			dev->parent_priv = NULL;
 		}
 	}
+	dev->flags &= ~DM_FLAG_PLATDATA_VALID;
 
 	devres_release_probe(dev);
 }

+ 42 - 14
drivers/core/device.c

@@ -311,17 +311,16 @@ static void *alloc_priv(int size, uint flags)
 	return priv;
 }
 
-int device_probe(struct udevice *dev)
+int device_ofdata_to_platdata(struct udevice *dev)
 {
 	const struct driver *drv;
 	int size = 0;
 	int ret;
-	int seq;
 
 	if (!dev)
 		return -EINVAL;
 
-	if (dev->flags & DM_FLAG_ACTIVATED)
+	if (dev->flags & DM_FLAG_PLATDATA_VALID)
 		return 0;
 
 	drv = dev->driver;
@@ -346,7 +345,7 @@ int device_probe(struct udevice *dev)
 		}
 	}
 
-	/* Ensure all parents are probed */
+	/* Allocate parent data for this child */
 	if (dev->parent) {
 		size = dev->parent->driver->per_child_auto_alloc_size;
 		if (!size) {
@@ -360,7 +359,45 @@ int device_probe(struct udevice *dev)
 				goto fail;
 			}
 		}
+	}
+
+	if (drv->ofdata_to_platdata &&
+	    (CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_of_node(dev))) {
+		ret = drv->ofdata_to_platdata(dev);
+		if (ret)
+			goto fail;
+	}
+
+	dev->flags |= DM_FLAG_PLATDATA_VALID;
 
+	return 0;
+fail:
+	device_free(dev);
+
+	return ret;
+}
+
+int device_probe(struct udevice *dev)
+{
+	const struct driver *drv;
+	int ret;
+	int seq;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return 0;
+
+	drv = dev->driver;
+	assert(drv);
+
+	ret = device_ofdata_to_platdata(dev);
+	if (ret)
+		goto fail;
+
+	/* Ensure all parents are probed */
+	if (dev->parent) {
 		ret = device_probe(dev->parent);
 		if (ret)
 			goto fail;
@@ -411,13 +448,6 @@ int device_probe(struct udevice *dev)
 			goto fail;
 	}
 
-	if (drv->ofdata_to_platdata &&
-	    (CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_of_node(dev))) {
-		ret = drv->ofdata_to_platdata(dev);
-		if (ret)
-			goto fail;
-	}
-
 	/* Only handle devices that have a valid ofnode */
 	if (dev_of_valid(dev)) {
 		/*
@@ -431,10 +461,8 @@ int device_probe(struct udevice *dev)
 
 	if (drv->probe) {
 		ret = drv->probe(dev);
-		if (ret) {
-			dev->flags &= ~DM_FLAG_ACTIVATED;
+		if (ret)
 			goto fail;
-		}
 	}
 
 	ret = uclass_post_probe_device(dev);

+ 45 - 12
drivers/core/devres.c

@@ -7,6 +7,8 @@
  * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
  */
 
+#define LOG_CATEGORY LOGC_DEVRES
+
 #include <common.h>
 #include <linux/compat.h>
 #include <linux/kernel.h>
@@ -15,12 +17,23 @@
 #include <dm/root.h>
 #include <dm/util.h>
 
+/** enum devres_phase - Shows where resource was allocated
+ *
+ * DEVRES_PHASE_BIND: In the bind() method
+ * DEVRES_PHASE_OFDATA: In the ofdata_to_platdata() method
+ * DEVRES_PHASE_PROBE: In the probe() method
+ */
+enum devres_phase {
+	DEVRES_PHASE_BIND,
+	DEVRES_PHASE_OFDATA,
+	DEVRES_PHASE_PROBE,
+};
+
 /**
  * struct devres - Bookkeeping info for managed device resource
  * @entry: List to associate this structure with a device
  * @release: Callback invoked when this resource is released
- * @probe: Flag to show when this resource was allocated
-	   (true = probe, false = bind)
+ * @probe: Show where this resource was allocated
  * @name: Name of release function
  * @size: Size of resource data
  * @data: Resource data
@@ -28,7 +41,7 @@
 struct devres {
 	struct list_head		entry;
 	dr_release_t			release;
-	bool				probe;
+	enum devres_phase		phase;
 #ifdef CONFIG_DEBUG_DEVRES
 	const char			*name;
 	size_t				size;
@@ -46,8 +59,8 @@ static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
 static void devres_log(struct udevice *dev, struct devres *dr,
 		       const char *op)
 {
-	printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
-	       dev->name, op, dr, dr->name, (unsigned long)dr->size);
+	log_debug("%s: DEVRES %3s %p %s (%lu bytes)\n", dev->name, op, dr,
+		  dr->name, (unsigned long)dr->size);
 }
 #else /* CONFIG_DEBUG_DEVRES */
 #define set_node_dbginfo(dr, n, s)	do {} while (0)
@@ -80,7 +93,7 @@ void devres_free(void *res)
 	if (res) {
 		struct devres *dr = container_of(res, struct devres, data);
 
-		BUG_ON(!list_empty(&dr->entry));
+		assert_noisy(list_empty(&dr->entry));
 		kfree(dr);
 	}
 }
@@ -90,8 +103,13 @@ void devres_add(struct udevice *dev, void *res)
 	struct devres *dr = container_of(res, struct devres, data);
 
 	devres_log(dev, dr, "ADD");
-	BUG_ON(!list_empty(&dr->entry));
-	dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
+	assert_noisy(list_empty(&dr->entry));
+	if (dev->flags & DM_FLAG_PLATDATA_VALID)
+		dr->phase = DEVRES_PHASE_PROBE;
+	else if (dev->flags & DM_FLAG_BOUND)
+		dr->phase = DEVRES_PHASE_OFDATA;
+	else
+		dr->phase = DEVRES_PHASE_BIND;
 	list_add_tail(&dr->entry, &dev->devres_head);
 }
 
@@ -172,12 +190,12 @@ int devres_release(struct udevice *dev, dr_release_t release,
 }
 
 static void release_nodes(struct udevice *dev, struct list_head *head,
-			  bool probe_only)
+			  bool probe_and_ofdata_only)
 {
 	struct devres *dr, *tmp;
 
 	list_for_each_entry_safe_reverse(dr, tmp, head, entry)  {
-		if (probe_only && !dr->probe)
+		if (probe_and_ofdata_only && dr->phase == DEVRES_PHASE_BIND)
 			break;
 		devres_log(dev, dr, "REL");
 		dr->release(dev, dr->data);
@@ -197,6 +215,8 @@ void devres_release_all(struct udevice *dev)
 }
 
 #ifdef CONFIG_DEBUG_DEVRES
+static char *const devres_phase_name[] = {"BIND", "OFDATA", "PROBE"};
+
 static void dump_resources(struct udevice *dev, int depth)
 {
 	struct devres *dr;
@@ -207,7 +227,7 @@ static void dump_resources(struct udevice *dev, int depth)
 	list_for_each_entry(dr, &dev->devres_head, entry)
 		printf("    %p (%lu byte) %s  %s\n", dr,
 		       (unsigned long)dr->size, dr->name,
-		       dr->probe ? "PROBE" : "BIND");
+		       devres_phase_name[dr->phase]);
 
 	list_for_each_entry(child, &dev->child_head, sibling_node)
 		dump_resources(child, depth + 1);
@@ -221,6 +241,19 @@ void dm_dump_devres(void)
 	if (root)
 		dump_resources(root, 0);
 }
+
+void devres_get_stats(const struct udevice *dev, struct devres_stats *stats)
+{
+	struct devres *dr;
+
+	stats->allocs = 0;
+	stats->total_size = 0;
+	list_for_each_entry(dr, &dev->devres_head, entry) {
+		stats->allocs++;
+		stats->total_size += dr->size;
+	}
+}
+
 #endif
 
 /*
@@ -254,5 +287,5 @@ void devm_kfree(struct udevice *dev, void *p)
 	int rc;
 
 	rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
-	WARN_ON(rc);
+	assert_noisy(!rc);
 }

+ 3 - 1
drivers/core/lists.c

@@ -176,8 +176,10 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
 
 		if (pre_reloc_only) {
 			if (!dm_ofnode_pre_reloc(node) &&
-			    !(entry->flags & DM_FLAG_PRE_RELOC))
+			    !(entry->flags & DM_FLAG_PRE_RELOC)) {
+				log_debug("Skipping device pre-relocation\n");
 				return 0;
+			}
 		}
 
 		log_debug("   - found match at '%s': '%s' matches '%s'\n",

+ 13 - 0
drivers/pci/pci-uclass.c

@@ -48,6 +48,19 @@ pci_dev_t dm_pci_get_bdf(struct udevice *dev)
 	struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
 	struct udevice *bus = dev->parent;
 
+	/*
+	 * This error indicates that @dev is a device on an unprobed PCI bus.
+	 * The bus likely has bus=seq == -1, so the PCI_ADD_BUS() macro below
+	 * will produce a bad BDF>
+	 *
+	 * A common cause of this problem is that this function is called in the
+	 * ofdata_to_platdata() method of @dev. Accessing the PCI bus in that
+	 * method is not allowed, since it has not yet been probed. To fix this,
+	 * move that access to the probe() method of @dev instead.
+	 */
+	if (!device_active(bus))
+		log_err("PCI: Device '%s' on unprobed bus '%s'\n", dev->name,
+			bus->name);
 	return PCI_ADD_BUS(bus->seq, pplat->devfn);
 }
 

+ 4 - 0
drivers/usb/gadget/composite.c

@@ -1003,7 +1003,11 @@ static void composite_unbind(struct usb_gadget *gadget)
 	 * so there's no i/o concurrency that could affect the
 	 * state protected by cdev->lock.
 	 */
+#ifdef __UBOOT__
+	assert_noisy(!cdev->config);
+#else
 	BUG_ON(cdev->config);
+#endif
 
 	while (!list_empty(&cdev->configs)) {
 		c = list_first_entry(&cdev->configs,

+ 4 - 0
drivers/usb/gadget/f_mass_storage.c

@@ -390,7 +390,11 @@ static inline int __fsg_is_set(struct fsg_common *common,
 	if (common->fsg)
 		return 1;
 	ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+#ifdef __UBOOT__
+	assert_noisy(false);
+#else
 	WARN_ON(1);
+#endif
 	return 0;
 }
 

+ 4 - 0
drivers/usb/musb-new/musb_core.c

@@ -1859,7 +1859,11 @@ allocate_instance(struct device *dev,
 	musb->ctrl_base = mbase;
 	musb->nIrq = -ENODEV;
 	musb->config = config;
+#ifdef __UBOOT__
+	assert_noisy(musb->config->num_eps <= MUSB_C_NUM_EPS);
+#else
 	BUG_ON(musb->config->num_eps > MUSB_C_NUM_EPS);
+#endif
 	for (epnum = 0, ep = musb->endpoints;
 			epnum < musb->config->num_eps;
 			epnum++, ep++) {

+ 1 - 1
drivers/usb/musb-new/musb_gadget_ep0.c

@@ -882,7 +882,7 @@ finish:
 
 	default:
 		/* "can't happen" */
-		WARN_ON(1);
+		assert_noisy(false);
 		musb_writew(regs, MUSB_CSR0, MUSB_CSR0_P_SENDSTALL);
 		musb->ep0_state = MUSB_EP0_STAGE_IDLE;
 		break;

+ 16 - 0
include/dm/device-internal.h

@@ -83,6 +83,22 @@ int device_bind_with_driver_data(struct udevice *parent,
 int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			const struct driver_info *info, struct udevice **devp);
 
+/**
+ * device_ofdata_to_platdata() - Read platform data for a device
+ *
+ * Read platform data for a device (typically from the device tree) so that
+ * the information needed to probe the device is present.
+ *
+ * This may cause some others devices to be probed if this one depends on them,
+ * e.g. a GPIO line will cause a GPIO device to be probed.
+ *
+ * All private data associated with the device is allocated.
+ *
+ * @dev: Pointer to device to process
+ * @return 0 if OK, -ve on error
+ */
+int device_ofdata_to_platdata(struct udevice *dev);
+
 /**
  * device_probe() - Probe a device, activating it
  *

+ 5 - 254
include/dm/device.h

@@ -45,6 +45,7 @@ struct driver_info;
 /* Device name is allocated and should be freed on unbind() */
 #define DM_FLAG_NAME_ALLOCED		(1 << 7)
 
+/* Device has platform data provided by of-platdata */
 #define DM_FLAG_OF_PLATDATA		(1 << 8)
 
 /*
@@ -64,6 +65,9 @@ struct driver_info;
 /* DM does not enable/disable the power domains corresponding to this device */
 #define DM_FLAG_DEFAULT_PD_CTRL_OFF	(1 << 11)
 
+/* Driver platdata has been read. Cleared when the device is removed */
+#define DM_FLAG_PLATDATA_VALID		(1 << 12)
+
 /*
  * One or multiple of these flags are passed to device_remove() so that
  * a selective device removal as specified by the remove-stage and the
@@ -716,260 +720,7 @@ static inline bool device_is_on_pci_bus(struct udevice *dev)
  */
 int dm_scan_fdt_dev(struct udevice *dev);
 
-/* device resource management */
-typedef void (*dr_release_t)(struct udevice *dev, void *res);
-typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data);
-
-#ifdef CONFIG_DEVRES
-
-#ifdef CONFIG_DEBUG_DEVRES
-void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
-		     const char *name);
-#define _devres_alloc(release, size, gfp) \
-	__devres_alloc(release, size, gfp, #release)
-#else
-void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
-#endif
-
-/**
- * devres_alloc() - Allocate device resource data
- * @release: Release function devres will be associated with
- * @size: Allocation size
- * @gfp: Allocation flags
- *
- * Allocate devres of @size bytes.  The allocated area is associated
- * with @release.  The returned pointer can be passed to
- * other devres_*() functions.
- *
- * RETURNS:
- * Pointer to allocated devres on success, NULL on failure.
- */
-#define devres_alloc(release, size, gfp) \
-	_devres_alloc(release, size, gfp | __GFP_ZERO)
-
-/**
- * devres_free() - Free device resource data
- * @res: Pointer to devres data to free
- *
- * Free devres created with devres_alloc().
- */
-void devres_free(void *res);
-
-/**
- * devres_add() - Register device resource
- * @dev: Device to add resource to
- * @res: Resource to register
- *
- * Register devres @res to @dev.  @res should have been allocated
- * using devres_alloc().  On driver detach, the associated release
- * function will be invoked and devres will be freed automatically.
- */
-void devres_add(struct udevice *dev, void *res);
-
-/**
- * devres_find() - Find device resource
- * @dev: Device to lookup resource from
- * @release: Look for resources associated with this release function
- * @match: Match function (optional)
- * @match_data: Data for the match function
- *
- * Find the latest devres of @dev which is associated with @release
- * and for which @match returns 1.  If @match is NULL, it's considered
- * to match all.
- *
- * @return pointer to found devres, NULL if not found.
- */
-void *devres_find(struct udevice *dev, dr_release_t release,
-		  dr_match_t match, void *match_data);
-
-/**
- * devres_get() - Find devres, if non-existent, add one atomically
- * @dev: Device to lookup or add devres for
- * @new_res: Pointer to new initialized devres to add if not found
- * @match: Match function (optional)
- * @match_data: Data for the match function
- *
- * Find the latest devres of @dev which has the same release function
- * as @new_res and for which @match return 1.  If found, @new_res is
- * freed; otherwise, @new_res is added atomically.
- *
- * @return ointer to found or added devres.
- */
-void *devres_get(struct udevice *dev, void *new_res,
-		 dr_match_t match, void *match_data);
-
-/**
- * devres_remove() - Find a device resource and remove it
- * @dev: Device to find resource from
- * @release: Look for resources associated with this release function
- * @match: Match function (optional)
- * @match_data: Data for the match function
- *
- * Find the latest devres of @dev associated with @release and for
- * which @match returns 1.  If @match is NULL, it's considered to
- * match all.  If found, the resource is removed atomically and
- * returned.
- *
- * @return ointer to removed devres on success, NULL if not found.
- */
-void *devres_remove(struct udevice *dev, dr_release_t release,
-		    dr_match_t match, void *match_data);
-
-/**
- * devres_destroy() - Find a device resource and destroy it
- * @dev: Device to find resource from
- * @release: Look for resources associated with this release function
- * @match: Match function (optional)
- * @match_data: Data for the match function
- *
- * Find the latest devres of @dev associated with @release and for
- * which @match returns 1.  If @match is NULL, it's considered to
- * match all.  If found, the resource is removed atomically and freed.
- *
- * Note that the release function for the resource will not be called,
- * only the devres-allocated data will be freed.  The caller becomes
- * responsible for freeing any other data.
- *
- * @return 0 if devres is found and freed, -ENOENT if not found.
- */
-int devres_destroy(struct udevice *dev, dr_release_t release,
-		   dr_match_t match, void *match_data);
-
-/**
- * devres_release() - Find a device resource and destroy it, calling release
- * @dev: Device to find resource from
- * @release: Look for resources associated with this release function
- * @match: Match function (optional)
- * @match_data: Data for the match function
- *
- * Find the latest devres of @dev associated with @release and for
- * which @match returns 1.  If @match is NULL, it's considered to
- * match all.  If found, the resource is removed atomically, the
- * release function called and the resource freed.
- *
- * @return 0 if devres is found and freed, -ENOENT if not found.
- */
-int devres_release(struct udevice *dev, dr_release_t release,
-		   dr_match_t match, void *match_data);
-
-/* managed devm_k.alloc/kfree for device drivers */
-/**
- * devm_kmalloc() - Resource-managed kmalloc
- * @dev: Device to allocate memory for
- * @size: Allocation size
- * @gfp: Allocation gfp flags
- *
- * Managed kmalloc.  Memory allocated with this function is
- * automatically freed on driver detach.  Like all other devres
- * resources, guaranteed alignment is unsigned long long.
- *
- * @return pointer to allocated memory on success, NULL on failure.
- */
-void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp);
-static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp)
-{
-	return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
-}
-static inline void *devm_kmalloc_array(struct udevice *dev,
-				       size_t n, size_t size, gfp_t flags)
-{
-	if (size != 0 && n > SIZE_MAX / size)
-		return NULL;
-	return devm_kmalloc(dev, n * size, flags);
-}
-static inline void *devm_kcalloc(struct udevice *dev,
-				 size_t n, size_t size, gfp_t flags)
-{
-	return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
-}
-
-/**
- * devm_kfree() - Resource-managed kfree
- * @dev: Device this memory belongs to
- * @ptr: Memory to free
- *
- * Free memory allocated with devm_kmalloc().
- */
-void devm_kfree(struct udevice *dev, void *ptr);
-
-#else /* ! CONFIG_DEVRES */
-
-static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
-{
-	return kzalloc(size, gfp);
-}
-
-static inline void devres_free(void *res)
-{
-	kfree(res);
-}
-
-static inline void devres_add(struct udevice *dev, void *res)
-{
-}
-
-static inline void *devres_find(struct udevice *dev, dr_release_t release,
-				dr_match_t match, void *match_data)
-{
-	return NULL;
-}
-
-static inline void *devres_get(struct udevice *dev, void *new_res,
-			       dr_match_t match, void *match_data)
-{
-	return NULL;
-}
-
-static inline void *devres_remove(struct udevice *dev, dr_release_t release,
-				  dr_match_t match, void *match_data)
-{
-	return NULL;
-}
-
-static inline int devres_destroy(struct udevice *dev, dr_release_t release,
-				 dr_match_t match, void *match_data)
-{
-	return 0;
-}
-
-static inline int devres_release(struct udevice *dev, dr_release_t release,
-				 dr_match_t match, void *match_data)
-{
-	return 0;
-}
-
-static inline void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
-{
-	return kmalloc(size, gfp);
-}
-
-static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp)
-{
-	return kzalloc(size, gfp);
-}
-
-static inline void *devm_kmalloc_array(struct udevice *dev,
-				       size_t n, size_t size, gfp_t flags)
-{
-	/* TODO: add kmalloc_array() to linux/compat.h */
-	if (size != 0 && n > SIZE_MAX / size)
-		return NULL;
-	return kmalloc(n * size, flags);
-}
-
-static inline void *devm_kcalloc(struct udevice *dev,
-				 size_t n, size_t size, gfp_t flags)
-{
-	/* TODO: add kcalloc() to linux/compat.h */
-	return kmalloc(n * size, flags | __GFP_ZERO);
-}
-
-static inline void devm_kfree(struct udevice *dev, void *ptr)
-{
-	kfree(ptr);
-}
-
-#endif /* ! CONFIG_DEVRES */
+#include <dm/devres.h>
 
 /*
  * REVISIT:

+ 289 - 0
include/dm/devres.h

@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * Based on the original work in Linux by
+ * Copyright (c) 2006  SUSE Linux Products GmbH
+ * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef _DM_DEVRES_H
+#define _DM_DEVRES_H
+
+/* device resource management */
+typedef void (*dr_release_t)(struct udevice *dev, void *res);
+typedef int (*dr_match_t)(struct udevice *dev, void *res, void *match_data);
+
+/**
+ * struct devres_stats - Information about devres allocations for a device
+ *
+ * @allocs: Number of allocations
+ * @total_size: Total size of allocations in bytes
+ */
+struct devres_stats {
+	int allocs;
+	int total_size;
+};
+
+#ifdef CONFIG_DEVRES
+
+#ifdef CONFIG_DEBUG_DEVRES
+void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
+		     const char *name);
+#define _devres_alloc(release, size, gfp) \
+	__devres_alloc(release, size, gfp, #release)
+#else
+void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
+#endif
+
+/**
+ * devres_alloc() - Allocate device resource data
+ * @release: Release function devres will be associated with
+ * @size: Allocation size
+ * @gfp: Allocation flags
+ *
+ * Allocate devres of @size bytes.  The allocated area is associated
+ * with @release.  The returned pointer can be passed to
+ * other devres_*() functions.
+ *
+ * RETURNS:
+ * Pointer to allocated devres on success, NULL on failure.
+ */
+#define devres_alloc(release, size, gfp) \
+	_devres_alloc(release, size, (gfp) | __GFP_ZERO)
+
+/**
+ * devres_free() - Free device resource data
+ * @res: Pointer to devres data to free
+ *
+ * Free devres created with devres_alloc().
+ */
+void devres_free(void *res);
+
+/**
+ * devres_add() - Register device resource
+ * @dev: Device to add resource to
+ * @res: Resource to register
+ *
+ * Register devres @res to @dev.  @res should have been allocated
+ * using devres_alloc().  On driver detach, the associated release
+ * function will be invoked and devres will be freed automatically.
+ */
+void devres_add(struct udevice *dev, void *res);
+
+/**
+ * devres_find() - Find device resource
+ * @dev: Device to lookup resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which is associated with @release
+ * and for which @match returns 1.  If @match is NULL, it's considered
+ * to match all.
+ *
+ * @return pointer to found devres, NULL if not found.
+ */
+void *devres_find(struct udevice *dev, dr_release_t release,
+		  dr_match_t match, void *match_data);
+
+/**
+ * devres_get() - Find devres, if non-existent, add one atomically
+ * @dev: Device to lookup or add devres for
+ * @new_res: Pointer to new initialized devres to add if not found
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev which has the same release function
+ * as @new_res and for which @match return 1.  If found, @new_res is
+ * freed; otherwise, @new_res is added atomically.
+ *
+ * @return ointer to found or added devres.
+ */
+void *devres_get(struct udevice *dev, void *new_res,
+		 dr_match_t match, void *match_data);
+
+/**
+ * devres_remove() - Find a device resource and remove it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1.  If @match is NULL, it's considered to
+ * match all.  If found, the resource is removed atomically and
+ * returned.
+ *
+ * @return ointer to removed devres on success, NULL if not found.
+ */
+void *devres_remove(struct udevice *dev, dr_release_t release,
+		    dr_match_t match, void *match_data);
+
+/**
+ * devres_destroy() - Find a device resource and destroy it
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1.  If @match is NULL, it's considered to
+ * match all.  If found, the resource is removed atomically and freed.
+ *
+ * Note that the release function for the resource will not be called,
+ * only the devres-allocated data will be freed.  The caller becomes
+ * responsible for freeing any other data.
+ *
+ * @return 0 if devres is found and freed, -ENOENT if not found.
+ */
+int devres_destroy(struct udevice *dev, dr_release_t release,
+		   dr_match_t match, void *match_data);
+
+/**
+ * devres_release() - Find a device resource and destroy it, calling release
+ * @dev: Device to find resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ *
+ * Find the latest devres of @dev associated with @release and for
+ * which @match returns 1.  If @match is NULL, it's considered to
+ * match all.  If found, the resource is removed atomically, the
+ * release function called and the resource freed.
+ *
+ * @return 0 if devres is found and freed, -ENOENT if not found.
+ */
+int devres_release(struct udevice *dev, dr_release_t release,
+		   dr_match_t match, void *match_data);
+
+/* managed devm_k.alloc/kfree for device drivers */
+/**
+ * devm_kmalloc() - Resource-managed kmalloc
+ * @dev: Device to allocate memory for
+ * @size: Allocation size
+ * @gfp: Allocation gfp flags
+ *
+ * Managed kmalloc.  Memory allocated with this function is
+ * automatically freed on driver detach.  Like all other devres
+ * resources, guaranteed alignment is unsigned long long.
+ *
+ * @return pointer to allocated memory on success, NULL on failure.
+ */
+void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp);
+static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp)
+{
+	return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
+}
+
+static inline void *devm_kmalloc_array(struct udevice *dev,
+				       size_t n, size_t size, gfp_t flags)
+{
+	if (size != 0 && n > SIZE_MAX / size)
+		return NULL;
+	return devm_kmalloc(dev, n * size, flags);
+}
+
+static inline void *devm_kcalloc(struct udevice *dev,
+				 size_t n, size_t size, gfp_t flags)
+{
+	return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
+}
+
+/**
+ * devm_kfree() - Resource-managed kfree
+ * @dev: Device this memory belongs to
+ * @ptr: Memory to free
+ *
+ * Free memory allocated with devm_kmalloc().
+ */
+void devm_kfree(struct udevice *dev, void *ptr);
+
+/* Get basic stats on allocations */
+void devres_get_stats(const struct udevice *dev, struct devres_stats *stats);
+
+#else /* ! CONFIG_DEVRES */
+
+static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
+{
+	return kzalloc(size, gfp);
+}
+
+static inline void devres_free(void *res)
+{
+	kfree(res);
+}
+
+static inline void devres_add(struct udevice *dev, void *res)
+{
+}
+
+static inline void *devres_find(struct udevice *dev, dr_release_t release,
+				dr_match_t match, void *match_data)
+{
+	return NULL;
+}
+
+static inline void *devres_get(struct udevice *dev, void *new_res,
+			       dr_match_t match, void *match_data)
+{
+	return NULL;
+}
+
+static inline void *devres_remove(struct udevice *dev, dr_release_t release,
+				  dr_match_t match, void *match_data)
+{
+	return NULL;
+}
+
+static inline int devres_destroy(struct udevice *dev, dr_release_t release,
+				 dr_match_t match, void *match_data)
+{
+	return 0;
+}
+
+static inline int devres_release(struct udevice *dev, dr_release_t release,
+				 dr_match_t match, void *match_data)
+{
+	return 0;
+}
+
+static inline void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
+{
+	return kmalloc(size, gfp);
+}
+
+static inline void *devm_kzalloc(struct udevice *dev, size_t size, gfp_t gfp)
+{
+	return kzalloc(size, gfp);
+}
+
+static inline void *devm_kmalloc_array(struct udevice *dev,
+				       size_t n, size_t size, gfp_t flags)
+{
+	/* TODO: add kmalloc_array() to linux/compat.h */
+	if (size != 0 && n > SIZE_MAX / size)
+		return NULL;
+	return kmalloc(n * size, flags);
+}
+
+static inline void *devm_kcalloc(struct udevice *dev,
+				 size_t n, size_t size, gfp_t flags)
+{
+	/* TODO: add kcalloc() to linux/compat.h */
+	return kmalloc(n * size, flags | __GFP_ZERO);
+}
+
+static inline void devm_kfree(struct udevice *dev, void *ptr)
+{
+	kfree(ptr);
+}
+
+static inline void devres_get_stats(const struct udevice *dev,
+				    struct devres_stats *stats)
+{
+}
+
+#endif /* DEVRES */
+#endif /* _DM_DEVRES_H */

+ 1 - 0
include/dm/uclass-id.h

@@ -19,6 +19,7 @@ enum uclass_id {
 	UCLASS_TEST_BUS,
 	UCLASS_TEST_PROBE,
 	UCLASS_TEST_DUMMY,
+	UCLASS_TEST_DEVRES,
 	UCLASS_SPI_EMUL,	/* sandbox SPI device emulator */
 	UCLASS_I2C_EMUL,	/* sandbox I2C device emulator */
 	UCLASS_I2C_EMUL_PARENT,	/* parent for I2C device emulators */

+ 1 - 0
include/fdt_support.h

@@ -9,6 +9,7 @@
 
 #ifdef CONFIG_OF_LIBFDT
 
+#include <asm/u-boot.h>
 #include <linux/libfdt.h>
 
 u32 fdt_getprop_u32_default_node(const void *fdt, int off, int cell,

+ 16 - 0
include/log.h

@@ -9,6 +9,7 @@
 #ifndef __LOG_H
 #define __LOG_H
 
+#include <command.h>
 #include <dm/uclass-id.h>
 #include <linux/list.h>
 
@@ -49,6 +50,7 @@ enum log_category_t {
 	LOGC_ALLOC,	/* Memory allocation */
 	LOGC_SANDBOX,	/* Related to the sandbox board */
 	LOGC_BLOBLIST,	/* Bloblist */
+	LOGC_DEVRES,	/* Device resources (devres_... functions) */
 
 	LOGC_COUNT,	/* Number of log categories */
 	LOGC_END,	/* Sentinel value for a list of log categories */
@@ -218,6 +220,20 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
 	({ if (!(x) && _DEBUG) \
 		__assert_fail(#x, __FILE__, __LINE__, __func__); })
 
+/*
+ * This one actually gets compiled in even without DEBUG. It doesn't include the
+ * full pathname as it may be huge. Only use this when the user should be
+ * warning, similar to BUG_ON() in linux.
+ *
+ * @return true if assertion succeeded (condition is true), else false
+ */
+#define assert_noisy(x) \
+	({ bool _val = (x); \
+	if (!_val) \
+		__assert_fail(#x, "?", __LINE__, __func__); \
+	_val; \
+	})
+
 #if CONFIG_IS_ENABLED(LOG) && defined(CONFIG_LOG_ERROR_RETURN)
 /*
  * Log an error return value, possibly with a message. Usage:

+ 10 - 0
include/test/test.h

@@ -46,5 +46,15 @@ struct unit_test {
 		.func = _name,						\
 	}
 
+/* Sizes for devres tests */
+enum {
+	TEST_DEVRES_SIZE	= 100,
+	TEST_DEVRES_COUNT	= 10,
+	TEST_DEVRES_TOTAL	= TEST_DEVRES_SIZE * TEST_DEVRES_COUNT,
+
+	/* A few different sizes */
+	TEST_DEVRES_SIZE2	= 15,
+	TEST_DEVRES_SIZE3	= 37,
+};
 
 #endif /* __TEST_TEST_H */

+ 16 - 0
include/test/ut.h

@@ -149,4 +149,20 @@ void ut_failf(struct unit_test_state *uts, const char *fname, int line,
 /* Assert that an operation succeeds (returns 0) */
 #define ut_assertok(cond)	ut_asserteq(0, cond)
 
+/**
+ * ut_check_free() - Return the number of bytes free in the malloc() pool
+ *
+ * @return bytes free
+ */
+ulong ut_check_free(void);
+
+/**
+ * ut_check_delta() - Return the number of bytes allocated/freed
+ *
+ * @last: Last value from ut_check_free
+ * @return free memory delta from @last; positive means more memory has been
+ *	allocated, negative means less has been allocated (i.e. some is freed)
+ */
+long ut_check_delta(ulong last);
+
 #endif

+ 1 - 0
test/dm/Makefile

@@ -18,6 +18,7 @@ obj-$(CONFIG_BLK) += blk.o
 obj-$(CONFIG_BOARD) += board.o
 obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o
 obj-$(CONFIG_CLK) += clk.o clk_ccf.o
+obj-$(CONFIG_DEVRES) += devres.o
 obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o
 obj-$(CONFIG_DM_ETH) += eth.o
 obj-$(CONFIG_FIRMWARE) += firmware.o

+ 186 - 0
test/dm/devres.c

@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the devres (
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+
+/* Test that devm_kmalloc() allocates memory, free when device is removed */
+static int dm_test_devres_alloc(struct unit_test_state *uts)
+{
+	ulong mem_start, mem_dev, mem_kmalloc;
+	struct udevice *dev;
+	void *ptr;
+
+	mem_start = ut_check_delta(0);
+	ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+	mem_dev = ut_check_delta(mem_start);
+	ut_assert(mem_dev > 0);
+
+	/* This should increase allocated memory */
+	ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+	ut_assert(ptr != NULL);
+	mem_kmalloc = ut_check_delta(mem_dev);
+	ut_assert(mem_kmalloc > 0);
+
+	/* Check that ptr is freed */
+	device_remove(dev, DM_REMOVE_NORMAL);
+	ut_asserteq(0, ut_check_delta(mem_start));
+
+	return 0;
+}
+DM_TEST(dm_test_devres_alloc, DM_TESTF_SCAN_PDATA);
+
+/* Test devm_kfree() can be used to free memory too */
+static int dm_test_devres_free(struct unit_test_state *uts)
+{
+	ulong mem_start, mem_dev, mem_kmalloc;
+	struct udevice *dev;
+	void *ptr;
+
+	mem_start = ut_check_delta(0);
+	ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+	mem_dev = ut_check_delta(mem_start);
+	ut_assert(mem_dev > 0);
+
+	ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+	ut_assert(ptr != NULL);
+	mem_kmalloc = ut_check_delta(mem_dev);
+	ut_assert(mem_kmalloc > 0);
+
+	/* Free the ptr and check that memory usage goes down */
+	devm_kfree(dev, ptr);
+	ut_assert(ut_check_delta(mem_kmalloc) < 0);
+
+	device_remove(dev, DM_REMOVE_NORMAL);
+	ut_asserteq(0, ut_check_delta(mem_start));
+
+	return 0;
+}
+DM_TEST(dm_test_devres_free, DM_TESTF_SCAN_PDATA);
+
+
+/* Test that kzalloc() returns memory that is zeroed */
+static int dm_test_devres_kzalloc(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 *ptr, val;
+	int i;
+
+	ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+
+	ptr = devm_kzalloc(dev, TEST_DEVRES_SIZE, 0);
+	ut_assert(ptr != NULL);
+	for (val = 0, i = 0; i < TEST_DEVRES_SIZE; i++)
+		val |= *ptr;
+	ut_asserteq(0, val);
+
+	return 0;
+}
+DM_TEST(dm_test_devres_kzalloc, DM_TESTF_SCAN_PDATA);
+
+/* Test that devm_kmalloc_array() allocates an array that can be set */
+static int dm_test_devres_kmalloc_array(struct unit_test_state *uts)
+{
+	ulong mem_start, mem_dev;
+	struct udevice *dev;
+	u8 *ptr;
+
+	mem_start = ut_check_delta(0);
+	ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+	mem_dev = ut_check_delta(mem_start);
+
+	ptr = devm_kmalloc_array(dev, TEST_DEVRES_COUNT, TEST_DEVRES_SIZE, 0);
+	ut_assert(ptr != NULL);
+	memset(ptr, '\xff', TEST_DEVRES_TOTAL);
+	ut_assert(ut_check_delta(mem_dev) > 0);
+
+	device_remove(dev, DM_REMOVE_NORMAL);
+	ut_asserteq(0, ut_check_delta(mem_start));
+
+	return 0;
+}
+DM_TEST(dm_test_devres_kmalloc_array, DM_TESTF_SCAN_PDATA);
+
+/* Test that devm_kcalloc() allocates a zeroed array */
+static int dm_test_devres_kcalloc(struct unit_test_state *uts)
+{
+	ulong mem_start, mem_dev;
+	struct udevice *dev;
+	u8 *ptr, val;
+	int i;
+
+	mem_start = ut_check_delta(0);
+	ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+	mem_dev = ut_check_delta(mem_start);
+	ut_assert(mem_dev > 0);
+
+	/* This should increase allocated memory */
+	ptr = devm_kcalloc(dev, TEST_DEVRES_SIZE, TEST_DEVRES_COUNT, 0);
+	ut_assert(ptr != NULL);
+	ut_assert(ut_check_delta(mem_dev) > 0);
+	for (val = 0, i = 0; i < TEST_DEVRES_TOTAL; i++)
+		val |= *ptr;
+	ut_asserteq(0, val);
+
+	/* Check that ptr is freed */
+	device_remove(dev, DM_REMOVE_NORMAL);
+	ut_asserteq(0, ut_check_delta(mem_start));
+
+	return 0;
+}
+DM_TEST(dm_test_devres_kcalloc, DM_TESTF_SCAN_PDATA);
+
+/* Test devres releases resources automatically as expected */
+static int dm_test_devres_phase(struct unit_test_state *uts)
+{
+	struct devres_stats stats;
+	struct udevice *dev;
+
+	/*
+	 * The device is bound already, so find it and check that it has the
+	 * allocation created in the bind() method.
+	 */
+	ut_assertok(uclass_find_first_device(UCLASS_TEST_DEVRES, &dev));
+	devres_get_stats(dev, &stats);
+	ut_asserteq(1, stats.allocs);
+	ut_asserteq(TEST_DEVRES_SIZE, stats.total_size);
+
+	/* Getting platdata should add one allocation */
+	ut_assertok(device_ofdata_to_platdata(dev));
+	devres_get_stats(dev, &stats);
+	ut_asserteq(2, stats.allocs);
+	ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE3, stats.total_size);
+
+	/* Probing the device should add one allocation */
+	ut_assertok(uclass_first_device(UCLASS_TEST_DEVRES, &dev));
+	ut_assert(dev != NULL);
+	devres_get_stats(dev, &stats);
+	ut_asserteq(3, stats.allocs);
+	ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE2 + TEST_DEVRES_SIZE3,
+		    stats.total_size);
+
+	/* Removing the device should drop both those allocations */
+	device_remove(dev, DM_REMOVE_NORMAL);
+	devres_get_stats(dev, &stats);
+	ut_asserteq(1, stats.allocs);
+	ut_asserteq(TEST_DEVRES_SIZE, stats.total_size);
+
+	/* Unbinding removes the other. Note this access a freed pointer */
+	device_unbind(dev);
+	devres_get_stats(dev, &stats);
+	ut_asserteq(0, stats.allocs);
+	ut_asserteq(0, stats.total_size);
+
+	return 0;
+}
+DM_TEST(dm_test_devres_phase, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);

+ 58 - 0
test/dm/test-fdt.c

@@ -153,6 +153,64 @@ UCLASS_DRIVER(testprobe) = {
 	.flags		= DM_UC_FLAG_SEQ_ALIAS,
 };
 
+struct dm_testdevres_pdata {
+	void *ptr;
+};
+
+struct dm_testdevres_priv {
+	void *ptr;
+	void *ptr_ofdata;
+};
+
+static int testdevres_drv_bind(struct udevice *dev)
+{
+	struct dm_testdevres_pdata *pdata = dev_get_platdata(dev);
+
+	pdata->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+
+	return 0;
+}
+
+static int testdevres_drv_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dm_testdevres_priv *priv = dev_get_priv(dev);
+
+	priv->ptr_ofdata = devm_kmalloc(dev, TEST_DEVRES_SIZE3, 0);
+
+	return 0;
+}
+
+static int testdevres_drv_probe(struct udevice *dev)
+{
+	struct dm_testdevres_priv *priv = dev_get_priv(dev);
+
+	priv->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE2, 0);
+
+	return 0;
+}
+
+static const struct udevice_id testdevres_ids[] = {
+	{ .compatible = "denx,u-boot-devres-test" },
+	{ }
+};
+
+U_BOOT_DRIVER(testdevres_drv) = {
+	.name	= "testdevres_drv",
+	.of_match	= testdevres_ids,
+	.id	= UCLASS_TEST_DEVRES,
+	.bind	= testdevres_drv_bind,
+	.ofdata_to_platdata	= testdevres_drv_ofdata_to_platdata,
+	.probe	= testdevres_drv_probe,
+	.platdata_auto_alloc_size	= sizeof(struct dm_testdevres_pdata),
+	.priv_auto_alloc_size	= sizeof(struct dm_testdevres_priv),
+};
+
+UCLASS_DRIVER(testdevres) = {
+	.name		= "testdevres",
+	.id		= UCLASS_TEST_DEVRES,
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+};
+
 int dm_check_devices(struct unit_test_state *uts, int num_devices)
 {
 	struct udevice *dev;

+ 14 - 0
test/ut.c

@@ -6,6 +6,7 @@
  */
 
 #include <common.h>
+#include <malloc.h>
 #include <test/test.h>
 #include <test/ut.h>
 
@@ -32,3 +33,16 @@ void ut_failf(struct unit_test_state *uts, const char *fname, int line,
 	putc('\n');
 	uts->fail_count++;
 }
+
+ulong ut_check_free(void)
+{
+	struct mallinfo info = mallinfo();
+
+	return info.uordblks;
+}
+
+long ut_check_delta(ulong last)
+{
+	return ut_check_free() - last;
+}
+

+ 1 - 1
tools/binman/README.entries

@@ -971,7 +971,7 @@ Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer
 --------------------------------------------------------------------
 
 Properties / Entry arguments:
-    - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
+    - filename: Filename of u-boot-nodtb.bin (default 'u-boot-nodtb.bin')
     - optional-ucode: boolean property to make microcode optional. If the
         u-boot.bin image does not include microcode, no error will
         be generated.

+ 1 - 1
tools/binman/etype/u_boot_with_ucode_ptr.py

@@ -18,7 +18,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
     """U-Boot with embedded microcode pointer
 
     Properties / Entry arguments:
-        - filename: Filename of u-boot-nodtb.dtb (default 'u-boot-nodtb.dtb')
+        - filename: Filename of u-boot-nodtb.bin (default 'u-boot-nodtb.bin')
         - optional-ucode: boolean property to make microcode optional. If the
             u-boot.bin image does not include microcode, no error will
             be generated.