1 /* 2 * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <mmc.h> 10 #include <nand.h> 11 #include <spl.h> 12 13 #if CONFIG_IS_ENABLED(OF_CONTROL) && ! CONFIG_IS_ENABLED(OF_PLATDATA) 14 /** 15 * spl_node_to_boot_device() - maps from a DT-node to a SPL boot device 16 * @node: of_offset of the node 17 * 18 * The SPL framework uses BOOT_DEVICE_... constants to identify its boot 19 * sources. These may take on a device-specific meaning, depending on 20 * what nodes are enabled in a DTS (e.g. BOOT_DEVICE_MMC1 may refer to 21 * different controllers/block-devices, depending on which SD/MMC controllers 22 * are enabled in any given DTS). This function maps from a DT-node back 23 * onto a BOOT_DEVICE_... constant, considering the currently active devices. 24 * 25 * Returns 26 * -ENOENT, if no device matching the node could be found 27 * -ENOSYS, if the device matching the node can not be mapped onto a 28 * SPL boot device (e.g. the third MMC device) 29 * -1, for unspecified failures 30 * a positive integer (from the BOOT_DEVICE_... family) on succes. 31 */ 32 33 static int spl_node_to_boot_device(int node) 34 { 35 struct udevice *parent; 36 37 /* 38 * This should eventually move into the SPL code, once SPL becomes 39 * aware of the block-device layer. Until then (and to avoid unneeded 40 * delays in getting this feature out, it lives at the board-level). 41 */ 42 if (!uclass_get_device_by_of_offset(UCLASS_MMC, node, &parent)) { 43 struct udevice *dev; 44 struct blk_desc *desc = NULL; 45 46 for (device_find_first_child(parent, &dev); 47 dev; 48 device_find_next_child(&dev)) { 49 if (device_get_uclass_id(dev) == UCLASS_BLK) { 50 desc = dev_get_uclass_platdata(dev); 51 break; 52 } 53 } 54 55 if (!desc) 56 return -ENOENT; 57 58 switch (desc->devnum) { 59 case 0: 60 return BOOT_DEVICE_MMC1; 61 case 1: 62 return BOOT_DEVICE_MMC2; 63 default: 64 return -ENOSYS; 65 } 66 } 67 68 /* 69 * SPL doesn't differentiate SPI flashes, so we keep the detection 70 * brief and inaccurate... hopefully, the common SPL layer can be 71 * extended with awareness of the BLK layer (and matching OF_CONTROL) 72 * soon. 73 */ 74 if (!uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node, &parent)) 75 #ifndef CONFIG_SPL_MTD_SUPPORT 76 return BOOT_DEVICE_SPI; 77 #else 78 return BOOT_DEVICE_MTD_BLK_SPI_NOR; 79 if (!uclass_get_device_by_of_offset(UCLASS_MTD, node, &parent)) 80 return BOOT_DEVICE_MTD_BLK_SPI_NAND; 81 #endif 82 83 #ifdef CONFIG_SPL_NAND_SUPPORT 84 if (!rk_nand_init()) 85 return BOOT_DEVICE_NAND; 86 #endif 87 88 return -1; 89 } 90 91 /** 92 * board_spl_was_booted_from() - retrieves the of-path the SPL was loaded from 93 * 94 * To support a 'same-as-spl' specification in the search-order for the next 95 * stage, we need a SoC- or board-specific way to handshake with what 'came 96 * before us' (either a BROM or TPL stage) and map the info retrieved onto 97 * a OF path. 98 * 99 * Returns 100 * NULL, on failure or if the device could not be identified 101 * a of_path (a string), on success 102 */ 103 __weak const char *board_spl_was_booted_from(void) 104 { 105 debug("%s: no support for 'same-as-spl' for this board\n", __func__); 106 return NULL; 107 } 108 109 void board_boot_order(u32 *spl_boot_list) 110 { 111 const void *blob = gd->fdt_blob; 112 int chosen_node = fdt_path_offset(blob, "/chosen"); 113 int idx = 0; 114 int elem; 115 int boot_device; 116 int node; 117 const char *conf; 118 119 if (chosen_node < 0) { 120 debug("%s: /chosen not found, using spl_boot_device()\n", 121 __func__); 122 spl_boot_list[0] = spl_boot_device(); 123 return; 124 } 125 126 for (elem = 0; 127 (conf = fdt_stringlist_get(blob, chosen_node, 128 "u-boot,spl-boot-order", elem, NULL)); 129 elem++) { 130 const char *alias; 131 132 /* Handle the case of 'same device the SPL was loaded from' */ 133 if (strncmp(conf, "same-as-spl", 11) == 0) { 134 conf = board_spl_was_booted_from(); 135 if (!conf) 136 continue; 137 } 138 139 /* First check if the list element is an alias */ 140 alias = fdt_get_alias(blob, conf); 141 if (alias) 142 conf = alias; 143 144 /* Try to resolve the config item (or alias) as a path */ 145 node = fdt_path_offset(blob, conf); 146 if (node < 0) { 147 debug("%s: could not find %s in FDT", __func__, conf); 148 continue; 149 } 150 151 /* Try to map this back onto SPL boot devices */ 152 boot_device = spl_node_to_boot_device(node); 153 if (boot_device < 0) { 154 debug("%s: could not map node @%x to a boot-device\n", 155 __func__, node); 156 continue; 157 } 158 159 spl_boot_list[idx++] = boot_device; 160 } 161 162 /* If we had no matches, fall back to spl_boot_device */ 163 if (idx == 0) 164 spl_boot_list[0] = spl_boot_device(); 165 } 166 #endif 167