16494d708SSimon Glass /* 26494d708SSimon Glass * Copyright (c) 2013 Google, Inc 36494d708SSimon Glass * 46494d708SSimon Glass * (C) Copyright 2012 56494d708SSimon Glass * Pavel Herrmann <morpheus.ibis@gmail.com> 66494d708SSimon Glass * 76494d708SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 86494d708SSimon Glass */ 96494d708SSimon Glass 106494d708SSimon Glass #include <common.h> 116494d708SSimon Glass #include <errno.h> 1294f7afdfSSimon Glass #include <fdtdec.h> 136494d708SSimon Glass #include <malloc.h> 140e00a84cSMasahiro Yamada #include <linux/libfdt.h> 156494d708SSimon Glass #include <dm/device.h> 166494d708SSimon Glass #include <dm/device-internal.h> 176494d708SSimon Glass #include <dm/lists.h> 1819c8205eSSimon Glass #include <dm/of.h> 1919c8205eSSimon Glass #include <dm/of_access.h> 206494d708SSimon Glass #include <dm/platdata.h> 2119c8205eSSimon Glass #include <dm/read.h> 22fd536d81SJeroen Hofstee #include <dm/root.h> 236494d708SSimon Glass #include <dm/uclass.h> 246494d708SSimon Glass #include <dm/util.h> 256494d708SSimon Glass #include <linux/list.h> 266494d708SSimon Glass 276494d708SSimon Glass DECLARE_GLOBAL_DATA_PTR; 286494d708SSimon Glass 2966eaea6cSStefan Roese struct root_priv { 3066eaea6cSStefan Roese fdt_addr_t translation_offset; /* optional translation offset */ 3166eaea6cSStefan Roese }; 3266eaea6cSStefan Roese 336494d708SSimon Glass static const struct driver_info root_info = { 346494d708SSimon Glass .name = "root_driver", 356494d708SSimon Glass }; 366494d708SSimon Glass 3754c5d08aSHeiko Schocher struct udevice *dm_root(void) 386494d708SSimon Glass { 396494d708SSimon Glass if (!gd->dm_root) { 406494d708SSimon Glass dm_warn("Virtual root driver does not exist!\n"); 416494d708SSimon Glass return NULL; 426494d708SSimon Glass } 436494d708SSimon Glass 446494d708SSimon Glass return gd->dm_root; 456494d708SSimon Glass } 466494d708SSimon Glass 472f11cd91SSimon Glass void dm_fixup_for_gd_move(struct global_data *new_gd) 482f11cd91SSimon Glass { 492f11cd91SSimon Glass /* The sentinel node has moved, so update things that point to it */ 50b0d9512aSLokesh Vutla if (gd->dm_root) { 512f11cd91SSimon Glass new_gd->uclass_root.next->prev = &new_gd->uclass_root; 522f11cd91SSimon Glass new_gd->uclass_root.prev->next = &new_gd->uclass_root; 532f11cd91SSimon Glass } 54b0d9512aSLokesh Vutla } 552f11cd91SSimon Glass 5666eaea6cSStefan Roese fdt_addr_t dm_get_translation_offset(void) 5766eaea6cSStefan Roese { 5866eaea6cSStefan Roese struct udevice *root = dm_root(); 5966eaea6cSStefan Roese struct root_priv *priv = dev_get_priv(root); 6066eaea6cSStefan Roese 6166eaea6cSStefan Roese return priv->translation_offset; 6266eaea6cSStefan Roese } 6366eaea6cSStefan Roese 6466eaea6cSStefan Roese void dm_set_translation_offset(fdt_addr_t offs) 6566eaea6cSStefan Roese { 6666eaea6cSStefan Roese struct udevice *root = dm_root(); 6766eaea6cSStefan Roese struct root_priv *priv = dev_get_priv(root); 6866eaea6cSStefan Roese 6966eaea6cSStefan Roese priv->translation_offset = offs; 7066eaea6cSStefan Roese } 7166eaea6cSStefan Roese 72484fdf5bSMichal Simek #if defined(CONFIG_NEEDS_MANUAL_RELOC) 73484fdf5bSMichal Simek void fix_drivers(void) 74484fdf5bSMichal Simek { 75484fdf5bSMichal Simek struct driver *drv = 76484fdf5bSMichal Simek ll_entry_start(struct driver, driver); 77484fdf5bSMichal Simek const int n_ents = ll_entry_count(struct driver, driver); 78484fdf5bSMichal Simek struct driver *entry; 79484fdf5bSMichal Simek 80484fdf5bSMichal Simek for (entry = drv; entry != drv + n_ents; entry++) { 81484fdf5bSMichal Simek if (entry->of_match) 82484fdf5bSMichal Simek entry->of_match = (const struct udevice_id *) 83484fdf5bSMichal Simek ((u32)entry->of_match + gd->reloc_off); 84484fdf5bSMichal Simek if (entry->bind) 85484fdf5bSMichal Simek entry->bind += gd->reloc_off; 86484fdf5bSMichal Simek if (entry->probe) 87484fdf5bSMichal Simek entry->probe += gd->reloc_off; 88484fdf5bSMichal Simek if (entry->remove) 89484fdf5bSMichal Simek entry->remove += gd->reloc_off; 90484fdf5bSMichal Simek if (entry->unbind) 91484fdf5bSMichal Simek entry->unbind += gd->reloc_off; 92484fdf5bSMichal Simek if (entry->ofdata_to_platdata) 93484fdf5bSMichal Simek entry->ofdata_to_platdata += gd->reloc_off; 9431e1029aSMichal Simek if (entry->child_post_bind) 9531e1029aSMichal Simek entry->child_post_bind += gd->reloc_off; 96484fdf5bSMichal Simek if (entry->child_pre_probe) 97484fdf5bSMichal Simek entry->child_pre_probe += gd->reloc_off; 98484fdf5bSMichal Simek if (entry->child_post_remove) 99484fdf5bSMichal Simek entry->child_post_remove += gd->reloc_off; 100484fdf5bSMichal Simek /* OPS are fixed in every uclass post_probe function */ 101484fdf5bSMichal Simek if (entry->ops) 102484fdf5bSMichal Simek entry->ops += gd->reloc_off; 103484fdf5bSMichal Simek } 104484fdf5bSMichal Simek } 105484fdf5bSMichal Simek 106484fdf5bSMichal Simek void fix_uclass(void) 107484fdf5bSMichal Simek { 108484fdf5bSMichal Simek struct uclass_driver *uclass = 109484fdf5bSMichal Simek ll_entry_start(struct uclass_driver, uclass); 110484fdf5bSMichal Simek const int n_ents = ll_entry_count(struct uclass_driver, uclass); 111484fdf5bSMichal Simek struct uclass_driver *entry; 112484fdf5bSMichal Simek 113484fdf5bSMichal Simek for (entry = uclass; entry != uclass + n_ents; entry++) { 114484fdf5bSMichal Simek if (entry->post_bind) 115484fdf5bSMichal Simek entry->post_bind += gd->reloc_off; 116484fdf5bSMichal Simek if (entry->pre_unbind) 117484fdf5bSMichal Simek entry->pre_unbind += gd->reloc_off; 11831e1029aSMichal Simek if (entry->pre_probe) 11931e1029aSMichal Simek entry->pre_probe += gd->reloc_off; 120484fdf5bSMichal Simek if (entry->post_probe) 121484fdf5bSMichal Simek entry->post_probe += gd->reloc_off; 122484fdf5bSMichal Simek if (entry->pre_remove) 123484fdf5bSMichal Simek entry->pre_remove += gd->reloc_off; 12431e1029aSMichal Simek if (entry->child_post_bind) 12531e1029aSMichal Simek entry->child_post_bind += gd->reloc_off; 12631e1029aSMichal Simek if (entry->child_pre_probe) 12731e1029aSMichal Simek entry->child_pre_probe += gd->reloc_off; 128484fdf5bSMichal Simek if (entry->init) 129484fdf5bSMichal Simek entry->init += gd->reloc_off; 130484fdf5bSMichal Simek if (entry->destroy) 131484fdf5bSMichal Simek entry->destroy += gd->reloc_off; 132484fdf5bSMichal Simek /* FIXME maybe also need to fix these ops */ 133484fdf5bSMichal Simek if (entry->ops) 134484fdf5bSMichal Simek entry->ops += gd->reloc_off; 135484fdf5bSMichal Simek } 136484fdf5bSMichal Simek } 1375aeedebcSAngelo Dureghello 1385aeedebcSAngelo Dureghello void fix_devices(void) 1395aeedebcSAngelo Dureghello { 1405aeedebcSAngelo Dureghello struct driver_info *dev = 1415aeedebcSAngelo Dureghello ll_entry_start(struct driver_info, driver_info); 1425aeedebcSAngelo Dureghello const int n_ents = ll_entry_count(struct driver_info, driver_info); 1435aeedebcSAngelo Dureghello struct driver_info *entry; 1445aeedebcSAngelo Dureghello 1455aeedebcSAngelo Dureghello for (entry = dev; entry != dev + n_ents; entry++) { 1465aeedebcSAngelo Dureghello if (entry->platdata) 1475aeedebcSAngelo Dureghello entry->platdata += gd->reloc_off; 1485aeedebcSAngelo Dureghello } 1495aeedebcSAngelo Dureghello } 1505aeedebcSAngelo Dureghello 151484fdf5bSMichal Simek #endif 152484fdf5bSMichal Simek 15319c8205eSSimon Glass int dm_init(bool of_live) 1546494d708SSimon Glass { 1556494d708SSimon Glass int ret; 1566494d708SSimon Glass 1576494d708SSimon Glass if (gd->dm_root) { 1586494d708SSimon Glass dm_warn("Virtual root driver already exists!\n"); 1596494d708SSimon Glass return -EINVAL; 1606494d708SSimon Glass } 16189876a55SSimon Glass INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); 1626494d708SSimon Glass 163484fdf5bSMichal Simek #if defined(CONFIG_NEEDS_MANUAL_RELOC) 164484fdf5bSMichal Simek fix_drivers(); 165484fdf5bSMichal Simek fix_uclass(); 1665aeedebcSAngelo Dureghello fix_devices(); 167484fdf5bSMichal Simek #endif 168484fdf5bSMichal Simek 16900606d7eSSimon Glass ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); 1706494d708SSimon Glass if (ret) 1716494d708SSimon Glass return ret; 1720f925822SMasahiro Yamada #if CONFIG_IS_ENABLED(OF_CONTROL) 17319c8205eSSimon Glass # if CONFIG_IS_ENABLED(OF_LIVE) 17419c8205eSSimon Glass if (of_live) 17519c8205eSSimon Glass DM_ROOT_NON_CONST->node = np_to_ofnode(gd->of_root); 17619c8205eSSimon Glass else 17719c8205eSSimon Glass #endif 1784984de2bSSimon Glass DM_ROOT_NON_CONST->node = offset_to_ofnode(0); 1792f3b95dbSSimon Glass #endif 1807497812dSSimon Glass ret = device_probe(DM_ROOT_NON_CONST); 1817497812dSSimon Glass if (ret) 1827497812dSSimon Glass return ret; 1836494d708SSimon Glass 1846494d708SSimon Glass return 0; 1856494d708SSimon Glass } 1866494d708SSimon Glass 1879adbd7a1SSimon Glass int dm_uninit(void) 1889adbd7a1SSimon Glass { 189706865afSStefan Roese device_remove(dm_root(), DM_REMOVE_NORMAL); 1909adbd7a1SSimon Glass device_unbind(dm_root()); 1919adbd7a1SSimon Glass 1929adbd7a1SSimon Glass return 0; 1939adbd7a1SSimon Glass } 1949adbd7a1SSimon Glass 195bc85aa40SStefan Roese #if CONFIG_IS_ENABLED(DM_DEVICE_REMOVE) 196bc85aa40SStefan Roese int dm_remove_devices_flags(uint flags) 197bc85aa40SStefan Roese { 198bc85aa40SStefan Roese device_remove(dm_root(), flags); 199bc85aa40SStefan Roese 200bc85aa40SStefan Roese return 0; 201bc85aa40SStefan Roese } 202bc85aa40SStefan Roese #endif 203bc85aa40SStefan Roese 20400606d7eSSimon Glass int dm_scan_platdata(bool pre_reloc_only) 2056494d708SSimon Glass { 2066494d708SSimon Glass int ret; 2076494d708SSimon Glass 20800606d7eSSimon Glass ret = lists_bind_drivers(DM_ROOT_NON_CONST, pre_reloc_only); 2096494d708SSimon Glass if (ret == -ENOENT) { 2106494d708SSimon Glass dm_warn("Some drivers were not found\n"); 2116494d708SSimon Glass ret = 0; 2126494d708SSimon Glass } 2136494d708SSimon Glass 214cbf86d71SMasahiro Yamada return ret; 2156494d708SSimon Glass } 2166494d708SSimon Glass 21719c8205eSSimon Glass #if CONFIG_IS_ENABLED(OF_LIVE) 21819c8205eSSimon Glass static int dm_scan_fdt_live(struct udevice *parent, 21919c8205eSSimon Glass const struct device_node *node_parent, 22019c8205eSSimon Glass bool pre_reloc_only) 22119c8205eSSimon Glass { 22219c8205eSSimon Glass struct device_node *np; 22319c8205eSSimon Glass int ret = 0, err; 22419c8205eSSimon Glass 22519c8205eSSimon Glass for (np = node_parent->child; np; np = np->sibling) { 22619c8205eSSimon Glass if (pre_reloc_only && 227930ceb12SJoseph Chen #ifdef CONFIG_USING_KERNEL_DTB 228b455f42cSJoseph Chen (!of_find_property(np, "u-boot,dm-pre-reloc", NULL) && 229930ceb12SJoseph Chen !of_find_property(np, "u-boot,dm-spl", NULL))) 230930ceb12SJoseph Chen #else 23119c8205eSSimon Glass !of_find_property(np, "u-boot,dm-pre-reloc", NULL)) 232930ceb12SJoseph Chen #endif 23319c8205eSSimon Glass continue; 23419c8205eSSimon Glass if (!of_device_is_available(np)) { 235d42197e4SMasahiro Yamada pr_debug(" - ignoring disabled device\n"); 23619c8205eSSimon Glass continue; 23719c8205eSSimon Glass } 23819c8205eSSimon Glass err = lists_bind_fdt(parent, np_to_ofnode(np), NULL); 23919c8205eSSimon Glass if (err && !ret) { 24019c8205eSSimon Glass ret = err; 24119c8205eSSimon Glass debug("%s: ret=%d\n", np->name, ret); 24219c8205eSSimon Glass } 243*5120375cSJoseph Chen 244*5120375cSJoseph Chen if (!pre_reloc_only && !strcmp(np->name, "firmware")) 245*5120375cSJoseph Chen ret = device_bind_driver_to_node(gd->dm_root, 246*5120375cSJoseph Chen "firmware", np->name, np_to_ofnode(np), NULL); 24719c8205eSSimon Glass } 24819c8205eSSimon Glass 24919c8205eSSimon Glass if (ret) 25019c8205eSSimon Glass dm_warn("Some drivers failed to bind\n"); 25119c8205eSSimon Glass 25219c8205eSSimon Glass return ret; 25319c8205eSSimon Glass } 25419c8205eSSimon Glass #endif /* CONFIG_IS_ENABLED(OF_LIVE) */ 25519c8205eSSimon Glass 25629629eb8SSimon Glass #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) 257a771a04fSSimon Glass /** 258a771a04fSSimon Glass * dm_scan_fdt_node() - Scan the device tree and bind drivers for a node 259a771a04fSSimon Glass * 260a771a04fSSimon Glass * This scans the subnodes of a device tree node and and creates a driver 261a771a04fSSimon Glass * for each one. 262a771a04fSSimon Glass * 263a771a04fSSimon Glass * @parent: Parent device for the devices that will be created 264a771a04fSSimon Glass * @blob: Pointer to device tree blob 265a771a04fSSimon Glass * @offset: Offset of node to scan 266a771a04fSSimon Glass * @pre_reloc_only: If true, bind only drivers with the DM_FLAG_PRE_RELOC 267a771a04fSSimon Glass * flag. If false bind all drivers. 268a771a04fSSimon Glass * @return 0 if OK, -ve on error 269a771a04fSSimon Glass */ 270a771a04fSSimon Glass static int dm_scan_fdt_node(struct udevice *parent, const void *blob, 271a771a04fSSimon Glass int offset, bool pre_reloc_only) 2726494d708SSimon Glass { 2736494d708SSimon Glass int ret = 0, err; 2746494d708SSimon Glass 2751ca7e206SSimon Glass for (offset = fdt_first_subnode(blob, offset); 2761ca7e206SSimon Glass offset > 0; 2771ca7e206SSimon Glass offset = fdt_next_subnode(blob, offset)) { 27800606d7eSSimon Glass if (pre_reloc_only && 27927326c7eSHeiko Stübner !dm_fdt_pre_reloc(blob, offset)) 28000606d7eSSimon Glass continue; 28194f7afdfSSimon Glass if (!fdtdec_get_is_enabled(blob, offset)) { 282d42197e4SMasahiro Yamada pr_debug(" - ignoring disabled device\n"); 28394f7afdfSSimon Glass continue; 28494f7afdfSSimon Glass } 285f5b5719cSSimon Glass err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL); 286bc7b2f43SSimon Glass if (err && !ret) { 2876494d708SSimon Glass ret = err; 288bc7b2f43SSimon Glass debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL), 289bc7b2f43SSimon Glass ret); 290bc7b2f43SSimon Glass } 2916494d708SSimon Glass } 2926494d708SSimon Glass 2936494d708SSimon Glass if (ret) 2946494d708SSimon Glass dm_warn("Some drivers failed to bind\n"); 2956494d708SSimon Glass 2966494d708SSimon Glass return ret; 2976494d708SSimon Glass } 2981ca7e206SSimon Glass 299cc7f66f7SSimon Glass int dm_scan_fdt_dev(struct udevice *dev) 300cc7f66f7SSimon Glass { 30119c8205eSSimon Glass if (!dev_of_valid(dev)) 302cc7f66f7SSimon Glass return 0; 303cc7f66f7SSimon Glass 30419c8205eSSimon Glass #if CONFIG_IS_ENABLED(OF_LIVE) 30519c8205eSSimon Glass if (of_live_active()) 30619c8205eSSimon Glass return dm_scan_fdt_live(dev, dev_np(dev), 30719c8205eSSimon Glass gd->flags & GD_FLG_RELOC ? false : true); 30819c8205eSSimon Glass else 30919c8205eSSimon Glass #endif 310e160f7d4SSimon Glass return dm_scan_fdt_node(dev, gd->fdt_blob, dev_of_offset(dev), 311cc7f66f7SSimon Glass gd->flags & GD_FLG_RELOC ? false : true); 312cc7f66f7SSimon Glass } 313cc7f66f7SSimon Glass 3141ca7e206SSimon Glass int dm_scan_fdt(const void *blob, bool pre_reloc_only) 3151ca7e206SSimon Glass { 31619c8205eSSimon Glass #if CONFIG_IS_ENABLED(OF_LIVE) 31719c8205eSSimon Glass if (of_live_active()) 31819c8205eSSimon Glass return dm_scan_fdt_live(gd->dm_root, gd->of_root, 31919c8205eSSimon Glass pre_reloc_only); 32019c8205eSSimon Glass else 32119c8205eSSimon Glass #endif 3221ca7e206SSimon Glass return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); 3231ca7e206SSimon Glass } 32446fb92f0SPatrice Chotard #else 32546fb92f0SPatrice Chotard static int dm_scan_fdt_node(struct udevice *parent, const void *blob, 32646fb92f0SPatrice Chotard int offset, bool pre_reloc_only) 32746fb92f0SPatrice Chotard { 32846fb92f0SPatrice Chotard return 0; 32946fb92f0SPatrice Chotard } 3306494d708SSimon Glass #endif 3316494d708SSimon Glass 33246fb92f0SPatrice Chotard int dm_extended_scan_fdt(const void *blob, bool pre_reloc_only) 33346fb92f0SPatrice Chotard { 33446fb92f0SPatrice Chotard int node, ret; 33546fb92f0SPatrice Chotard 33646fb92f0SPatrice Chotard ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); 33746fb92f0SPatrice Chotard if (ret) { 33846fb92f0SPatrice Chotard debug("dm_scan_fdt() failed: %d\n", ret); 33946fb92f0SPatrice Chotard return ret; 34046fb92f0SPatrice Chotard } 34146fb92f0SPatrice Chotard 34246fb92f0SPatrice Chotard /* bind fixed-clock */ 34346fb92f0SPatrice Chotard node = ofnode_to_offset(ofnode_path("/clocks")); 34446fb92f0SPatrice Chotard /* if no DT "clocks" node, no need to go further */ 34546fb92f0SPatrice Chotard if (node < 0) 34646fb92f0SPatrice Chotard return ret; 34746fb92f0SPatrice Chotard 34846fb92f0SPatrice Chotard ret = dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, node, 34946fb92f0SPatrice Chotard pre_reloc_only); 35046fb92f0SPatrice Chotard if (ret) 35146fb92f0SPatrice Chotard debug("dm_scan_fdt_node() failed: %d\n", ret); 35246fb92f0SPatrice Chotard 35346fb92f0SPatrice Chotard return ret; 35446fb92f0SPatrice Chotard } 35546fb92f0SPatrice Chotard 356bb58503dSSimon Glass __weak int dm_scan_other(bool pre_reloc_only) 357bb58503dSSimon Glass { 358bb58503dSSimon Glass return 0; 359bb58503dSSimon Glass } 360bb58503dSSimon Glass 361ab7cd627SSimon Glass int dm_init_and_scan(bool pre_reloc_only) 362ab7cd627SSimon Glass { 363ab7cd627SSimon Glass int ret; 364ab7cd627SSimon Glass 36519c8205eSSimon Glass ret = dm_init(IS_ENABLED(CONFIG_OF_LIVE)); 366ab7cd627SSimon Glass if (ret) { 367ab7cd627SSimon Glass debug("dm_init() failed: %d\n", ret); 368ab7cd627SSimon Glass return ret; 369ab7cd627SSimon Glass } 370ab7cd627SSimon Glass ret = dm_scan_platdata(pre_reloc_only); 371ab7cd627SSimon Glass if (ret) { 372ab7cd627SSimon Glass debug("dm_scan_platdata() failed: %d\n", ret); 373ab7cd627SSimon Glass return ret; 374ab7cd627SSimon Glass } 375b2b0d3e7SSimon Glass 37629629eb8SSimon Glass if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { 37746fb92f0SPatrice Chotard ret = dm_extended_scan_fdt(gd->fdt_blob, pre_reloc_only); 378ab7cd627SSimon Glass if (ret) { 37946fb92f0SPatrice Chotard debug("dm_extended_scan_dt() failed: %d\n", ret); 380ab7cd627SSimon Glass return ret; 381ab7cd627SSimon Glass } 382b2b0d3e7SSimon Glass } 383b2b0d3e7SSimon Glass 384bb58503dSSimon Glass ret = dm_scan_other(pre_reloc_only); 385bb58503dSSimon Glass if (ret) 386bb58503dSSimon Glass return ret; 387ab7cd627SSimon Glass 388ab7cd627SSimon Glass return 0; 389ab7cd627SSimon Glass } 390ab7cd627SSimon Glass 3916494d708SSimon Glass /* This is the root driver - all drivers are children of this */ 3926494d708SSimon Glass U_BOOT_DRIVER(root_driver) = { 3936494d708SSimon Glass .name = "root_driver", 3946494d708SSimon Glass .id = UCLASS_ROOT, 39566eaea6cSStefan Roese .priv_auto_alloc_size = sizeof(struct root_priv), 3966494d708SSimon Glass }; 3976494d708SSimon Glass 3986494d708SSimon Glass /* This is the root uclass */ 3996494d708SSimon Glass UCLASS_DRIVER(root) = { 4006494d708SSimon Glass .name = "root", 4016494d708SSimon Glass .id = UCLASS_ROOT, 4026494d708SSimon Glass }; 403