summaryrefslogtreecommitdiffstats
path: root/common/bootsource.c
blob: 6808c9c51d88b185779819bca19b0301d9a5ab29 (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
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
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2011 Marc Reilly <marc@cpdesign.com.au>
 * Copyright (C) 2013 Marc Kleine-Budde <mkl@pengutronix.de>
 */

#include <common.h>
#include <bootsource.h>
#include <environment.h>
#include <magicvar.h>
#include <init.h>

static const char *bootsource_str[BOOTSOURCE_MAX] = {
	[BOOTSOURCE_UNKNOWN] = "unknown",
	[BOOTSOURCE_NAND] = "nand",
	[BOOTSOURCE_NOR] = "nor",
	[BOOTSOURCE_MMC] = "mmc",
	[BOOTSOURCE_I2C] = "i2c",
	[BOOTSOURCE_I2C_EEPROM] = "i2c-eeprom",
	[BOOTSOURCE_SPI] = "spi",
	[BOOTSOURCE_SPI_EEPROM] = "spi-eeprom",
	[BOOTSOURCE_SPI_NOR] = "spi-nor",
	[BOOTSOURCE_SERIAL] = "serial",
	[BOOTSOURCE_ONENAND] = "onenand",
	[BOOTSOURCE_HD] = "harddisk",
	[BOOTSOURCE_USB] = "usb",
	[BOOTSOURCE_NET] = "net",
	[BOOTSOURCE_CAN] = "can",
	[BOOTSOURCE_JTAG] = "jtag",
};

static enum bootsource bootsource = BOOTSOURCE_UNKNOWN;
static int bootsource_instance = BOOTSOURCE_INSTANCE_UNKNOWN;
const char *bootsource_alias_name = NULL;

const char *bootsource_to_string(enum bootsource src)
{
	if (src >= ARRAY_SIZE(bootsource_str))
		return NULL;

	return bootsource_str[src];
}

const char *bootsource_get_alias_stem(enum bootsource src)
{
	switch (src) {
		/*
		 * For I2C and SPI EEPROMs we set the stem to be 'i2c'
		 * and 'spi' correspondingly. The resulting alias will
		 * be pointing at the controller said EEPROM is
		 * attached to.
		 *
		 * NOTE: This code assumes single bootable EEPROM per
		 * controller
		 */
	case BOOTSOURCE_I2C_EEPROM:
		return bootsource_str[BOOTSOURCE_I2C];
	case BOOTSOURCE_SPI_EEPROM:
	case BOOTSOURCE_SPI_NOR:
		return bootsource_str[BOOTSOURCE_SPI];
	case BOOTSOURCE_SERIAL:	/* FALLTHROUGH */
	case BOOTSOURCE_I2C:	/* FALLTHROUGH */
	case BOOTSOURCE_MMC:	/* FALLTHROUGH */
	case BOOTSOURCE_SPI:	/* FALLTHROUGH */
	case BOOTSOURCE_CAN:
		return bootsource_str[src];
	default:
		return NULL;
	}
}

/**
 * bootsource_get_alias_name() - Get the name of the bootsource alias
 *
 * This function will return newly allocated string containing name of
 * the alias that is expected to point to DTB node corresponding to
 * detected bootsource
 *
 * NOTE: Caller is expected to free() the string allocated by this
 * function
 */
char *bootsource_get_alias_name(void)
{
	const char *stem;

	/*
	 * If alias name was overridden via
	 * bootsource_set_alias_name() return that value without
	 * asking any questions.
	 *
	 * Note that we have to strdup() the result to make it
	 * free-able.
	 */
	if (bootsource_alias_name)
		return strdup(bootsource_alias_name);

	stem = bootsource_get_alias_stem(bootsource);
	if (!stem)
		return NULL;

	/*
	 * We expect SoC specific bootsource detection code to properly
	 * initialize bootsource_instance, so we bail out if it didn't
	 */
	if (bootsource_instance == BOOTSOURCE_INSTANCE_UNKNOWN)
		return NULL;

	return basprintf("%s%d", stem, bootsource_instance);
}

struct device_node *bootsource_of_node_get(struct device_node *root)
{
	struct device_node *np;
	char *alias_name;

	alias_name = bootsource_get_alias_name();
	if (!alias_name)
		return NULL;

	np = of_find_node_by_alias(root, alias_name);

	free(alias_name);

	return np;
}

void bootsource_set_alias_name(const char *name)
{
	bootsource_alias_name = name;
}

void bootsource_set_raw(enum bootsource src, int instance)
{
	if (src >= BOOTSOURCE_MAX)
		src = BOOTSOURCE_UNKNOWN;

	bootsource = src;

	setenv("bootsource", bootsource_to_string(src));

	bootsource_set_raw_instance(instance);
}

void bootsource_set_raw_instance(int instance)
{
	bootsource_instance = instance;

	if (instance < 0)
		setenv("bootsource_instance","unknown");
	else
		pr_setenv("bootsource_instance", "%d", instance);
}

int bootsource_of_alias_xlate(enum bootsource src, int instance)
{
	char chosen[sizeof("barebox,bootsource-harddisk4294967295")];
	const char *bootsource_stem;
	struct device_node *np;
	int alias_id;

	if (!IS_ENABLED(CONFIG_OFDEVICE))
		return BOOTSOURCE_INSTANCE_UNKNOWN;

	if (src == BOOTSOURCE_UNKNOWN ||
	    instance == BOOTSOURCE_INSTANCE_UNKNOWN)
		return BOOTSOURCE_INSTANCE_UNKNOWN;

	bootsource_stem = bootsource_get_alias_stem(src);
	if (!bootsource_stem)
		return BOOTSOURCE_INSTANCE_UNKNOWN;

	scnprintf(chosen, sizeof(chosen), "barebox,bootsource-%s%u",
		  bootsource_stem, instance);

	np = of_find_node_by_chosen(chosen, NULL);
	if (!np)
		return BOOTSOURCE_INSTANCE_UNKNOWN;

	alias_id = of_alias_get_id(np, bootsource_stem);
	if (alias_id < 0)
		return BOOTSOURCE_INSTANCE_UNKNOWN;

	return alias_id;
}

int bootsource_set(enum bootsource src, int instance)
{
	int alias_id;

	alias_id = bootsource_of_alias_xlate(src, instance);
	if (alias_id == BOOTSOURCE_INSTANCE_UNKNOWN)
		alias_id = instance;

	bootsource_set_raw(src, alias_id);

	return alias_id;
}

enum bootsource bootsource_get(void)
{
	return bootsource;
}

BAREBOX_MAGICVAR(bootsource, "The source barebox has been booted from");

int bootsource_get_instance(void)
{
	return bootsource_instance;
}

BAREBOX_MAGICVAR(bootsource_instance, "The instance of the source barebox has been booted from");

static int bootsource_init(void)
{
	bootsource_set_raw(bootsource, bootsource_instance);
	export("bootsource");
	export("bootsource_instance");

	return 0;
}
coredevice_initcall(bootsource_init);