rpi.c 9.79 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
 * (C) Copyright 2012-2016 Stephen Warren
4
5
6
 */

#include <common.h>
7
#include <config.h>
8
#include <dm.h>
Simon Glass's avatar
Simon Glass committed
9
#include <env.h>
10
#include <efi_loader.h>
11
#include <fdt_support.h>
12
#include <fdt_simplefb.h>
13
#include <init.h>
14
#include <lcd.h>
15
#include <memalign.h>
16
#include <mmc.h>
17
#include <asm/gpio.h>
18
#include <asm/arch/mbox.h>
19
#include <asm/arch/msg.h>
20
#include <asm/arch/sdhci.h>
21
#include <asm/global_data.h>
22
#include <dm/platform_data/serial_bcm283x_mu.h>
23
24
25
#ifdef CONFIG_ARM64
#include <asm/armv8/mmu.h>
#endif
26
#include <watchdog.h>
Alexander Graf's avatar
Alexander Graf committed
27
#include <dm/pinctrl.h>
28
29
30

DECLARE_GLOBAL_DATA_PTR;

31
32
33
34
35
/* Assigned in lowlevel_init.S
 * Push the variable into the .data section so that it
 * does not get cleared later.
 */
unsigned long __section(".data") fw_dtb_pointer;
36

37
/* TODO(sjg@chromium.org): Move these to the msg.c file */
38
39
40
41
42
43
struct msg_get_arm_mem {
	struct bcm2835_mbox_hdr hdr;
	struct bcm2835_mbox_tag_get_arm_mem get_arm_mem;
	u32 end_tag;
};

44
45
46
47
48
49
struct msg_get_board_rev {
	struct bcm2835_mbox_hdr hdr;
	struct bcm2835_mbox_tag_get_board_rev get_board_rev;
	u32 end_tag;
};

50
51
52
53
54
55
struct msg_get_board_serial {
	struct bcm2835_mbox_hdr hdr;
	struct bcm2835_mbox_tag_get_board_serial get_board_serial;
	u32 end_tag;
};

56
57
58
59
60
61
struct msg_get_mac_address {
	struct bcm2835_mbox_hdr hdr;
	struct bcm2835_mbox_tag_get_mac_address get_mac_address;
	u32 end_tag;
};

62
63
64
65
66
67
struct msg_get_clock_rate {
	struct bcm2835_mbox_hdr hdr;
	struct bcm2835_mbox_tag_get_clock_rate get_clock_rate;
	u32 end_tag;
};

68
69
70
71
72
73
#ifdef CONFIG_ARM64
#define DTB_DIR "broadcom/"
#else
#define DTB_DIR ""
#endif

74
/*
75
 * https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
76
 */
77
struct rpi_model {
78
79
	const char *name;
	const char *fdtfile;
80
	bool has_onboard_eth;
81
82
83
84
};

static const struct rpi_model rpi_model_unknown = {
	"Unknown model",
85
	DTB_DIR "bcm283x-rpi-other.dtb",
86
87
88
89
	false,
};

