Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH */ #include <dm.h> #include <fdt_support.h> #include <log.h> #include <mmc.h> #include <spl.h> #include <asm/arch-rockchip/bootrom.h> #include <asm/global_data.h> #include <dm/uclass-internal.h> #if CONFIG_IS_ENABLED(OF_LIBFDT) /** * spl_node_to_boot_device() - maps from a DT-node to a SPL boot device * @node: of_offset of the node * * The SPL framework uses BOOT_DEVICE_... constants to identify its boot * sources. These may take on a device-specific meaning, depending on * what nodes are enabled in a DTS (e.g. BOOT_DEVICE_MMC1 may refer to * different controllers/block-devices, depending on which SD/MMC controllers * are enabled in any given DTS). This function maps from a DT-node back * onto a BOOT_DEVICE_... constant, considering the currently active devices. * * Returns * -ENOENT, if no device matching the node could be found * -ENOSYS, if the device matching the node can not be mapped onto a * SPL boot device (e.g. the third MMC device) * -1, for unspecified failures * a positive integer (from the BOOT_DEVICE_... family) on success. */ static int spl_node_to_boot_device(int node) { struct udevice *parent; /* * This should eventually move into the SPL code, once SPL becomes * aware of the block-device layer. Until then (and to avoid unneeded * delays in getting this feature out), it lives at the board-level. */ if (!uclass_find_device_by_of_offset(UCLASS_MMC, node, &parent)) { struct udevice *dev; struct blk_desc *desc = NULL; for (device_find_first_child(parent, &dev); dev; device_find_next_child(&dev)) { if (device_get_uclass_id(dev) == UCLASS_BLK) { desc = dev_get_uclass_plat(dev); break; } } if (!desc) return -ENOENT; switch (desc->devnum) { case 0: return BOOT_DEVICE_MMC1; case 1: return BOOT_DEVICE_MMC2; default: return -ENOSYS; } } /* * SPL doesn't differentiate SPI flashes, so we keep the detection * brief and inaccurate... hopefully, the common SPL layer can be * extended with awareness of the BLK layer (and matching OF_CONTROL) * soon. */ if (!uclass_find_device_by_of_offset(UCLASS_SPI_FLASH, node, &parent)) return BOOT_DEVICE_SPI; return -1; } /** * board_spl_was_booted_from() - retrieves the of-path the SPL was loaded from * * To support a 'same-as-spl' specification in the search-order for the next * stage, we need a SoC- or board-specific way to handshake with what 'came * before us' (either a BROM or TPL stage) and map the info retrieved onto * a OF path. * * Returns * NULL, on failure or if the device could not be identified * a of_path (a string), on success */ __weak const char *board_spl_was_booted_from(void) { debug("%s: no support for 'same-as-spl' for this board\n", __func__); return NULL; } void board_boot_order(u32 *spl_boot_list) { int idx = 0; /* Add RAM boot for maskrom mode boot over USB */ if (BROM_BOOTSOURCE_ID_ADDR && CONFIG_IS_ENABLED(RAM_DEVICE) && read_brom_bootsource_id() == BROM_BOOTSOURCE_USB) { spl_boot_list[idx++] = BOOT_DEVICE_RAM; } /* In case of no fdt (or only plat), use spl_boot_device() */ if (!CONFIG_IS_ENABLED(OF_CONTROL) || CONFIG_IS_ENABLED(OF_PLATDATA)) { spl_boot_list[idx++] = spl_boot_device(); return; } const void *blob = gd->fdt_blob; int chosen_node = fdt_path_offset(blob, "/chosen"); int elem; int boot_device; int node; const char *conf; if (chosen_node < 0) { debug("%s: /chosen not found, using spl_boot_device()\n", __func__); spl_boot_list[idx++] = spl_boot_device(); return; } for (elem = 0; (conf = fdt_stringlist_get(blob, chosen_node, "u-boot,spl-boot-order", elem, NULL)); elem++) { const char *alias; /* Handle the case of 'same device the SPL was loaded from' */ if (strncmp(conf, "same-as-spl", 11) == 0) { conf = board_spl_was_booted_from(); if (!conf) continue; } /* First check if the list element is an alias */ alias = fdt_get_alias(blob, conf); if (alias) conf = alias; /* Try to resolve the config item (or alias) as a path */ node = fdt_path_offset(blob, conf); if (node < 0) { debug("%s: could not find %s in FDT\n", __func__, conf); continue; } /* Try to map this back onto SPL boot devices */ boot_device = spl_node_to_boot_device(node); if (boot_device < 0) { debug("%s: could not map node %s to a boot-device\n", __func__, conf); continue; } spl_boot_list[idx++] = boot_device; } /* If we had no matches, fall back to spl_boot_device */ if (idx == 0) spl_boot_list[0] = spl_boot_device(); } int spl_decode_boot_device(u32 boot_device, char *buf, size_t buflen) { struct udevice *dev; #if CONFIG_IS_ENABLED(BLK) int dev_num; #endif int ret; if (boot_device == BOOT_DEVICE_SPI) { /* Revert spl_node_to_boot_device() logic to find appropriate SPI flash device */ /* * Devices with multiple SPI flash devices will take the first SPI flash found in * /chosen/u-boot,spl-boot-order. */ const void *blob = gd->fdt_blob; int chosen_node = fdt_path_offset(blob, "/chosen"); int elem; int node; const char *conf; if (chosen_node < 0) { debug("%s: /chosen not found\n", __func__); return -ENODEV; } for (elem = 0; (conf = fdt_stringlist_get(blob, chosen_node, "u-boot,spl-boot-order", elem, NULL)); elem++) { const char *alias; /* Handle the case of 'same device the SPL was loaded from' */ if (strncmp(conf, "same-as-spl", 11) == 0) { conf = board_spl_was_booted_from(); if (!conf) continue; } /* First check if the list element is an alias */ alias = fdt_get_alias(blob, conf); if (alias) conf = alias; /* Try to resolve the config item (or alias) as a path */ node = fdt_path_offset(blob, conf); if (node < 0) { debug("%s: could not find %s in FDT\n", __func__, conf); continue; } ret = uclass_find_device_by_of_offset(UCLASS_SPI_FLASH, node, &dev); if (ret) { debug("%s: could not find udevice for %s\n", __func__, conf); continue; } return ofnode_get_path(dev_ofnode(dev), buf, buflen); } return -ENODEV; } #if CONFIG_IS_ENABLED(BLK) dev_num = (boot_device == BOOT_DEVICE_MMC1) ? 0 : 1; ret = blk_find_device(UCLASS_MMC, dev_num, &dev); if (ret) { debug("%s: could not find blk device for MMC device %d: %d\n", __func__, dev_num, ret); return ret; } dev = dev_get_parent(dev); return ofnode_get_path(dev_ofnode(dev), buf, buflen); #else return -ENODEV; #endif } void spl_perform_arch_fixups(struct spl_image_info *spl_image) { const char *bootrom_ofpath = board_spl_was_booted_from(); void *blob = spl_image_fdt_addr(spl_image); char boot_ofpath[512]; int chosen, ret; if (!blob) return; chosen = fdt_find_or_add_subnode(blob, 0, "chosen"); if (chosen < 0) { pr_err("%s: could not find/create '/chosen'\n", __func__); return; } /* * Inject the ofpath of the device the full U-Boot (or Linux in * Falcon-mode) was booted from into the FDT. */ ret = spl_decode_boot_device(spl_image->boot_device, boot_ofpath, sizeof(boot_ofpath)); if (ret) pr_err("%s: could not map boot_device to ofpath: %d\n", __func__, ret); else fdt_setprop_string(blob, chosen, "u-boot,spl-boot-device", boot_ofpath); /* * Inject the ofpath of the device the BootROM loaded the very first * stage from into the FDT. */ if (!bootrom_ofpath) pr_err("%s: could not map BootROM boot device to ofpath\n", __func__); else fdt_setprop_string(blob, chosen, "bootsource", bootrom_ofpath); } #endif |