aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-rockchip/spl-boot-order.c
blob: 94673f34c9b8e72fe79abd67e7ca71c3628dedf3 (plain)
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
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
 */

#include <common.h>
#include <dm.h>
#include <log.h>
#include <mmc.h>
#include <spl.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 succes.
 */

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_get_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_platdata(dev);
				break;
			}
		}

		if (!desc)
			return -ENOENT;

		switch (desc->devnum) {
		case 0:
			return BOOT_DEVICE_MMC1;
		case 1:
			return BOOT_DEVICE_MMC2;
		default:
			return -ENOSYS;
		}
	} else if (!uclass_get_device_by_of_offset(UCLASS_SPI_FLASH, node,
		&parent)) {
		return BOOT_DEVICE_SPI;
	}

	/*
	 * 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_get_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)
{
	/* In case of no fdt (or only platdata), use spl_boot_device() */
	if (!CONFIG_IS_ENABLED(OF_CONTROL) || CONFIG_IS_ENABLED(OF_PLATDATA)) {
		spl_boot_list[0] = spl_boot_device();
		return;
	}

	const void *blob = gd->fdt_blob;
	int chosen_node = fdt_path_offset(blob, "/chosen");
	int idx = 0;
	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[0] = 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 @%x to a boot-device\n",
			      __func__, node);
			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();
}
#endif