16494d708SSimon Glass /* 26494d708SSimon Glass * Device manager 36494d708SSimon Glass * 46494d708SSimon Glass * Copyright (c) 2013 Google, Inc 56494d708SSimon Glass * 66494d708SSimon Glass * (C) Copyright 2012 76494d708SSimon Glass * Pavel Herrmann <morpheus.ibis@gmail.com> 86494d708SSimon Glass * 96494d708SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 106494d708SSimon Glass */ 116494d708SSimon Glass 126494d708SSimon Glass #include <common.h> 135a66a8ffSSimon Glass #include <fdtdec.h> 146494d708SSimon Glass #include <malloc.h> 156494d708SSimon Glass #include <dm/device.h> 166494d708SSimon Glass #include <dm/device-internal.h> 176494d708SSimon Glass #include <dm/lists.h> 186494d708SSimon Glass #include <dm/platdata.h> 196494d708SSimon Glass #include <dm/uclass.h> 206494d708SSimon Glass #include <dm/uclass-internal.h> 216494d708SSimon Glass #include <dm/util.h> 226494d708SSimon Glass #include <linux/err.h> 236494d708SSimon Glass #include <linux/list.h> 246494d708SSimon Glass 255a66a8ffSSimon Glass DECLARE_GLOBAL_DATA_PTR; 265a66a8ffSSimon Glass 276494d708SSimon Glass /** 286494d708SSimon Glass * device_chld_unbind() - Unbind all device's children from the device 296494d708SSimon Glass * 306494d708SSimon Glass * On error, the function continues to unbind all children, and reports the 316494d708SSimon Glass * first error. 326494d708SSimon Glass * 336494d708SSimon Glass * @dev: The device that is to be stripped of its children 346494d708SSimon Glass * @return 0 on success, -ve on error 356494d708SSimon Glass */ 3654c5d08aSHeiko Schocher static int device_chld_unbind(struct udevice *dev) 376494d708SSimon Glass { 3854c5d08aSHeiko Schocher struct udevice *pos, *n; 396494d708SSimon Glass int ret, saved_ret = 0; 406494d708SSimon Glass 416494d708SSimon Glass assert(dev); 426494d708SSimon Glass 436494d708SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 446494d708SSimon Glass ret = device_unbind(pos); 456494d708SSimon Glass if (ret && !saved_ret) 466494d708SSimon Glass saved_ret = ret; 476494d708SSimon Glass } 486494d708SSimon Glass 496494d708SSimon Glass return saved_ret; 506494d708SSimon Glass } 516494d708SSimon Glass 526494d708SSimon Glass /** 536494d708SSimon Glass * device_chld_remove() - Stop all device's children 546494d708SSimon Glass * @dev: The device whose children are to be removed 556494d708SSimon Glass * @return 0 on success, -ve on error 566494d708SSimon Glass */ 5754c5d08aSHeiko Schocher static int device_chld_remove(struct udevice *dev) 586494d708SSimon Glass { 5954c5d08aSHeiko Schocher struct udevice *pos, *n; 606494d708SSimon Glass int ret; 616494d708SSimon Glass 626494d708SSimon Glass assert(dev); 636494d708SSimon Glass 646494d708SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 656494d708SSimon Glass ret = device_remove(pos); 666494d708SSimon Glass if (ret) 676494d708SSimon Glass return ret; 686494d708SSimon Glass } 696494d708SSimon Glass 706494d708SSimon Glass return 0; 716494d708SSimon Glass } 726494d708SSimon Glass 7354c5d08aSHeiko Schocher int device_bind(struct udevice *parent, struct driver *drv, const char *name, 7454c5d08aSHeiko Schocher void *platdata, int of_offset, struct udevice **devp) 756494d708SSimon Glass { 7654c5d08aSHeiko Schocher struct udevice *dev; 776494d708SSimon Glass struct uclass *uc; 786494d708SSimon Glass int ret = 0; 796494d708SSimon Glass 806494d708SSimon Glass *devp = NULL; 816494d708SSimon Glass if (!name) 826494d708SSimon Glass return -EINVAL; 836494d708SSimon Glass 846494d708SSimon Glass ret = uclass_get(drv->id, &uc); 856494d708SSimon Glass if (ret) 866494d708SSimon Glass return ret; 876494d708SSimon Glass 8854c5d08aSHeiko Schocher dev = calloc(1, sizeof(struct udevice)); 896494d708SSimon Glass if (!dev) 906494d708SSimon Glass return -ENOMEM; 916494d708SSimon Glass 926494d708SSimon Glass INIT_LIST_HEAD(&dev->sibling_node); 936494d708SSimon Glass INIT_LIST_HEAD(&dev->child_head); 946494d708SSimon Glass INIT_LIST_HEAD(&dev->uclass_node); 956494d708SSimon Glass dev->platdata = platdata; 966494d708SSimon Glass dev->name = name; 976494d708SSimon Glass dev->of_offset = of_offset; 986494d708SSimon Glass dev->parent = parent; 996494d708SSimon Glass dev->driver = drv; 1006494d708SSimon Glass dev->uclass = uc; 1015a66a8ffSSimon Glass 1025a66a8ffSSimon Glass /* 1035a66a8ffSSimon Glass * For some devices, such as a SPI or I2C bus, the 'reg' property 1045a66a8ffSSimon Glass * is a reasonable indicator of the sequence number. But if there is 1055a66a8ffSSimon Glass * an alias, we use that in preference. In any case, this is just 1065a66a8ffSSimon Glass * a 'requested' sequence, and will be resolved (and ->seq updated) 1075a66a8ffSSimon Glass * when the device is probed. 1085a66a8ffSSimon Glass */ 1095a66a8ffSSimon Glass dev->req_seq = fdtdec_get_int(gd->fdt_blob, of_offset, "reg", -1); 1105a66a8ffSSimon Glass dev->seq = -1; 1115a66a8ffSSimon Glass if (uc->uc_drv->name && of_offset != -1) { 1125a66a8ffSSimon Glass fdtdec_get_alias_seq(gd->fdt_blob, uc->uc_drv->name, of_offset, 1135a66a8ffSSimon Glass &dev->req_seq); 1145a66a8ffSSimon Glass } 1155a66a8ffSSimon Glass 1166494d708SSimon Glass if (!dev->platdata && drv->platdata_auto_alloc_size) 1176494d708SSimon Glass dev->flags |= DM_FLAG_ALLOC_PDATA; 1186494d708SSimon Glass 1196494d708SSimon Glass /* put dev into parent's successor list */ 1206494d708SSimon Glass if (parent) 1216494d708SSimon Glass list_add_tail(&dev->sibling_node, &parent->child_head); 1226494d708SSimon Glass 1236494d708SSimon Glass ret = uclass_bind_device(dev); 1246494d708SSimon Glass if (ret) 1256494d708SSimon Glass goto fail_bind; 1266494d708SSimon Glass 1276494d708SSimon Glass /* if we fail to bind we remove device from successors and free it */ 1286494d708SSimon Glass if (drv->bind) { 1296494d708SSimon Glass ret = drv->bind(dev); 1306494d708SSimon Glass if (ret) { 1316494d708SSimon Glass if (uclass_unbind_device(dev)) { 1326494d708SSimon Glass dm_warn("Failed to unbind dev '%s' on error path\n", 1336494d708SSimon Glass dev->name); 1346494d708SSimon Glass } 1356494d708SSimon Glass goto fail_bind; 1366494d708SSimon Glass } 1376494d708SSimon Glass } 1386494d708SSimon Glass if (parent) 1396494d708SSimon Glass dm_dbg("Bound device %s to %s\n", dev->name, parent->name); 1406494d708SSimon Glass *devp = dev; 1416494d708SSimon Glass 1426494d708SSimon Glass return 0; 1436494d708SSimon Glass 1446494d708SSimon Glass fail_bind: 1456494d708SSimon Glass list_del(&dev->sibling_node); 1466494d708SSimon Glass free(dev); 1476494d708SSimon Glass return ret; 1486494d708SSimon Glass } 1496494d708SSimon Glass 15000606d7eSSimon Glass int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, 15100606d7eSSimon Glass const struct driver_info *info, struct udevice **devp) 1526494d708SSimon Glass { 1536494d708SSimon Glass struct driver *drv; 1546494d708SSimon Glass 1556494d708SSimon Glass drv = lists_driver_lookup_name(info->name); 1566494d708SSimon Glass if (!drv) 1576494d708SSimon Glass return -ENOENT; 15800606d7eSSimon Glass if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) 15900606d7eSSimon Glass return -EPERM; 1606494d708SSimon Glass 1616494d708SSimon Glass return device_bind(parent, drv, info->name, (void *)info->platdata, 1626494d708SSimon Glass -1, devp); 1636494d708SSimon Glass } 1646494d708SSimon Glass 16554c5d08aSHeiko Schocher int device_unbind(struct udevice *dev) 1666494d708SSimon Glass { 1676494d708SSimon Glass struct driver *drv; 1686494d708SSimon Glass int ret; 1696494d708SSimon Glass 1706494d708SSimon Glass if (!dev) 1716494d708SSimon Glass return -EINVAL; 1726494d708SSimon Glass 1736494d708SSimon Glass if (dev->flags & DM_FLAG_ACTIVATED) 1746494d708SSimon Glass return -EINVAL; 1756494d708SSimon Glass 1766494d708SSimon Glass drv = dev->driver; 1776494d708SSimon Glass assert(drv); 1786494d708SSimon Glass 1796494d708SSimon Glass if (drv->unbind) { 1806494d708SSimon Glass ret = drv->unbind(dev); 1816494d708SSimon Glass if (ret) 1826494d708SSimon Glass return ret; 1836494d708SSimon Glass } 1846494d708SSimon Glass 1856494d708SSimon Glass ret = device_chld_unbind(dev); 1866494d708SSimon Glass if (ret) 1876494d708SSimon Glass return ret; 1886494d708SSimon Glass 1896494d708SSimon Glass ret = uclass_unbind_device(dev); 1906494d708SSimon Glass if (ret) 1916494d708SSimon Glass return ret; 1926494d708SSimon Glass 1936494d708SSimon Glass if (dev->parent) 1946494d708SSimon Glass list_del(&dev->sibling_node); 1956494d708SSimon Glass free(dev); 1966494d708SSimon Glass 1976494d708SSimon Glass return 0; 1986494d708SSimon Glass } 1996494d708SSimon Glass 2006494d708SSimon Glass /** 2016494d708SSimon Glass * device_free() - Free memory buffers allocated by a device 2026494d708SSimon Glass * @dev: Device that is to be started 2036494d708SSimon Glass */ 20454c5d08aSHeiko Schocher static void device_free(struct udevice *dev) 2056494d708SSimon Glass { 2066494d708SSimon Glass int size; 2076494d708SSimon Glass 2086494d708SSimon Glass if (dev->driver->priv_auto_alloc_size) { 2096494d708SSimon Glass free(dev->priv); 2106494d708SSimon Glass dev->priv = NULL; 2116494d708SSimon Glass } 2126494d708SSimon Glass if (dev->flags & DM_FLAG_ALLOC_PDATA) { 2136494d708SSimon Glass free(dev->platdata); 2146494d708SSimon Glass dev->platdata = NULL; 2156494d708SSimon Glass } 2166494d708SSimon Glass size = dev->uclass->uc_drv->per_device_auto_alloc_size; 2176494d708SSimon Glass if (size) { 2186494d708SSimon Glass free(dev->uclass_priv); 2196494d708SSimon Glass dev->uclass_priv = NULL; 2206494d708SSimon Glass } 2216494d708SSimon Glass } 2226494d708SSimon Glass 22354c5d08aSHeiko Schocher int device_probe(struct udevice *dev) 2246494d708SSimon Glass { 2256494d708SSimon Glass struct driver *drv; 2266494d708SSimon Glass int size = 0; 2276494d708SSimon Glass int ret; 2285a66a8ffSSimon Glass int seq; 2296494d708SSimon Glass 2306494d708SSimon Glass if (!dev) 2316494d708SSimon Glass return -EINVAL; 2326494d708SSimon Glass 2336494d708SSimon Glass if (dev->flags & DM_FLAG_ACTIVATED) 2346494d708SSimon Glass return 0; 2356494d708SSimon Glass 2366494d708SSimon Glass drv = dev->driver; 2376494d708SSimon Glass assert(drv); 2386494d708SSimon Glass 2396494d708SSimon Glass /* Allocate private data and platdata if requested */ 2406494d708SSimon Glass if (drv->priv_auto_alloc_size) { 2416494d708SSimon Glass dev->priv = calloc(1, drv->priv_auto_alloc_size); 2426494d708SSimon Glass if (!dev->priv) { 2436494d708SSimon Glass ret = -ENOMEM; 2446494d708SSimon Glass goto fail; 2456494d708SSimon Glass } 2466494d708SSimon Glass } 2476494d708SSimon Glass /* Allocate private data if requested */ 2486494d708SSimon Glass if (dev->flags & DM_FLAG_ALLOC_PDATA) { 2496494d708SSimon Glass dev->platdata = calloc(1, drv->platdata_auto_alloc_size); 2506494d708SSimon Glass if (!dev->platdata) { 2516494d708SSimon Glass ret = -ENOMEM; 2526494d708SSimon Glass goto fail; 2536494d708SSimon Glass } 2546494d708SSimon Glass } 2556494d708SSimon Glass size = dev->uclass->uc_drv->per_device_auto_alloc_size; 2566494d708SSimon Glass if (size) { 2576494d708SSimon Glass dev->uclass_priv = calloc(1, size); 2586494d708SSimon Glass if (!dev->uclass_priv) { 2596494d708SSimon Glass ret = -ENOMEM; 2606494d708SSimon Glass goto fail; 2616494d708SSimon Glass } 2626494d708SSimon Glass } 2636494d708SSimon Glass 2646494d708SSimon Glass /* Ensure all parents are probed */ 2656494d708SSimon Glass if (dev->parent) { 2666494d708SSimon Glass ret = device_probe(dev->parent); 2676494d708SSimon Glass if (ret) 2686494d708SSimon Glass goto fail; 2696494d708SSimon Glass } 2706494d708SSimon Glass 2715a66a8ffSSimon Glass seq = uclass_resolve_seq(dev); 2725a66a8ffSSimon Glass if (seq < 0) { 2735a66a8ffSSimon Glass ret = seq; 2745a66a8ffSSimon Glass goto fail; 2755a66a8ffSSimon Glass } 2765a66a8ffSSimon Glass dev->seq = seq; 2775a66a8ffSSimon Glass 2786494d708SSimon Glass if (drv->ofdata_to_platdata && dev->of_offset >= 0) { 2796494d708SSimon Glass ret = drv->ofdata_to_platdata(dev); 2806494d708SSimon Glass if (ret) 2816494d708SSimon Glass goto fail; 2826494d708SSimon Glass } 2836494d708SSimon Glass 2846494d708SSimon Glass if (drv->probe) { 2856494d708SSimon Glass ret = drv->probe(dev); 2866494d708SSimon Glass if (ret) 2876494d708SSimon Glass goto fail; 2886494d708SSimon Glass } 2896494d708SSimon Glass 2906494d708SSimon Glass dev->flags |= DM_FLAG_ACTIVATED; 2916494d708SSimon Glass 2926494d708SSimon Glass ret = uclass_post_probe_device(dev); 2936494d708SSimon Glass if (ret) { 2946494d708SSimon Glass dev->flags &= ~DM_FLAG_ACTIVATED; 2956494d708SSimon Glass goto fail_uclass; 2966494d708SSimon Glass } 2976494d708SSimon Glass 2986494d708SSimon Glass return 0; 2996494d708SSimon Glass fail_uclass: 3006494d708SSimon Glass if (device_remove(dev)) { 3016494d708SSimon Glass dm_warn("%s: Device '%s' failed to remove on error path\n", 3026494d708SSimon Glass __func__, dev->name); 3036494d708SSimon Glass } 3046494d708SSimon Glass fail: 3055a66a8ffSSimon Glass dev->seq = -1; 3066494d708SSimon Glass device_free(dev); 3076494d708SSimon Glass 3086494d708SSimon Glass return ret; 3096494d708SSimon Glass } 3106494d708SSimon Glass 31154c5d08aSHeiko Schocher int device_remove(struct udevice *dev) 3126494d708SSimon Glass { 3136494d708SSimon Glass struct driver *drv; 3146494d708SSimon Glass int ret; 3156494d708SSimon Glass 3166494d708SSimon Glass if (!dev) 3176494d708SSimon Glass return -EINVAL; 3186494d708SSimon Glass 3196494d708SSimon Glass if (!(dev->flags & DM_FLAG_ACTIVATED)) 3206494d708SSimon Glass return 0; 3216494d708SSimon Glass 3226494d708SSimon Glass drv = dev->driver; 3236494d708SSimon Glass assert(drv); 3246494d708SSimon Glass 3256494d708SSimon Glass ret = uclass_pre_remove_device(dev); 3266494d708SSimon Glass if (ret) 3276494d708SSimon Glass return ret; 3286494d708SSimon Glass 3296494d708SSimon Glass ret = device_chld_remove(dev); 3306494d708SSimon Glass if (ret) 3316494d708SSimon Glass goto err; 3326494d708SSimon Glass 3336494d708SSimon Glass if (drv->remove) { 3346494d708SSimon Glass ret = drv->remove(dev); 3356494d708SSimon Glass if (ret) 3366494d708SSimon Glass goto err_remove; 3376494d708SSimon Glass } 3386494d708SSimon Glass 3396494d708SSimon Glass device_free(dev); 3406494d708SSimon Glass 3415a66a8ffSSimon Glass dev->seq = -1; 3426494d708SSimon Glass dev->flags &= ~DM_FLAG_ACTIVATED; 3436494d708SSimon Glass 3446494d708SSimon Glass return 0; 3456494d708SSimon Glass 3466494d708SSimon Glass err_remove: 3476494d708SSimon Glass /* We can't put the children back */ 3486494d708SSimon Glass dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 3496494d708SSimon Glass __func__, dev->name); 3506494d708SSimon Glass err: 3516494d708SSimon Glass ret = uclass_post_probe_device(dev); 3526494d708SSimon Glass if (ret) { 3536494d708SSimon Glass dm_warn("%s: Device '%s' failed to post_probe on error path\n", 3546494d708SSimon Glass __func__, dev->name); 3556494d708SSimon Glass } 3566494d708SSimon Glass 3576494d708SSimon Glass return ret; 3586494d708SSimon Glass } 3596494d708SSimon Glass 36054c5d08aSHeiko Schocher void *dev_get_platdata(struct udevice *dev) 3616494d708SSimon Glass { 3626494d708SSimon Glass if (!dev) { 3636494d708SSimon Glass dm_warn("%s: null device", __func__); 3646494d708SSimon Glass return NULL; 3656494d708SSimon Glass } 3666494d708SSimon Glass 3676494d708SSimon Glass return dev->platdata; 3686494d708SSimon Glass } 3696494d708SSimon Glass 37054c5d08aSHeiko Schocher void *dev_get_priv(struct udevice *dev) 3716494d708SSimon Glass { 3726494d708SSimon Glass if (!dev) { 3736494d708SSimon Glass dm_warn("%s: null device", __func__); 3746494d708SSimon Glass return NULL; 3756494d708SSimon Glass } 3766494d708SSimon Glass 3776494d708SSimon Glass return dev->priv; 3786494d708SSimon Glass } 379*997c87bbSSimon Glass 380*997c87bbSSimon Glass static int device_get_device_tail(struct udevice *dev, int ret, 381*997c87bbSSimon Glass struct udevice **devp) 382*997c87bbSSimon Glass { 383*997c87bbSSimon Glass if (ret) 384*997c87bbSSimon Glass return ret; 385*997c87bbSSimon Glass 386*997c87bbSSimon Glass ret = device_probe(dev); 387*997c87bbSSimon Glass if (ret) 388*997c87bbSSimon Glass return ret; 389*997c87bbSSimon Glass 390*997c87bbSSimon Glass *devp = dev; 391*997c87bbSSimon Glass 392*997c87bbSSimon Glass return 0; 393*997c87bbSSimon Glass } 394*997c87bbSSimon Glass 395*997c87bbSSimon Glass int device_get_child(struct udevice *parent, int index, struct udevice **devp) 396*997c87bbSSimon Glass { 397*997c87bbSSimon Glass struct udevice *dev; 398*997c87bbSSimon Glass 399*997c87bbSSimon Glass list_for_each_entry(dev, &parent->child_head, sibling_node) { 400*997c87bbSSimon Glass if (!index--) 401*997c87bbSSimon Glass return device_get_device_tail(dev, 0, devp); 402*997c87bbSSimon Glass } 403*997c87bbSSimon Glass 404*997c87bbSSimon Glass return -ENODEV; 405*997c87bbSSimon Glass } 406*997c87bbSSimon Glass 407*997c87bbSSimon Glass int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq, 408*997c87bbSSimon Glass bool find_req_seq, struct udevice **devp) 409*997c87bbSSimon Glass { 410*997c87bbSSimon Glass struct udevice *dev; 411*997c87bbSSimon Glass 412*997c87bbSSimon Glass *devp = NULL; 413*997c87bbSSimon Glass if (seq_or_req_seq == -1) 414*997c87bbSSimon Glass return -ENODEV; 415*997c87bbSSimon Glass 416*997c87bbSSimon Glass list_for_each_entry(dev, &parent->child_head, sibling_node) { 417*997c87bbSSimon Glass if ((find_req_seq ? dev->req_seq : dev->seq) == 418*997c87bbSSimon Glass seq_or_req_seq) { 419*997c87bbSSimon Glass *devp = dev; 420*997c87bbSSimon Glass return 0; 421*997c87bbSSimon Glass } 422*997c87bbSSimon Glass } 423*997c87bbSSimon Glass 424*997c87bbSSimon Glass return -ENODEV; 425*997c87bbSSimon Glass } 426*997c87bbSSimon Glass 427*997c87bbSSimon Glass int device_get_child_by_seq(struct udevice *parent, int seq, 428*997c87bbSSimon Glass struct udevice **devp) 429*997c87bbSSimon Glass { 430*997c87bbSSimon Glass struct udevice *dev; 431*997c87bbSSimon Glass int ret; 432*997c87bbSSimon Glass 433*997c87bbSSimon Glass *devp = NULL; 434*997c87bbSSimon Glass ret = device_find_child_by_seq(parent, seq, false, &dev); 435*997c87bbSSimon Glass if (ret == -ENODEV) { 436*997c87bbSSimon Glass /* 437*997c87bbSSimon Glass * We didn't find it in probed devices. See if there is one 438*997c87bbSSimon Glass * that will request this seq if probed. 439*997c87bbSSimon Glass */ 440*997c87bbSSimon Glass ret = device_find_child_by_seq(parent, seq, true, &dev); 441*997c87bbSSimon Glass } 442*997c87bbSSimon Glass return device_get_device_tail(dev, ret, devp); 443*997c87bbSSimon Glass } 444*997c87bbSSimon Glass 445*997c87bbSSimon Glass int device_find_child_by_of_offset(struct udevice *parent, int of_offset, 446*997c87bbSSimon Glass struct udevice **devp) 447*997c87bbSSimon Glass { 448*997c87bbSSimon Glass struct udevice *dev; 449*997c87bbSSimon Glass 450*997c87bbSSimon Glass *devp = NULL; 451*997c87bbSSimon Glass 452*997c87bbSSimon Glass list_for_each_entry(dev, &parent->child_head, sibling_node) { 453*997c87bbSSimon Glass if (dev->of_offset == of_offset) { 454*997c87bbSSimon Glass *devp = dev; 455*997c87bbSSimon Glass return 0; 456*997c87bbSSimon Glass } 457*997c87bbSSimon Glass } 458*997c87bbSSimon Glass 459*997c87bbSSimon Glass return -ENODEV; 460*997c87bbSSimon Glass } 461*997c87bbSSimon Glass 462*997c87bbSSimon Glass int device_get_child_by_of_offset(struct udevice *parent, int seq, 463*997c87bbSSimon Glass struct udevice **devp) 464*997c87bbSSimon Glass { 465*997c87bbSSimon Glass struct udevice *dev; 466*997c87bbSSimon Glass int ret; 467*997c87bbSSimon Glass 468*997c87bbSSimon Glass *devp = NULL; 469*997c87bbSSimon Glass ret = device_find_child_by_of_offset(parent, seq, &dev); 470*997c87bbSSimon Glass return device_get_device_tail(dev, ret, devp); 471*997c87bbSSimon Glass } 472