1
0
mirror of https://xff.cz/git/u-boot/ synced 2025-09-01 16:52:14 +02:00

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

dm: Increased separation of ofdata_to_platdata() and probe methods
This commit is contained in:
Tom Rini
2020-01-09 08:52:21 -05:00
27 changed files with 767 additions and 297 deletions

View File

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

View File

@@ -106,11 +106,6 @@ int apl_p2sb_ofdata_to_platdata(struct udevice *dev)
if (plat->bdf < 0) if (plat->bdf < 0)
return log_msg_ret("Cannot get p2sb PCI address", return log_msg_ret("Cannot get p2sb PCI address",
plat->bdf); 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 #else
plat->mmio_base = plat->dtplat.early_regs[0]; 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) static int apl_p2sb_probe(struct udevice *dev)
{ {
if (spl_phase() == PHASE_TPL) if (spl_phase() == PHASE_TPL) {
return apl_p2sb_early_init(dev); return apl_p2sb_early_init(dev);
else if (spl_phase() == PHASE_SPL) } else {
return apl_p2sb_spl_init(dev); 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; return 0;
} }

View 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)); ret = dev_read_u32_array(dev, "early-regs", base, ARRAY_SIZE(base));
if (ret) if (ret)
return log_msg_ret("Missing/short early-regs", ret); return log_msg_ret("Missing/short early-regs", ret);
upriv->pmc_bar0 = (void *)base[0]; if (spl_phase() == PHASE_TPL) {
upriv->pmc_bar2 = (void *)base[2]; 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]; upriv->acpi_base = base[4];
/* Since PCI is not enabled, we must get the BDF manually */ /* 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) static int apl_pmc_probe(struct udevice *dev)
{ {
if (spl_phase() == PHASE_TPL) if (spl_phase() == PHASE_TPL) {
return enable_pmcbar(dev); 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; return 0;
} }

View File

@@ -490,7 +490,7 @@ struct clk_ops ast2500_clk_ops = {
.enable = ast2500_clk_enable, .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); 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), .priv_auto_alloc_size = sizeof(struct ast2500_clk_priv),
.ops = &ast2500_clk_ops, .ops = &ast2500_clk_ops,
.bind = ast2500_clk_bind, .bind = ast2500_clk_bind,
.probe = ast2500_clk_probe, .ofdata_to_platdata = ast2500_clk_ofdata_to_platdata,
}; };

View File

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

View File

@@ -311,17 +311,16 @@ static void *alloc_priv(int size, uint flags)
return priv; return priv;
} }
int device_probe(struct udevice *dev) int device_ofdata_to_platdata(struct udevice *dev)
{ {
const struct driver *drv; const struct driver *drv;
int size = 0; int size = 0;
int ret; int ret;
int seq;
if (!dev) if (!dev)
return -EINVAL; return -EINVAL;
if (dev->flags & DM_FLAG_ACTIVATED) if (dev->flags & DM_FLAG_PLATDATA_VALID)
return 0; return 0;
drv = dev->driver; 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) { if (dev->parent) {
size = dev->parent->driver->per_child_auto_alloc_size; size = dev->parent->driver->per_child_auto_alloc_size;
if (!size) { if (!size) {
@@ -360,7 +359,45 @@ int device_probe(struct udevice *dev)
goto fail; 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); ret = device_probe(dev->parent);
if (ret) if (ret)
goto fail; goto fail;
@@ -411,13 +448,6 @@ int device_probe(struct udevice *dev)
goto fail; 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 */ /* Only handle devices that have a valid ofnode */
if (dev_of_valid(dev)) { if (dev_of_valid(dev)) {
/* /*
@@ -431,10 +461,8 @@ int device_probe(struct udevice *dev)
if (drv->probe) { if (drv->probe) {
ret = drv->probe(dev); ret = drv->probe(dev);
if (ret) { if (ret)
dev->flags &= ~DM_FLAG_ACTIVATED;
goto fail; goto fail;
}
} }
ret = uclass_post_probe_device(dev); ret = uclass_post_probe_device(dev);

View File

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

View File

@@ -176,8 +176,10 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
if (pre_reloc_only) { if (pre_reloc_only) {
if (!dm_ofnode_pre_reloc(node) && 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; return 0;
}
} }
log_debug(" - found match at '%s': '%s' matches '%s'\n", log_debug(" - found match at '%s': '%s' matches '%s'\n",

View 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 pci_child_platdata *pplat = dev_get_parent_platdata(dev);
struct udevice *bus = dev->parent; 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); return PCI_ADD_BUS(bus->seq, pplat->devfn);
} }

View File

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

View File

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

View File

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

View File

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

View 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, int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
const struct driver_info *info, struct udevice **devp); 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 * device_probe() - Probe a device, activating it
* *

View File

@@ -45,6 +45,7 @@ struct driver_info;
/* Device name is allocated and should be freed on unbind() */ /* Device name is allocated and should be freed on unbind() */
#define DM_FLAG_NAME_ALLOCED (1 << 7) #define DM_FLAG_NAME_ALLOCED (1 << 7)
/* Device has platform data provided by of-platdata */
#define DM_FLAG_OF_PLATDATA (1 << 8) #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 */ /* DM does not enable/disable the power domains corresponding to this device */
#define DM_FLAG_DEFAULT_PD_CTRL_OFF (1 << 11) #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 * 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 * 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); int dm_scan_fdt_dev(struct udevice *dev);
/* device resource management */ #include <dm/devres.h>
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 */
/* /*
* REVISIT: * REVISIT:

289
include/dm/devres.h Normal file
View File

@@ -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 */

View File

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

View File

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

View File

@@ -9,6 +9,7 @@
#ifndef __LOG_H #ifndef __LOG_H
#define __LOG_H #define __LOG_H
#include <command.h>
#include <dm/uclass-id.h> #include <dm/uclass-id.h>
#include <linux/list.h> #include <linux/list.h>
@@ -49,6 +50,7 @@ enum log_category_t {
LOGC_ALLOC, /* Memory allocation */ LOGC_ALLOC, /* Memory allocation */
LOGC_SANDBOX, /* Related to the sandbox board */ LOGC_SANDBOX, /* Related to the sandbox board */
LOGC_BLOBLIST, /* Bloblist */ LOGC_BLOBLIST, /* Bloblist */
LOGC_DEVRES, /* Device resources (devres_... functions) */
LOGC_COUNT, /* Number of log categories */ LOGC_COUNT, /* Number of log categories */
LOGC_END, /* Sentinel value for a list 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) \ ({ if (!(x) && _DEBUG) \
__assert_fail(#x, __FILE__, __LINE__, __func__); }) __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) #if CONFIG_IS_ENABLED(LOG) && defined(CONFIG_LOG_ERROR_RETURN)
/* /*
* Log an error return value, possibly with a message. Usage: * Log an error return value, possibly with a message. Usage:

View File

@@ -46,5 +46,15 @@ struct unit_test {
.func = _name, \ .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 */ #endif /* __TEST_TEST_H */

View 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) */ /* Assert that an operation succeeds (returns 0) */
#define ut_assertok(cond) ut_asserteq(0, cond) #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 #endif

View File

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

186
test/dm/devres.c Normal file
View File

@@ -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);

View File

@@ -153,6 +153,64 @@ UCLASS_DRIVER(testprobe) = {
.flags = DM_UC_FLAG_SEQ_ALIAS, .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) int dm_check_devices(struct unit_test_state *uts, int num_devices)
{ {
struct udevice *dev; struct udevice *dev;

View File

@@ -6,6 +6,7 @@
*/ */
#include <common.h> #include <common.h>
#include <malloc.h>
#include <test/test.h> #include <test/test.h>
#include <test/ut.h> #include <test/ut.h>
@@ -32,3 +33,16 @@ void ut_failf(struct unit_test_state *uts, const char *fname, int line,
putc('\n'); putc('\n');
uts->fail_count++; 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;
}

View File

@@ -971,7 +971,7 @@ Entry: u-boot-with-ucode-ptr: U-Boot with embedded microcode pointer
-------------------------------------------------------------------- --------------------------------------------------------------------
Properties / Entry arguments: 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 - optional-ucode: boolean property to make microcode optional. If the
u-boot.bin image does not include microcode, no error will u-boot.bin image does not include microcode, no error will
be generated. be generated.

View File

@@ -18,7 +18,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
"""U-Boot with embedded microcode pointer """U-Boot with embedded microcode pointer
Properties / Entry arguments: 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 - optional-ucode: boolean property to make microcode optional. If the
u-boot.bin image does not include microcode, no error will u-boot.bin image does not include microcode, no error will
be generated. be generated.