static const struct rpi_model rpi_models_new_scheme[] = {
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
	[0x0] = {
		"Model A",
		DTB_DIR "bcm2835-rpi-a.dtb",
		false,
	},
	[0x1] = {
		"Model B",
		DTB_DIR "bcm2835-rpi-b.dtb",
		true,
	},
	[0x2] = {
		"Model A+",
		DTB_DIR "bcm2835-rpi-a-plus.dtb",
		false,
	},
	[0x3] = {
		"Model B+",
		DTB_DIR "bcm2835-rpi-b-plus.dtb",
		true,
	},
110
	[0x4] = {
111
		"2 Model B",
112
		DTB_DIR "bcm2836-rpi-2-b.dtb",
113
114
		true,
	},
115
116
117
118
119
	[0x6] = {
		"Compute Module",
		DTB_DIR "bcm2835-rpi-cm.dtb",
		false,
	},
120
121
	[0x8] = {
		"3 Model B",
122
		DTB_DIR "bcm2837-rpi-3-b.dtb",
123
124
		true,
	},
125
126
	[0x9] = {
		"Zero",
127
		DTB_DIR "bcm2835-rpi-zero.dtb",
128
129
		false,
	},
130
131
132
133
134
	[0xA] = {
		"Compute Module 3",
		DTB_DIR "bcm2837-rpi-cm3.dtb",
		false,
	},
135
136
137
138
139
	[0xC] = {
		"Zero W",
		DTB_DIR "bcm2835-rpi-zero-w.dtb",
		false,
	},
140
141
142
143
144
	[0xD] = {
		"3 Model B+",
		DTB_DIR "bcm2837-rpi-3-b-plus.dtb",
		true,
	},
Jonathan Gray's avatar
Jonathan Gray committed
145
146
147
148
149
	[0xE] = {
		"3 Model A+",
		DTB_DIR "bcm2837-rpi-3-a-plus.dtb",
		false,
	},
Jonathan Gray's avatar
Jonathan Gray committed
150
151
152
153
154
	[0x10] = {
		"Compute Module 3+",
		DTB_DIR "bcm2837-rpi-cm3.dtb",
		false,
	},
155
156
157
158
159
	[0x11] = {
		"4 Model B",
		DTB_DIR "bcm2711-rpi-4-b.dtb",
		true,
	},
160
161
162
};

static const struct rpi_model rpi_models_old_scheme[] = {
163
	[0x2] = {
Lubomir Rintel's avatar
Lubomir Rintel committed
164
		"Model B",
165
		DTB_DIR "bcm2835-rpi-b.dtb",
166
		true,
167
	},
168
	[0x3] = {
Lubomir Rintel's avatar
Lubomir Rintel committed
169
		"Model B",
170
		DTB_DIR "bcm2835-rpi-b.dtb",
171
		true,
172
	},
173
	[0x4] = {
Lubomir Rintel's avatar
Lubomir Rintel committed
174
		"Model B rev2",
175
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
176
		true,
177
	},
178
	[0x5] = {
Lubomir Rintel's avatar
Lubomir Rintel committed
179
		"Model B rev2",
180
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
181
		true,
182
	},
183
	[0x6] = {
Lubomir Rintel's avatar
Lubomir Rintel committed
184
		"Model B rev2",
185
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
186
		true,
187
	},
188
	[0x7] = {
189
		"Model A",
190
		DTB_DIR "bcm2835-rpi-a.dtb",
191
		false,
192
	},
193
	[0x8] = {
194
		"Model A",
195
		DTB_DIR "bcm2835-rpi-a.dtb",
196
		false,
197
	},
198
	[0x9] = {
199
		"Model A",
200
		DTB_DIR "bcm2835-rpi-a.dtb",
201
		false,
202
	},
203
	[0xd] = {
204
		"Model B rev2",
205
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
206
		true,
207
	},
208
	[0xe] = {
209
		"Model B rev2",
210
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
211
		true,
212
	},
213
	[0xf] = {
214
		"Model B rev2",
215
		DTB_DIR "bcm2835-rpi-b-rev2.dtb",
216
		true,
217
	},
218
	[0x10] = {
219
		"Model B+",
220
		DTB_DIR "bcm2835-rpi-b-plus.dtb",
221
		true,
222
	},
223
	[0x11] = {
224
		"Compute Module",
225
		DTB_DIR "bcm2835-rpi-cm.dtb",
226
		false,
227
	},
228
	[0x12] = {
Stephen Warren's avatar
Stephen Warren committed
229
		"Model A+",
230
		DTB_DIR "bcm2835-rpi-a-plus.dtb",
Stephen Warren's avatar
Stephen Warren committed
231
232
		false,
	},
233
	[0x13] = {
234
		"Model B+",
235
		DTB_DIR "bcm2835-rpi-b-plus.dtb",
236
237
		true,
	},
238
	[0x14] = {
239
		"Compute Module",
240
		DTB_DIR "bcm2835-rpi-cm.dtb",
241
242
		false,
	},
243
	[0x15] = {
244
		"Model A+",
245
		DTB_DIR "bcm2835-rpi-a-plus.dtb",
246
247
		false,
	},
248
249
};

250
251
252
253
static uint32_t revision;
static uint32_t rev_scheme;
static uint32_t rev_type;
static const struct rpi_model *model;
254

