summaryrefslogtreecommitdiffstats
path: root/common/cmd_bootldr.c
blob: 535b931ff342ccf2385ef6ee90f2de06b451752b (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
/*
 * U-boot - bootldr.c
 *
 * Copyright (c) 2005-2008 Analog Devices Inc.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * Licensed under the GPL-2 or later.
 */

#include <config.h>
#include <common.h>
#include <command.h>

#include <asm/blackfin.h>
#include <asm/mach-common/bits/bootrom.h>

/* Simple sanity check on the specified address to make sure it contains
 * an LDR image of some sort.
 */
static bool ldr_valid_signature(uint8_t *data)
{
#if defined(__ADSPBF561__)

	/* BF56x has a 4 byte global header */
	if (data[3] == 0xA0)
		return true;

#elif defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \
      defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) || \
      defined(__ADSPBF538__) || defined(__ADSPBF539__)

	/* all the BF53x should start at this address mask */
	uint32_t addr;
	memmove(&addr, data, sizeof(addr));
	if ((addr & 0xFF0FFF0F) == 0xFF000000)
		return true;
#else

	/* everything newer has a magic byte */
	uint32_t count;
	memmove(&count, data + 8, sizeof(count));
	if (data[3] == 0xAD && count == 0)
		return true;

#endif

	return false;
}

/* If the Blackfin is new enough, the Blackfin on-chip ROM supports loading
 * LDRs from random memory addresses.  So whenever possible, use that.  In
 * the older cases (BF53x/BF561), parse the LDR format ourselves.
 */
#define ZEROFILL  0x0001
#define RESVECT   0x0002
#define INIT      0x0008
#define IGNORE    0x0010
#define FINAL     0x8000
static void ldr_load(uint8_t *base_addr)
{
#if defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \
  /*defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__) ||*/\
    defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__)

	uint32_t addr;
	uint32_t count;
	uint16_t flags;

	/* the bf56x has a 4 byte global header ... but it is useless to
	 * us when booting an LDR from a memory address, so skip it
	 */
# ifdef __ADSPBF561__
	base_addr += 4;
# endif

	memmove(&flags, base_addr + 8, sizeof(flags));
	bfin_write_EVT1(flags & RESVECT ? 0xFFA00000 : 0xFFA08000);

	do {
		/* block header may not be aligned */
		memmove(&addr, base_addr, sizeof(addr));
		memmove(&count, base_addr+4, sizeof(count));
		memmove(&flags, base_addr+8, sizeof(flags));
		base_addr += sizeof(addr) + sizeof(count) + sizeof(flags);

		printf("loading to 0x%08x (0x%x bytes) flags: 0x%04x\n",
			addr, count, flags);

		if (!(flags & IGNORE)) {
			if (flags & ZEROFILL)
				memset((void *)addr, 0x00, count);
			else
				memcpy((void *)addr, base_addr, count);

			if (flags & INIT) {
				void (*init)(void) = (void *)addr;
				init();
			}
		}

		if (!(flags & ZEROFILL))
			base_addr += count;
	} while (!(flags & FINAL));

#endif
}

/* For BF537, we use the _BOOTROM_BOOT_DXE_FLASH funky ROM function.
 * For all other BF53x/BF56x, we just call the entry point.
 * For everything else (newer), we use _BOOTROM_MEMBOOT ROM function.
 */
static void ldr_exec(void *addr)
{
#if defined(__ADSPBF534__) || defined(__ADSPBF536__) || defined(__ADSPBF537__)

	/* restore EVT1 to reset value as this is what the bootrom uses as
	 * the default entry point when booting the final block of LDRs
	 */
	bfin_write_EVT1(L1_INST_SRAM);
	__asm__("call (%0);" : : "a"(_BOOTROM_MEMBOOT), "q7"(addr) : "RETS", "memory");

#elif defined(__ADSPBF531__) || defined(__ADSPBF532__) || defined(__ADSPBF533__) || \
      defined(__ADSPBF538__) || defined(__ADSPBF539__) || defined(__ADSPBF561__)

	void (*ldr_entry)(void) = (void *)bfin_read_EVT1();
	ldr_entry();

#else

	int32_t (*BOOTROM_MEM)(void *, int32_t, int32_t, void *) = (void *)_BOOTROM_MEMBOOT;
	BOOTROM_MEM(addr, 0, 0, NULL);

#endif
}

/*
 * the bootldr command loads an address, checks to see if there
 *   is a Boot stream that the on-chip BOOTROM can understand,
 *   and loads it via the BOOTROM Callback. It is possible
 *   to also add booting from SPI, or TWI, but this function does
 *   not currently support that.
 */
int do_bootldr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	void *addr;

	/* Get the address */
	if (argc < 2)
		addr = (void *)load_addr;
	else
		addr = (void *)simple_strtoul(argv[1], NULL, 16);

	/* Check if it is a LDR file */
	if (ldr_valid_signature(addr)) {
		printf("## Booting ldr image at 0x%p ...\n", addr);
		ldr_load(addr);

		icache_disable();
		dcache_disable();

		ldr_exec(addr);
	} else
		printf("## No ldr image at address 0x%p\n", addr);

	return 0;
}

U_BOOT_CMD(
	bootldr, 2, 0, do_bootldr,
	"boot ldr image from memory",
	"[addr]\n"
	""
);