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> 136494d708SSimon Glass #include <malloc.h> 146494d708SSimon Glass #include <dm/device.h> 156494d708SSimon Glass #include <dm/device-internal.h> 166494d708SSimon Glass #include <dm/lists.h> 176494d708SSimon Glass #include <dm/platdata.h> 186494d708SSimon Glass #include <dm/uclass.h> 196494d708SSimon Glass #include <dm/uclass-internal.h> 206494d708SSimon Glass #include <dm/util.h> 216494d708SSimon Glass #include <linux/err.h> 226494d708SSimon Glass #include <linux/list.h> 236494d708SSimon Glass 246494d708SSimon Glass /** 256494d708SSimon Glass * device_chld_unbind() - Unbind all device's children from the device 266494d708SSimon Glass * 276494d708SSimon Glass * On error, the function continues to unbind all children, and reports the 286494d708SSimon Glass * first error. 296494d708SSimon Glass * 306494d708SSimon Glass * @dev: The device that is to be stripped of its children 316494d708SSimon Glass * @return 0 on success, -ve on error 326494d708SSimon Glass */ 3354c5d08aSHeiko Schocher static int device_chld_unbind(struct udevice *dev) 346494d708SSimon Glass { 3554c5d08aSHeiko Schocher struct udevice *pos, *n; 366494d708SSimon Glass int ret, saved_ret = 0; 376494d708SSimon Glass 386494d708SSimon Glass assert(dev); 396494d708SSimon Glass 406494d708SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 416494d708SSimon Glass ret = device_unbind(pos); 426494d708SSimon Glass if (ret && !saved_ret) 436494d708SSimon Glass saved_ret = ret; 446494d708SSimon Glass } 456494d708SSimon Glass 466494d708SSimon Glass return saved_ret; 476494d708SSimon Glass } 486494d708SSimon Glass 496494d708SSimon Glass /** 506494d708SSimon Glass * device_chld_remove() - Stop all device's children 516494d708SSimon Glass * @dev: The device whose children are to be removed 526494d708SSimon Glass * @return 0 on success, -ve on error 536494d708SSimon Glass */ 5454c5d08aSHeiko Schocher static int device_chld_remove(struct udevice *dev) 556494d708SSimon Glass { 5654c5d08aSHeiko Schocher struct udevice *pos, *n; 576494d708SSimon Glass int ret; 586494d708SSimon Glass 596494d708SSimon Glass assert(dev); 606494d708SSimon Glass 616494d708SSimon Glass list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) { 626494d708SSimon Glass ret = device_remove(pos); 636494d708SSimon Glass if (ret) 646494d708SSimon Glass return ret; 656494d708SSimon Glass } 666494d708SSimon Glass 676494d708SSimon Glass return 0; 686494d708SSimon Glass } 696494d708SSimon Glass 7054c5d08aSHeiko Schocher int device_bind(struct udevice *parent, struct driver *drv, const char *name, 7154c5d08aSHeiko Schocher void *platdata, int of_offset, struct udevice **devp) 726494d708SSimon Glass { 7354c5d08aSHeiko Schocher struct udevice *dev; 746494d708SSimon Glass struct uclass *uc; 756494d708SSimon Glass int ret = 0; 766494d708SSimon Glass 776494d708SSimon Glass *devp = NULL; 786494d708SSimon Glass if (!name) 796494d708SSimon Glass return -EINVAL; 806494d708SSimon Glass 816494d708SSimon Glass ret = uclass_get(drv->id, &uc); 826494d708SSimon Glass if (ret) 836494d708SSimon Glass return ret; 846494d708SSimon Glass 8554c5d08aSHeiko Schocher dev = calloc(1, sizeof(struct udevice)); 866494d708SSimon Glass if (!dev) 876494d708SSimon Glass return -ENOMEM; 886494d708SSimon Glass 896494d708SSimon Glass INIT_LIST_HEAD(&dev->sibling_node); 906494d708SSimon Glass INIT_LIST_HEAD(&dev->child_head); 916494d708SSimon Glass INIT_LIST_HEAD(&dev->uclass_node); 926494d708SSimon Glass dev->platdata = platdata; 936494d708SSimon Glass dev->name = name; 946494d708SSimon Glass dev->of_offset = of_offset; 956494d708SSimon Glass dev->parent = parent; 966494d708SSimon Glass dev->driver = drv; 976494d708SSimon Glass dev->uclass = uc; 986494d708SSimon Glass if (!dev->platdata && drv->platdata_auto_alloc_size) 996494d708SSimon Glass dev->flags |= DM_FLAG_ALLOC_PDATA; 1006494d708SSimon Glass 1016494d708SSimon Glass /* put dev into parent's successor list */ 1026494d708SSimon Glass if (parent) 1036494d708SSimon Glass list_add_tail(&dev->sibling_node, &parent->child_head); 1046494d708SSimon Glass 1056494d708SSimon Glass ret = uclass_bind_device(dev); 1066494d708SSimon Glass if (ret) 1076494d708SSimon Glass goto fail_bind; 1086494d708SSimon Glass 1096494d708SSimon Glass /* if we fail to bind we remove device from successors and free it */ 1106494d708SSimon Glass if (drv->bind) { 1116494d708SSimon Glass ret = drv->bind(dev); 1126494d708SSimon Glass if (ret) { 1136494d708SSimon Glass if (uclass_unbind_device(dev)) { 1146494d708SSimon Glass dm_warn("Failed to unbind dev '%s' on error path\n", 1156494d708SSimon Glass dev->name); 1166494d708SSimon Glass } 1176494d708SSimon Glass goto fail_bind; 1186494d708SSimon Glass } 1196494d708SSimon Glass } 1206494d708SSimon Glass if (parent) 1216494d708SSimon Glass dm_dbg("Bound device %s to %s\n", dev->name, parent->name); 1226494d708SSimon Glass *devp = dev; 1236494d708SSimon Glass 1246494d708SSimon Glass return 0; 1256494d708SSimon Glass 1266494d708SSimon Glass fail_bind: 1276494d708SSimon Glass list_del(&dev->sibling_node); 1286494d708SSimon Glass free(dev); 1296494d708SSimon Glass return ret; 1306494d708SSimon Glass } 1316494d708SSimon Glass 132*00606d7eSSimon Glass int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, 133*00606d7eSSimon Glass const struct driver_info *info, struct udevice **devp) 1346494d708SSimon Glass { 1356494d708SSimon Glass struct driver *drv; 1366494d708SSimon Glass 1376494d708SSimon Glass drv = lists_driver_lookup_name(info->name); 1386494d708SSimon Glass if (!drv) 1396494d708SSimon Glass return -ENOENT; 140*00606d7eSSimon Glass if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) 141*00606d7eSSimon Glass return -EPERM; 1426494d708SSimon Glass 1436494d708SSimon Glass return device_bind(parent, drv, info->name, (void *)info->platdata, 1446494d708SSimon Glass -1, devp); 1456494d708SSimon Glass } 1466494d708SSimon Glass 14754c5d08aSHeiko Schocher int device_unbind(struct udevice *dev) 1486494d708SSimon Glass { 1496494d708SSimon Glass struct driver *drv; 1506494d708SSimon Glass int ret; 1516494d708SSimon Glass 1526494d708SSimon Glass if (!dev) 1536494d708SSimon Glass return -EINVAL; 1546494d708SSimon Glass 1556494d708SSimon Glass if (dev->flags & DM_FLAG_ACTIVATED) 1566494d708SSimon Glass return -EINVAL; 1576494d708SSimon Glass 1586494d708SSimon Glass drv = dev->driver; 1596494d708SSimon Glass assert(drv); 1606494d708SSimon Glass 1616494d708SSimon Glass if (drv->unbind) { 1626494d708SSimon Glass ret = drv->unbind(dev); 1636494d708SSimon Glass if (ret) 1646494d708SSimon Glass return ret; 1656494d708SSimon Glass } 1666494d708SSimon Glass 1676494d708SSimon Glass ret = device_chld_unbind(dev); 1686494d708SSimon Glass if (ret) 1696494d708SSimon Glass return ret; 1706494d708SSimon Glass 1716494d708SSimon Glass ret = uclass_unbind_device(dev); 1726494d708SSimon Glass if (ret) 1736494d708SSimon Glass return ret; 1746494d708SSimon Glass 1756494d708SSimon Glass if (dev->parent) 1766494d708SSimon Glass list_del(&dev->sibling_node); 1776494d708SSimon Glass free(dev); 1786494d708SSimon Glass 1796494d708SSimon Glass return 0; 1806494d708SSimon Glass } 1816494d708SSimon Glass 1826494d708SSimon Glass /** 1836494d708SSimon Glass * device_free() - Free memory buffers allocated by a device 1846494d708SSimon Glass * @dev: Device that is to be started 1856494d708SSimon Glass */ 18654c5d08aSHeiko Schocher static void device_free(struct udevice *dev) 1876494d708SSimon Glass { 1886494d708SSimon Glass int size; 1896494d708SSimon Glass 1906494d708SSimon Glass if (dev->driver->priv_auto_alloc_size) { 1916494d708SSimon Glass free(dev->priv); 1926494d708SSimon Glass dev->priv = NULL; 1936494d708SSimon Glass } 1946494d708SSimon Glass if (dev->flags & DM_FLAG_ALLOC_PDATA) { 1956494d708SSimon Glass free(dev->platdata); 1966494d708SSimon Glass dev->platdata = NULL; 1976494d708SSimon Glass } 1986494d708SSimon Glass size = dev->uclass->uc_drv->per_device_auto_alloc_size; 1996494d708SSimon Glass if (size) { 2006494d708SSimon Glass free(dev->uclass_priv); 2016494d708SSimon Glass dev->uclass_priv = NULL; 2026494d708SSimon Glass } 2036494d708SSimon Glass } 2046494d708SSimon Glass 20554c5d08aSHeiko Schocher int device_probe(struct udevice *dev) 2066494d708SSimon Glass { 2076494d708SSimon Glass struct driver *drv; 2086494d708SSimon Glass int size = 0; 2096494d708SSimon Glass int ret; 2106494d708SSimon Glass 2116494d708SSimon Glass if (!dev) 2126494d708SSimon Glass return -EINVAL; 2136494d708SSimon Glass 2146494d708SSimon Glass if (dev->flags & DM_FLAG_ACTIVATED) 2156494d708SSimon Glass return 0; 2166494d708SSimon Glass 2176494d708SSimon Glass drv = dev->driver; 2186494d708SSimon Glass assert(drv); 2196494d708SSimon Glass 2206494d708SSimon Glass /* Allocate private data and platdata if requested */ 2216494d708SSimon Glass if (drv->priv_auto_alloc_size) { 2226494d708SSimon Glass dev->priv = calloc(1, drv->priv_auto_alloc_size); 2236494d708SSimon Glass if (!dev->priv) { 2246494d708SSimon Glass ret = -ENOMEM; 2256494d708SSimon Glass goto fail; 2266494d708SSimon Glass } 2276494d708SSimon Glass } 2286494d708SSimon Glass /* Allocate private data if requested */ 2296494d708SSimon Glass if (dev->flags & DM_FLAG_ALLOC_PDATA) { 2306494d708SSimon Glass dev->platdata = calloc(1, drv->platdata_auto_alloc_size); 2316494d708SSimon Glass if (!dev->platdata) { 2326494d708SSimon Glass ret = -ENOMEM; 2336494d708SSimon Glass goto fail; 2346494d708SSimon Glass } 2356494d708SSimon Glass } 2366494d708SSimon Glass size = dev->uclass->uc_drv->per_device_auto_alloc_size; 2376494d708SSimon Glass if (size) { 2386494d708SSimon Glass dev->uclass_priv = calloc(1, size); 2396494d708SSimon Glass if (!dev->uclass_priv) { 2406494d708SSimon Glass ret = -ENOMEM; 2416494d708SSimon Glass goto fail; 2426494d708SSimon Glass } 2436494d708SSimon Glass } 2446494d708SSimon Glass 2456494d708SSimon Glass /* Ensure all parents are probed */ 2466494d708SSimon Glass if (dev->parent) { 2476494d708SSimon Glass ret = device_probe(dev->parent); 2486494d708SSimon Glass if (ret) 2496494d708SSimon Glass goto fail; 2506494d708SSimon Glass } 2516494d708SSimon Glass 2526494d708SSimon Glass if (drv->ofdata_to_platdata && dev->of_offset >= 0) { 2536494d708SSimon Glass ret = drv->ofdata_to_platdata(dev); 2546494d708SSimon Glass if (ret) 2556494d708SSimon Glass goto fail; 2566494d708SSimon Glass } 2576494d708SSimon Glass 2586494d708SSimon Glass if (drv->probe) { 2596494d708SSimon Glass ret = drv->probe(dev); 2606494d708SSimon Glass if (ret) 2616494d708SSimon Glass goto fail; 2626494d708SSimon Glass } 2636494d708SSimon Glass 2646494d708SSimon Glass dev->flags |= DM_FLAG_ACTIVATED; 2656494d708SSimon Glass 2666494d708SSimon Glass ret = uclass_post_probe_device(dev); 2676494d708SSimon Glass if (ret) { 2686494d708SSimon Glass dev->flags &= ~DM_FLAG_ACTIVATED; 2696494d708SSimon Glass goto fail_uclass; 2706494d708SSimon Glass } 2716494d708SSimon Glass 2726494d708SSimon Glass return 0; 2736494d708SSimon Glass fail_uclass: 2746494d708SSimon Glass if (device_remove(dev)) { 2756494d708SSimon Glass dm_warn("%s: Device '%s' failed to remove on error path\n", 2766494d708SSimon Glass __func__, dev->name); 2776494d708SSimon Glass } 2786494d708SSimon Glass fail: 2796494d708SSimon Glass device_free(dev); 2806494d708SSimon Glass 2816494d708SSimon Glass return ret; 2826494d708SSimon Glass } 2836494d708SSimon Glass 28454c5d08aSHeiko Schocher int device_remove(struct udevice *dev) 2856494d708SSimon Glass { 2866494d708SSimon Glass struct driver *drv; 2876494d708SSimon Glass int ret; 2886494d708SSimon Glass 2896494d708SSimon Glass if (!dev) 2906494d708SSimon Glass return -EINVAL; 2916494d708SSimon Glass 2926494d708SSimon Glass if (!(dev->flags & DM_FLAG_ACTIVATED)) 2936494d708SSimon Glass return 0; 2946494d708SSimon Glass 2956494d708SSimon Glass drv = dev->driver; 2966494d708SSimon Glass assert(drv); 2976494d708SSimon Glass 2986494d708SSimon Glass ret = uclass_pre_remove_device(dev); 2996494d708SSimon Glass if (ret) 3006494d708SSimon Glass return ret; 3016494d708SSimon Glass 3026494d708SSimon Glass ret = device_chld_remove(dev); 3036494d708SSimon Glass if (ret) 3046494d708SSimon Glass goto err; 3056494d708SSimon Glass 3066494d708SSimon Glass if (drv->remove) { 3076494d708SSimon Glass ret = drv->remove(dev); 3086494d708SSimon Glass if (ret) 3096494d708SSimon Glass goto err_remove; 3106494d708SSimon Glass } 3116494d708SSimon Glass 3126494d708SSimon Glass device_free(dev); 3136494d708SSimon Glass 3146494d708SSimon Glass dev->flags &= ~DM_FLAG_ACTIVATED; 3156494d708SSimon Glass 3166494d708SSimon Glass return 0; 3176494d708SSimon Glass 3186494d708SSimon Glass err_remove: 3196494d708SSimon Glass /* We can't put the children back */ 3206494d708SSimon Glass dm_warn("%s: Device '%s' failed to remove, but children are gone\n", 3216494d708SSimon Glass __func__, dev->name); 3226494d708SSimon Glass err: 3236494d708SSimon Glass ret = uclass_post_probe_device(dev); 3246494d708SSimon Glass if (ret) { 3256494d708SSimon Glass dm_warn("%s: Device '%s' failed to post_probe on error path\n", 3266494d708SSimon Glass __func__, dev->name); 3276494d708SSimon Glass } 3286494d708SSimon Glass 3296494d708SSimon Glass return ret; 3306494d708SSimon Glass } 3316494d708SSimon Glass 33254c5d08aSHeiko Schocher void *dev_get_platdata(struct udevice *dev) 3336494d708SSimon Glass { 3346494d708SSimon Glass if (!dev) { 3356494d708SSimon Glass dm_warn("%s: null device", __func__); 3366494d708SSimon Glass return NULL; 3376494d708SSimon Glass } 3386494d708SSimon Glass 3396494d708SSimon Glass return dev->platdata; 3406494d708SSimon Glass } 3416494d708SSimon Glass 34254c5d08aSHeiko Schocher void *dev_get_priv(struct udevice *dev) 3436494d708SSimon Glass { 3446494d708SSimon Glass if (!dev) { 3456494d708SSimon Glass dm_warn("%s: null device", __func__); 3466494d708SSimon Glass return NULL; 3476494d708SSimon Glass } 3486494d708SSimon Glass 3496494d708SSimon Glass return dev->priv; 3506494d708SSimon Glass } 351