aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/fsl_debug_server.c
blob: 98d9fbe534c3bd9511c4f2dfd4a4be3957b4241c (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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
 * Copyright (C) 2014 Freescale Semiconductor
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/system.h>

#include <fsl-mc/fsl_mc.h>
#include <fsl_debug_server.h>

DECLARE_GLOBAL_DATA_PTR;
static int debug_server_ver_info_maj, debug_server_ver_info_min;

/**
 * Copying Debug Server firmware to DDR
 */
static int debug_server_copy_image(const char *title, u64 image_addr,
				   u32 image_size, u64 debug_server_ram_addr)
{
	debug("%s copied to address %p\n", title,
	      (void *)debug_server_ram_addr);
	memcpy((void *)debug_server_ram_addr, (void *)image_addr, image_size);

	return 0;
}

/**
 * Debug Server FIT image parser checks if the image is in FIT
 * format, verifies integrity of the image and calculates
 * raw image address and size values.
 *
 * Returns 0 if success and -1 if any of the above mentioned
 * task fail.
 **/
int debug_server_parse_firmware_fit_image(const void **raw_image_addr,
					  size_t *raw_image_size)
{
	int format;
	void *fit_hdr;
	int node_offset;
	const void *data;
	size_t size;
	const char *uname = "firmware";
	char *desc;
	char *debug_server_ver_info;
	char *debug_server_ver_info_major, *debug_server_ver_info_minor;

	/* Check if the image is in NOR flash */
#ifdef CONFIG_SYS_DEBUG_SERVER_FW_IN_NOR
	fit_hdr = (void *)CONFIG_SYS_DEBUG_SERVER_FW_ADDR;
#else
#error "CONFIG_SYS_DEBUG_SERVER_FW_IN_NOR not defined"
#endif

	/* Check if Image is in FIT format */
	format = genimg_get_format(fit_hdr);
	if (format != IMAGE_FORMAT_FIT) {
		printf("Debug Server FW: Not a FIT image\n");
		goto out_error;
	}

	if (!fit_check_format(fit_hdr)) {
		printf("Debug Server FW: Bad FIT image format\n");
		goto out_error;
	}

	node_offset = fit_image_get_node(fit_hdr, uname);
	if (node_offset < 0) {
		printf("Debug Server FW:Can not find %s subimage\n", uname);
		goto out_error;
	}

	/* Verify Debug Server firmware image */
	if (!fit_image_verify(fit_hdr, node_offset)) {
		printf("Debug Server FW: Bad Debug Server firmware hash");
		goto out_error;
	}

	if (fit_get_desc(fit_hdr, node_offset, &desc) < 0) {
		printf("Debug Server FW: Failed to get FW description");
		goto out_error;
	}

	debug_server_ver_info = strstr(desc, "Version");
	debug_server_ver_info_major = strtok(debug_server_ver_info, ".");
	debug_server_ver_info_minor = strtok(NULL, ".");

	debug_server_ver_info_maj =
			simple_strtoul(debug_server_ver_info_major, NULL, 10);
	debug_server_ver_info_min =
			simple_strtoul(debug_server_ver_info_minor, NULL, 10);

	/* Debug server version checking */
	if ((debug_server_ver_info_maj < DEBUG_SERVER_VER_MAJOR) ||
	    (debug_server_ver_info_min < DEBUG_SERVER_VER_MINOR)) {
		printf("Debug server FW mismatches the min version required\n");
		printf("Expected:%d.%d, Got %d.%d\n",
		       DEBUG_SERVER_VER_MAJOR, DEBUG_SERVER_VER_MINOR,
		       debug_server_ver_info_maj,
		       debug_server_ver_info_min);
		goto out_error;
	}

	/* Get address and size of raw image */
	fit_image_get_data(fit_hdr, node_offset, &data, &size);

	*raw_image_addr = data;
	*raw_image_size = size;

	return 0;

out_error:
	return -1;
}

/**
 * Return the actual size of the Debug Server private DRAM block.
 *
 * NOTE: For now this function always returns the minimum required size,
 * However, in the future, the actual size may be obtained from an environment
 * variable.
 */
unsigned long debug_server_get_dram_block_size(void)
{
	return CONFIG_SYS_DEBUG_SERVER_DRAM_BLOCK_MIN_SIZE;
}

int debug_server_init(void)
{
	struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
	int error, timeout = CONFIG_SYS_DEBUG_SERVER_TIMEOUT;
	int debug_server_boot_status;
	u64 debug_server_ram_addr, debug_server_ram_size;
	const void *raw_image_addr;
	size_t raw_image_size = 0;

	debug("debug_server_init called\n");
	/*
	 * The Debug Server private DRAM block was already carved at the end of
	 * DRAM by board_init_f() using CONFIG_SYS_MEM_TOP_HIDE:
	 */
	debug_server_ram_size = debug_server_get_dram_block_size();
	if (gd->bd->bi_dram[1].start)
		debug_server_ram_addr =
			gd->bd->bi_dram[1].start + gd->bd->bi_dram[1].size;
	else
		debug_server_ram_addr =
			gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size;

#ifdef CONFIG_FSL_MC_ENET
	debug_server_ram_addr += mc_get_dram_block_size();
#endif

	error = debug_server_parse_firmware_fit_image(&raw_image_addr,
							&raw_image_size);
	if (error != 0)
		goto out;

	debug("debug server (ram addr = 0x%llx, ram size = 0x%llx)\n",
	      debug_server_ram_addr, debug_server_ram_size);
	/*
	 * Load the Debug Server FW at the beginning of the Debug Server
	 * private DRAM block:
	 */
	debug_server_copy_image("Debug Server Firmware",
				(u64)raw_image_addr, raw_image_size,
				debug_server_ram_addr);

	/* flush dcache */
	flush_dcache_range((unsigned long)debug_server_ram_addr,
			   (unsigned long)debug_server_ram_addr +
			   (unsigned long)debug_server_ram_size);

	/*
	 * Tell SP that the Debug Server FW is about to be launched. Before that
	 * populate the following:
	 * 1. Write the size allocated to SP Memory region into Bits {31:16} of
	 *    SCRATCHRW5.
	 * 2. Write the start address of the SP memory regions into
	 *    SCRATCHRW5 (Bits {15:0}, contain most significant bits, Bits
	 *    {47:32} of the SP Memory Region physical start address
	 *    (SoC address)) and SCRATCHRW6 (Bits {31:0}).
	 * 3. To know the Debug Server FW boot status, set bit 0 of SCRATCHRW11
	 *    to 1. The Debug Server sets this to 0 to indicate a
	 *    successul boot.
	 * 4. Wakeup SP by writing 0x1F to VSG GIC reg VIGR2.
	 */

	/* 512 MB */
	out_le32(&gur->scratchrw[5 - 1],
		 (u32)((u64)debug_server_ram_addr >> 32) | (0x000D << 16));
	out_le32(&gur->scratchrw[6 - 1],
		 ((u32)debug_server_ram_addr) & 0xFFFFFFFF);

	out_le32(&gur->scratchrw[11 - 1], DEBUG_SERVER_INIT_STATUS);
	/* Allow the changes to reflect in GUR block */
	mb();

	/*
	 * Program VGIC to raise an interrupt to SP
	 */
	out_le32(CONFIG_SYS_FSL_SP_VSG_GIC_VIGR2, 0x1F);
	/* Allow the changes to reflect in VIGR2 */
	mb();

	dmb();
	debug("Polling for Debug server to launch ...\n");

	while (1) {
		debug_server_boot_status = in_le32(&gur->scratchrw[11 - 1]);
		if (!(debug_server_boot_status & DEBUG_SERVER_INIT_STATUS_MASK))
			break;

		udelay(1);	/* throttle polling */
		if (timeout-- <= 0)
			break;
	}

	if (timeout <= 0) {
		printf("Debug Server FW timed out (boot status: 0x%x)\n",
		       debug_server_boot_status);
		error = -ETIMEDOUT;
		goto out;
	}

	if (debug_server_boot_status & DEBUG_SERVER_INIT_STATUS_MASK) {
		printf("Debug server FW error'ed out (boot status: 0x%x)\n",
		       debug_server_boot_status);
		error = -ENODEV;
		goto out;
	}

	printf("Debug server booted\n");
	printf("Detected firmware %d.%d, (boot status: 0x0%x)\n",
	       debug_server_ver_info_maj, debug_server_ver_info_min,
	       debug_server_boot_status);

out:
	if (error != 0)
		debug_server_boot_status = -error;
	else
		debug_server_boot_status = 0;

	return debug_server_boot_status;
}