Merge tag 'dm-pull-8jan20' of git://git.denx.de/u-boot-dm
authorTom Rini <trini@konsulko.com>
Thu, 9 Jan 2020 13:52:21 +0000 (08:52 -0500)
committerTom Rini <trini@konsulko.com>
Thu, 9 Jan 2020 13:52:21 +0000 (08:52 -0500)
dm: Increased separation of ofdata_to_platdata() and probe methods

27 files changed:
arch/sandbox/dts/test.dts
arch/x86/cpu/apollolake/p2sb.c
arch/x86/cpu/apollolake/pmc.c
drivers/clk/aspeed/clk_ast2500.c
drivers/core/device-remove.c
drivers/core/device.c
drivers/core/devres.c
drivers/core/lists.c
drivers/pci/pci-uclass.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/f_mass_storage.c
drivers/usb/musb-new/musb_core.c
drivers/usb/musb-new/musb_gadget_ep0.c
include/dm/device-internal.h
include/dm/device.h
include/dm/devres.h [new file with mode: 0644]
include/dm/uclass-id.h
include/fdt_support.h
include/log.h
include/test/test.h
include/test/ut.h
test/dm/Makefile
test/dm/devres.c [new file with mode: 0644]
test/dm/test-fdt.c
test/ut.c
tools/binman/README.entries
tools/binman/etype/u_boot_with_ucode_ptr.py

index 4ea8fea674d0148455d1cc1bbb356b369a9fe756..e529c54d8de99ace4e37dedbe29465456cac87f9 100644 (file)
                compatible = "denx,u-boot-fdt-test1";
        };
 
+       devres-test {
+               compatible = "denx,u-boot-devres-test";
+       };
+
        clocks {
                clk_fixed: clk-fixed {
                        compatible = "fixed-clock";
index eb27861b7a467f887c3b1227ff72abe5599fea35..b72f50a6274dc17ff9e1df4ebc3ba49d2a191dcf 100644 (file)
@@ -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;
 }
index 683c6082f2c3b9129019adf23b60686c35a8988a..aec0c8394c2e5b708e1fdcd6d09b9d4ef2d4d486 100644 (file)
@@ -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;
 }
index 9249cf9cdfd5eb7b79fb8470a4676eab4455013a..b3a3f3d4dd9f7e9541939c4ce740236a24eaf6c0 100644 (file)
@@ -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,
 };
index 5c8dc4ad701bd37b347f240c0f3bc3202fa2a315..444e34b4921b047c329866e93d872ddb83b9e8d7 100644 (file)
@@ -140,6 +140,7 @@ void device_free(struct udevice *dev)
                        dev->parent_priv = NULL;
                }
        }
+       dev->flags &= ~DM_FLAG_PLATDATA_VALID;
 
        devres_release_probe(dev);
 }
index 4e037083a6374a11d9c73fd6f5e27a803ca9154a..9f39218423e1e1f9b4d734d3d4a73d9f9b9ed710 100644 (file)
@@ -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);
index f2a19ec61b1967b7db76ef6ef556bbebd394632f..237b42653c6beb51e6cdc8043a8ec0941b925f6b 100644 (file)
@@ -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>
 #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);
 }
index 4681b3e5dd1a02fd4268a089ea75941e94087be7..68204c303fbd37566e87b00f55aa118fbc155cbb 100644 (file)
@@ -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",
index 7308f612b67bcfc0760adaa148ef43b0b873f2b2..5be2dfd0bf64b19d633477d2bcbdeda6bc334fb0 100644 (file)
@@ -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);
 }
 
index c98a444245c28b6d77495e5fabfce07f481160c1..4a6f4271d5bf61c84eba29a07ffe744dba6b21ff 100644 (file)
@@ -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,
index 45c7b58eed43f4cd385a790dc20c7935340437b9..c1e6506659cc9c65ceb9161e302832aaff0be41f 100644 (file)
@@ -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;
 }
 
index afea9fbcef67205cab4306ff3df4b1002b28d2d8..ab5e3aa9d13a3e9c120cd7dfa3fa6eb79e85528d 100644 (file)
@@ -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++) {
index 9835a2e2bf75f127d38c1f4092c63814fcf4d27e..3ef8fe13732d26220017a5df7047ac5f63e0d1f1 100644 (file)
@@ -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;
index ee2b24a62a2d7a60d2ffb621cb92383a95b743ae..294d6c18105ac23796a6aa3f744f4a159e944925 100644 (file)
@@ -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
  *
index d7ad9d6728b0d26fff0ee1c85c9a26b86ee6e05a..1138a09149f4b8c1222e5c03eff1a786e9537c44 100644 (file)
@@ -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:
diff --git a/include/dm/devres.h b/include/dm/devres.h
new file mode 100644 (file)
index 0000000..9c69196
--- /dev/null
@@ -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 */
index 67f5d673cb80bab17392bb2b9a3f3801932133af..598f65ea7a32ecc5eaada566e7c55284fbef3db9 100644 (file)
@@ -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 */
index 2286ea7793924809bcbfe9f1e482889c44bda0d3..3f4bc643d4453b394523a082c5dea65b118c162f 100644 (file)
@@ -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,
index d8f18a6afdf38b7fb7fe0782337880af61f699db..62fb8afbd0e28c68105d16cb44e56287abc2f2f3 100644 (file)
@@ -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:
index 98fbcd11f6ffed8dcd55a7b2c27061f060e25022..e5bef4759a6f88c28929e33e1fed7ea030d14829 100644 (file)
@@ -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 */
index fbfde10719f90f14dd7d714d5bfaa51c7204c251..f616c202f357a4f72d8ca75e4f5045174fb33bd0 100644 (file)
@@ -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
index 201e2b093c16fa2094571efcffa6993ad7389932..dd1ceff86c0cc4412d85ef736b221138045fe309 100644 (file)
@@ -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
diff --git a/test/dm/devres.c b/test/dm/devres.c
new file mode 100644 (file)
index 0000000..e733189
--- /dev/null
@@ -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);
index 1fb8b5c248d346f9903663b7727b5e375dae0638..d59c449ce0cdd8159e731dfdf7a11851c11cb31e 100644 (file)
@@ -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;
index 55798041bafe937370fc69bc9899000ccf766efe..265da4a0d89a8d05fa50e39751c32e6b52ed1119 100644 (file)
--- a/test/ut.c
+++ b/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;
+}
+
index 0576e63a862c2e8bf221fb64226b28ae3d8020dc..6a816bba6bcf1b0d5157e8f1122fbb9e0abcd22f 100644 (file)
@@ -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.
index cb7dbc68dbb8c45d6cb7117be219ef74faf8badc..960a5efeb4182127b6b02509b6f9926649270c53 100644 (file)
@@ -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.