255
256
int dram_init(void)
{
257
	ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_arm_mem, msg, 1);
258
259
260
261
262
263
264
265
266
267
268
269
	int ret;

	BCM2835_MBOX_INIT_HDR(msg);
	BCM2835_MBOX_INIT_TAG(&msg->get_arm_mem, GET_ARM_MEMORY);

	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
	if (ret) {
		printf("bcm2835: Could not query ARM memory size\n");
		return -1;
	}

	gd->ram_size = msg->get_arm_mem.body.resp.mem_size;
270
271
272
273

	return 0;
}

274
275
276
277
#ifdef CONFIG_OF_BOARD
#ifdef CONFIG_BCM2711
int dram_init_banksize(void)
{
278
279
280
281
282
283
284
	int ret;

	ret = fdtdec_setup_memory_banksize();
	if (ret)
		return ret;

	return fdtdec_setup_mem_size_base();
285
286
287
288
}
#endif
#endif

289
290
291
292
static void set_fdtfile(void)
{
	const char *fdtfile;

293
	if (env_get("fdtfile"))
294
295
		return;

296
	fdtfile = model->fdtfile;
297
	env_set("fdtfile", fdtfile);
298
299
}

300
301
302
303
304
305
/*
 * If the firmware provided a valid FDT at boot time, let's expose it in
 * ${fdt_addr} so it may be passed unmodified to the kernel.
 */
static void set_fdt_addr(void)
{
306
	if (env_get("fdt_addr"))
307
308
309
310
311
		return;

	if (fdt_magic(fw_dtb_pointer) != FDT_MAGIC)
		return;

312
	env_set_hex("fdt_addr", fw_dtb_pointer);
313
314
315
316
317
318
319
320
321
322
323
324
}

/*
 * Prevent relocation from stomping on a firmware provided FDT blob.
 */
unsigned long board_get_usable_ram_top(unsigned long total_size)
{
	if ((gd->ram_top - fw_dtb_pointer) > SZ_64M)
		return gd->ram_top;
	return fw_dtb_pointer & ~0xffff;
}

325
static void set_usbethaddr(void)
326
{
327
	ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_mac_address, msg, 1);
328
329
	int ret;

330
	if (!model->has_onboard_eth)
331
332
		return;

333
	if (env_get("usbethaddr"))
334
		return;
335
336
337
338
339
340
341
342

	BCM2835_MBOX_INIT_HDR(msg);
	BCM2835_MBOX_INIT_TAG(&msg->get_mac_address, GET_MAC_ADDRESS);

	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
	if (ret) {
		printf("bcm2835: Could not query MAC address\n");
		/* Ignore error; not critical */
343
		return;
344
345
	}

346
	eth_env_set_enetaddr("usbethaddr", msg->get_mac_address.body.resp.mac);
347

348
349
	if (!env_get("ethaddr"))
		env_set("ethaddr", env_get("usbethaddr"));
Lubomir Rintel's avatar
Lubomir Rintel committed
350

351
352
353
	return;
}

354
355
356
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
static void set_board_info(void)
{
357
358
359
	char s[11];

	snprintf(s, sizeof(s), "0x%X", revision);
360
	env_set("board_revision", s);
361
	snprintf(s, sizeof(s), "%d", rev_scheme);
362
	env_set("board_rev_scheme", s);
363
364
	/* Can't rename this to board_rev_type since it's an ABI for scripts */
	snprintf(s, sizeof(s), "0x%X", rev_type);
365
366
	env_set("board_rev", s);
	env_set("board_name", model->name);
367
368
369
}
#endif /* CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG */

370
371
372
373
374
375
static void set_serial_number(void)
{
	ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_board_serial, msg, 1);
	int ret;
	char serial_string[17] = { 0 };

376
	if (env_get("serial#"))
377
378
379
380
381
382
383
384
385
386
387
388
		return;

	BCM2835_MBOX_INIT_HDR(msg);
	BCM2835_MBOX_INIT_TAG_NO_REQ(&msg->get_board_serial, GET_BOARD_SERIAL);

	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
	if (ret) {
		printf("bcm2835: Could not query board serial\n");
		/* Ignore error; not critical */
		return;
	}

