19b0bc593SPhilipp Tomsich /*
29b0bc593SPhilipp Tomsich * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
39b0bc593SPhilipp Tomsich *
49b0bc593SPhilipp Tomsich * SPDX-License-Identifier: GPL-2.0+
59b0bc593SPhilipp Tomsich */
69b0bc593SPhilipp Tomsich
79b0bc593SPhilipp Tomsich #include <common.h>
89b0bc593SPhilipp Tomsich #include <dm.h>
99b0bc593SPhilipp Tomsich #include <mmc.h>
10332967aeSYifeng Zhao #include <nand.h>
119b0bc593SPhilipp Tomsich #include <spl.h>
129b0bc593SPhilipp Tomsich
13c4bf7455SJoseph Chen #ifdef CONFIG_SPL_RAM_DEVICE
board_boot_order(u32 * spl_boot_list)14c4bf7455SJoseph Chen void board_boot_order(u32 *spl_boot_list)
15c4bf7455SJoseph Chen {
16c4bf7455SJoseph Chen spl_boot_list[0] = BOOT_DEVICE_RAM;
17c4bf7455SJoseph Chen }
18c4bf7455SJoseph Chen #else
19c4bf7455SJoseph Chen
2009a3210fSKever Yang #if CONFIG_IS_ENABLED(OF_CONTROL) && ! CONFIG_IS_ENABLED(OF_PLATDATA)
21309353d5SPhilipp Tomsich /**
22309353d5SPhilipp Tomsich * spl_node_to_boot_device() - maps from a DT-node to a SPL boot device
23309353d5SPhilipp Tomsich * @node: of_offset of the node
24309353d5SPhilipp Tomsich *
25309353d5SPhilipp Tomsich * The SPL framework uses BOOT_DEVICE_... constants to identify its boot
26309353d5SPhilipp Tomsich * sources. These may take on a device-specific meaning, depending on
27309353d5SPhilipp Tomsich * what nodes are enabled in a DTS (e.g. BOOT_DEVICE_MMC1 may refer to
28309353d5SPhilipp Tomsich * different controllers/block-devices, depending on which SD/MMC controllers
29309353d5SPhilipp Tomsich * are enabled in any given DTS). This function maps from a DT-node back
30309353d5SPhilipp Tomsich * onto a BOOT_DEVICE_... constant, considering the currently active devices.
31309353d5SPhilipp Tomsich *
32309353d5SPhilipp Tomsich * Returns
33309353d5SPhilipp Tomsich * -ENOENT, if no device matching the node could be found
34309353d5SPhilipp Tomsich * -ENOSYS, if the device matching the node can not be mapped onto a
35309353d5SPhilipp Tomsich * SPL boot device (e.g. the third MMC device)
36309353d5SPhilipp Tomsich * -1, for unspecified failures
37309353d5SPhilipp Tomsich * a positive integer (from the BOOT_DEVICE_... family) on succes.
38309353d5SPhilipp Tomsich */
spl_node_to_boot_device(int node)399b0bc593SPhilipp Tomsich static int spl_node_to_boot_device(int node)
409b0bc593SPhilipp Tomsich {
419b0bc593SPhilipp Tomsich struct udevice *parent;
429b0bc593SPhilipp Tomsich
43*f749a034SYifeng Zhao if (!uclass_get_device_by_of_offset(UCLASS_UFS, node, &parent))
44*f749a034SYifeng Zhao return BOOT_DEVICE_UFS;
45*f749a034SYifeng Zhao
469b0bc593SPhilipp Tomsich /*
479b0bc593SPhilipp Tomsich * This should eventually move into the SPL code, once SPL becomes
489b0bc593SPhilipp Tomsich * aware of the block-device layer. Until then (and to avoid unneeded
499b0bc593SPhilipp Tomsich * delays in getting this feature out, it lives at the board-level).
509b0bc593SPhilipp Tomsich */
519b0bc593SPhilipp Tomsich if (!uclass_get_device_by_of_offset(UCLASS_MMC, node, &parent)) {
529b0bc593SPhilipp Tomsich struct udevice *dev;
539b0bc593SPhilipp Tomsich struct blk_desc *desc = NULL;
549b0bc593SPhilipp Tomsich
559b0bc593SPhilipp Tomsich for (device_find_first_child(parent, &dev);
569b0bc593SPhilipp Tomsich dev;
579b0bc593SPhilipp Tomsich device_find_next_child(&dev)) {
589b0bc593SPhilipp Tomsich if (device_get_uclass_id(dev) == UCLASS_BLK) {
599b0bc593SPhilipp Tomsich desc = dev_get_uclass_platdata(dev);
609b0bc593SPhilipp Tomsich break;
619b0bc593SPhilipp Tomsich }
629b0bc593SPhilipp Tomsich }
639b0bc593SPhilipp Tomsich
649b0bc593SPhilipp Tomsich if (!desc)
659b0bc593SPhilipp Tomsich return -ENOENT;
669b0bc593SPhilipp Tomsich
679b0bc593SPhilipp Tomsich switch (desc->devnum) {
689b0bc593SPhilipp Tomsich case 0:
699b0bc593SPhilipp Tomsich return BOOT_DEVICE_MMC1;
709b0bc593SPhilipp Tomsich case 1:
719b0bc593SPhilipp Tomsich return BOOT_DEVICE_MMC2;
729b0bc593SPhilipp Tomsich default:
739b0bc593SPhilipp Tomsich return -ENOSYS;
749b0bc593SPhilipp Tomsich }
759b0bc593SPhilipp Tomsich }
769b0bc593SPhilipp Tomsich
779b0bc593SPhilipp Tomsich /*
789b0bc593SPhilipp Tomsich * SPL doesn't differentiate SPI flashes, so we keep the detection
799b0bc593SPhilipp Tomsich * brief and inaccurate... hopefully, the common SPL layer can be
809b0bc593SPhilipp Tomsich * extended with awareness of the BLK layer (and matching OF_CONTROL)
819b0bc593SPhilipp Tomsich * soon.
829b0bc593SPhilipp Tomsich */
839b0bc593SPhilipp Tomsich if (!uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node, &parent))
840afd346fSJon Lin #ifndef CONFIG_SPL_MTD_SUPPORT
850afd346fSJon Lin return BOOT_DEVICE_SPI;
860afd346fSJon Lin #else
87c9b995fdSJon Lin return BOOT_DEVICE_MTD_BLK_SPI_NOR;
889b0bc593SPhilipp Tomsich
8914be0258SJason Zhu if (!uclass_get_device_by_of_offset(UCLASS_MTD, node, &parent)) {
9014be0258SJason Zhu struct udevice *dev;
9114be0258SJason Zhu struct blk_desc *desc = NULL;
9214be0258SJason Zhu
9314be0258SJason Zhu for (device_find_first_child(parent, &dev);
9414be0258SJason Zhu dev;
9514be0258SJason Zhu device_find_next_child(&dev)) {
9614be0258SJason Zhu if (device_get_uclass_id(dev) == UCLASS_BLK) {
9714be0258SJason Zhu desc = dev_get_uclass_platdata(dev);
9814be0258SJason Zhu break;
9914be0258SJason Zhu }
10014be0258SJason Zhu }
10114be0258SJason Zhu
10214be0258SJason Zhu if (!desc)
10314be0258SJason Zhu return -ENOENT;
10414be0258SJason Zhu
10514be0258SJason Zhu switch (desc->devnum) {
10614be0258SJason Zhu case 0:
10714be0258SJason Zhu return BOOT_DEVICE_MTD_BLK_NAND;
10814be0258SJason Zhu case 1:
10914be0258SJason Zhu return BOOT_DEVICE_MTD_BLK_SPI_NAND;
11014be0258SJason Zhu default:
11114be0258SJason Zhu return -ENOSYS;
11214be0258SJason Zhu }
11314be0258SJason Zhu }
1144bac908bSJason Zhu #endif
1154bac908bSJason Zhu
116568252a0SYifeng Zhao /*
117568252a0SYifeng Zhao * This should eventually move into the SPL code, once SPL becomes
118568252a0SYifeng Zhao * aware of the block-device layer. Until then (and to avoid unneeded
119568252a0SYifeng Zhao * delays in getting this feature out, it lives at the board-level).
120568252a0SYifeng Zhao */
121568252a0SYifeng Zhao if (!uclass_get_device_by_of_offset(UCLASS_RKNAND, node, &parent))
122568252a0SYifeng Zhao return BOOT_DEVICE_RKNAND;
123568252a0SYifeng Zhao
1249b0bc593SPhilipp Tomsich return -1;
1259b0bc593SPhilipp Tomsich }
1269b0bc593SPhilipp Tomsich
1272d5ea11eSPhilipp Tomsich /**
1282d5ea11eSPhilipp Tomsich * board_spl_was_booted_from() - retrieves the of-path the SPL was loaded from
1292d5ea11eSPhilipp Tomsich *
1302d5ea11eSPhilipp Tomsich * To support a 'same-as-spl' specification in the search-order for the next
1312d5ea11eSPhilipp Tomsich * stage, we need a SoC- or board-specific way to handshake with what 'came
1322d5ea11eSPhilipp Tomsich * before us' (either a BROM or TPL stage) and map the info retrieved onto
1332d5ea11eSPhilipp Tomsich * a OF path.
1342d5ea11eSPhilipp Tomsich *
1352d5ea11eSPhilipp Tomsich * Returns
1362d5ea11eSPhilipp Tomsich * NULL, on failure or if the device could not be identified
1372d5ea11eSPhilipp Tomsich * a of_path (a string), on success
1382d5ea11eSPhilipp Tomsich */
board_spl_was_booted_from(void)1392d5ea11eSPhilipp Tomsich __weak const char *board_spl_was_booted_from(void)
1402d5ea11eSPhilipp Tomsich {
1412d5ea11eSPhilipp Tomsich debug("%s: no support for 'same-as-spl' for this board\n", __func__);
1422d5ea11eSPhilipp Tomsich return NULL;
1432d5ea11eSPhilipp Tomsich }
1442d5ea11eSPhilipp Tomsich
board_boot_order(u32 * spl_boot_list)1459b0bc593SPhilipp Tomsich void board_boot_order(u32 *spl_boot_list)
1469b0bc593SPhilipp Tomsich {
1479b0bc593SPhilipp Tomsich const void *blob = gd->fdt_blob;
1489b0bc593SPhilipp Tomsich int chosen_node = fdt_path_offset(blob, "/chosen");
1499b0bc593SPhilipp Tomsich int idx = 0;
1509b0bc593SPhilipp Tomsich int elem;
1519b0bc593SPhilipp Tomsich int boot_device;
1529b0bc593SPhilipp Tomsich int node;
1539b0bc593SPhilipp Tomsich const char *conf;
1549b0bc593SPhilipp Tomsich
1559b0bc593SPhilipp Tomsich if (chosen_node < 0) {
1569b0bc593SPhilipp Tomsich debug("%s: /chosen not found, using spl_boot_device()\n",
1579b0bc593SPhilipp Tomsich __func__);
1589b0bc593SPhilipp Tomsich spl_boot_list[0] = spl_boot_device();
1599b0bc593SPhilipp Tomsich return;
1609b0bc593SPhilipp Tomsich }
1619b0bc593SPhilipp Tomsich
1629b0bc593SPhilipp Tomsich for (elem = 0;
1639b0bc593SPhilipp Tomsich (conf = fdt_stringlist_get(blob, chosen_node,
1649b0bc593SPhilipp Tomsich "u-boot,spl-boot-order", elem, NULL));
1659b0bc593SPhilipp Tomsich elem++) {
1662d5ea11eSPhilipp Tomsich const char *alias;
1672d5ea11eSPhilipp Tomsich
1682d5ea11eSPhilipp Tomsich /* Handle the case of 'same device the SPL was loaded from' */
1692d5ea11eSPhilipp Tomsich if (strncmp(conf, "same-as-spl", 11) == 0) {
1702d5ea11eSPhilipp Tomsich conf = board_spl_was_booted_from();
1712d5ea11eSPhilipp Tomsich if (!conf)
1722d5ea11eSPhilipp Tomsich continue;
1732d5ea11eSPhilipp Tomsich }
1742d5ea11eSPhilipp Tomsich
1759b0bc593SPhilipp Tomsich /* First check if the list element is an alias */
1762d5ea11eSPhilipp Tomsich alias = fdt_get_alias(blob, conf);
1779b0bc593SPhilipp Tomsich if (alias)
1789b0bc593SPhilipp Tomsich conf = alias;
1799b0bc593SPhilipp Tomsich
1809b0bc593SPhilipp Tomsich /* Try to resolve the config item (or alias) as a path */
1819b0bc593SPhilipp Tomsich node = fdt_path_offset(blob, conf);
1829b0bc593SPhilipp Tomsich if (node < 0) {
1839b0bc593SPhilipp Tomsich debug("%s: could not find %s in FDT", __func__, conf);
1849b0bc593SPhilipp Tomsich continue;
1859b0bc593SPhilipp Tomsich }
1869b0bc593SPhilipp Tomsich
1879b0bc593SPhilipp Tomsich /* Try to map this back onto SPL boot devices */
1889b0bc593SPhilipp Tomsich boot_device = spl_node_to_boot_device(node);
1899b0bc593SPhilipp Tomsich if (boot_device < 0) {
1909b0bc593SPhilipp Tomsich debug("%s: could not map node @%x to a boot-device\n",
1919b0bc593SPhilipp Tomsich __func__, node);
1929b0bc593SPhilipp Tomsich continue;
1939b0bc593SPhilipp Tomsich }
1949b0bc593SPhilipp Tomsich
1959b0bc593SPhilipp Tomsich spl_boot_list[idx++] = boot_device;
1969b0bc593SPhilipp Tomsich }
1979b0bc593SPhilipp Tomsich
1989b0bc593SPhilipp Tomsich /* If we had no matches, fall back to spl_boot_device */
1999b0bc593SPhilipp Tomsich if (idx == 0)
2009b0bc593SPhilipp Tomsich spl_boot_list[0] = spl_boot_device();
2019b0bc593SPhilipp Tomsich }
2029b0bc593SPhilipp Tomsich #endif
203c4bf7455SJoseph Chen #endif
204