389
	snprintf(serial_string, sizeof(serial_string), "%016llx",
390
		 msg->get_board_serial.body.resp.serial);
391
	env_set("serial#", serial_string);
392
393
}

394
395
int misc_init_r(void)
{
396
	set_fdt_addr();
397
398
	set_fdtfile();
	set_usbethaddr();
399
400
401
#ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG
	set_board_info();
#endif
402
403
	set_serial_number();

404
405
406
	return 0;
}

407
408
static void get_board_rev(void)
{
409
	ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_board_rev, msg, 1);
410
	int ret;
411
412
	const struct rpi_model *models;
	uint32_t models_count;
413
414
415
416
417
418
419
420
421
422
423

	BCM2835_MBOX_INIT_HDR(msg);
	BCM2835_MBOX_INIT_TAG(&msg->get_board_rev, GET_BOARD_REV);

	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr);
	if (ret) {
		printf("bcm2835: Could not query board revision\n");
		/* Ignore error; not critical */
		return;
	}

424
425
426
427
428
	/*
	 * For details of old-vs-new scheme, see:
	 * https://github.com/pimoroni/RPi.version/blob/master/RPi/version.py
	 * http://www.raspberrypi.org/forums/viewtopic.php?f=63&t=99293&p=690282
	 * (a few posts down)
429
430
431
432
433
	 *
	 * For the RPi 1, bit 24 is the "warranty bit", so we mask off just the
	 * lower byte to use as the board rev:
	 * http://www.raspberrypi.org/forums/viewtopic.php?f=63&t=98367&start=250
	 * http://www.raspberrypi.org/forums/viewtopic.php?f=31&t=20594
434
	 */
435
436
437
438
439
440
441
442
443
444
445
	revision = msg->get_board_rev.body.resp.rev;
	if (revision & 0x800000) {
		rev_scheme = 1;
		rev_type = (revision >> 4) & 0xff;
		models = rpi_models_new_scheme;
		models_count = ARRAY_SIZE(rpi_models_new_scheme);
	} else {
		rev_scheme = 0;
		rev_type = revision & 0xff;
		models = rpi_models_old_scheme;
		models_count = ARRAY_SIZE(rpi_models_old_scheme);
Stephen Warren's avatar
Stephen Warren committed
446
	}
447
448
449
450
451
452
453
454
	if (rev_type >= models_count) {
		printf("RPI: Board rev 0x%x outside known range\n", rev_type);
		model = &rpi_model_unknown;
	} else if (!models[rev_type].name) {
		printf("RPI: Board rev 0x%x unknown\n", rev_type);
		model = &rpi_model_unknown;
	} else {
		model = &models[rev_type];
Stephen Warren's avatar
Stephen Warren committed
455
	}
456

457
	printf("RPI %s (0x%x)\n", model->name, revision);
458
459
}

460
int board_init(void)
461
{
462
463
464
#ifdef CONFIG_HW_WATCHDOG
	hw_watchdog_init();
#endif
465

466
467
468
469
	get_board_rev();

	gd->bd->bi_boot_params = 0x100;

470
	return bcm2835_power_on_module(BCM2835_MBOX_POWER_DEVID_USB_HCD);
471
472
}

473
474
475
476
477
478
479
480
481
482
/*
 * If the firmware passed a device tree use it for U-Boot.
 */
void *board_fdt_blob_setup(void)
{
	if (fdt_magic(fw_dtb_pointer) != FDT_MAGIC)
		return NULL;
	return (void *)fw_dtb_pointer;
}

483
int ft_board_setup(void *blob, bd_t *bd)
484
485
486
487
488
489
490
{
	/*
	 * For now, we simply always add the simplefb DT node. Later, we
	 * should be more intelligent, and e.g. only do this if no enabled DT
	 * node exists for the "real" graphics driver.
	 */
	lcd_dt_simplefb_add_node(blob);
491

492
493
494
495
496
#ifdef CONFIG_EFI_LOADER
	/* Reserve the spin table */
	efi_add_memory_map(0, 1, EFI_RESERVED_MEMORY_TYPE, 0);
#endif

497
	return 0;
498
}