summaryrefslogtreecommitdiffstats
path: root/drivers/brcm/chimp.c
blob: 81767bb5b0f2c8d921f8ef5a0c412765caf2f045 (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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/*
 * Copyright (c) 2016 - 2020, Broadcom
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <string.h>

#include <drivers/delay_timer.h>

#include <chimp.h>
#include <chimp_nv_defs.h>

#define CHIMP_DEFAULT_STARTUP_ADDR 0xb4300000

/* ChiMP's view of APE scratchpad memory for fastboot */
#define CHIMP_FASTBOOT_ADDR 0x61000000

#define CHIMP_PREPARE_ACCESS_WINDOW(addr) \
	(\
		mmio_write_32(\
			NIC400_NITRO_CHIMP_S_IDM_IO_CONTROL_DIRECT, \
			addr & 0xffc00000)\
	)
#define CHIMP_INDIRECT_TGT_ADDR(addr) \
	(CHIMP_INDIRECT_BASE + (addr & CHIMP_INDIRECT_ADDR_MASK))

#define CHIMP_CTRL_ADDR(x) (CHIMP_REG_CTRL_BASE + x)

/* For non-PAXC builds */
#ifndef CHIMP_FB1_ENTRY
#define CHIMP_FB1_ENTRY 0
#endif

#define CHIMP_DBG	VERBOSE

void bcm_chimp_write(uintptr_t addr, uint32_t value)
{
	CHIMP_PREPARE_ACCESS_WINDOW(addr);
	mmio_write_32(CHIMP_INDIRECT_TGT_ADDR(addr), value);
}

uint32_t bcm_chimp_read(uintptr_t addr)
{
	CHIMP_PREPARE_ACCESS_WINDOW(addr);
	return mmio_read_32(CHIMP_INDIRECT_TGT_ADDR(addr));
}

void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits)
{
	CHIMP_PREPARE_ACCESS_WINDOW(addr);
	mmio_clrbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
}

void bcm_chimp_setbits(uintptr_t addr, uint32_t bits)
{
	CHIMP_PREPARE_ACCESS_WINDOW(addr);
	mmio_setbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
}

int bcm_chimp_is_nic_mode(void)
{
	uint32_t val;

	/* Check if ChiMP straps are set */
	val = mmio_read_32(CDRU_CHIP_STRAP_DATA_LSW);
	val &= CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;

	return val == CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;
}

void bcm_chimp_fru_prog_done(bool is_done)
{
	uint32_t val;

	val = is_done ? (1 << CHIMP_FRU_PROG_DONE_BIT) : 0;
	bcm_chimp_setbits(CHIMP_REG_ECO_RESERVED, val);
}

int bcm_chimp_handshake_done(void)
{
	uint32_t value;

	value = bcm_chimp_read(CHIMP_REG_ECO_RESERVED);
	value &= (1 << CHIMP_FLASH_ACCESS_DONE_BIT);

	return value != 0;
}

int bcm_chimp_wait_handshake(void)
{
	uint32_t timeout = CHIMP_HANDSHAKE_TIMEOUT_MS;
	uint32_t status;

	INFO("Waiting for ChiMP handshake...\n");
	do {
		if (bcm_chimp_handshake_done())
			break;
		/* No need to wait if ChiMP reported an error */
		status = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_STAT_REG);
		if (status & CHIMP_ERROR_MASK) {
			ERROR("ChiMP error 0x%x. Wait aborted\n", status);
			break;
		}
		mdelay(1);
	} while (--timeout);

	if (!bcm_chimp_handshake_done()) {
		if (timeout == 0) {
			WARN("Timeout waiting for ChiMP handshake\n");
		}
	} else {
		INFO("Got handshake from ChiMP!\n");
	}

	return bcm_chimp_handshake_done();
}

uint32_t bcm_chimp_read_ctrl(uint32_t offset)
{
	return bcm_chimp_read(CHIMP_CTRL_ADDR(offset));
}

static int bcm_chimp_nitro_reset(void)
{
	uint32_t timeout;

	/* Perform tasks done by M0 in NIC mode */
	CHIMP_DBG("Taking Nitro out of reset\n");
	mmio_setbits_32(CDRU_MISC_RESET_CONTROL,
		/* MHB_RESET_N */
		(1 << CDRU_MISC_RESET_CONTROL__CDRU_MHB_RESET_N_R)  |
		/* PCI_RESET_N */
		(1 << CDRU_MISC_RESET_CONTROL__CDRU_PCIE_RESET_N_R) |
		/* PM_RESET_N */
		(1 << CDRU_MISC_RESET_CONTROL__CDRU_PM_RESET_N_R)   |
		/* NIC_RESET_N */
		(1 << CDRU_MISC_RESET_CONTROL__CDRU_NITRO_RESET_N_R)
	);

	/* Wait until Nitro is out of reset */
	timeout = NIC_RESET_RELEASE_TIMEOUT_US;
	do {
		uint32_t value;

		value = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_MODE_REG);
		if ((value & CHIMP_BPE_MODE_ID_MASK) ==
				CHIMP_BPE_MODE_ID_PATTERN)
			break;
		udelay(1);
	} while (--timeout);

	if (timeout == 0) {
		ERROR("NIC reset release timed out\n");
		return -1;
	}

	return 0;
}

static void bcm_nitro_secure_mode_enable(void)
{
	mmio_setbits_32(CDRU_NITRO_CONTROL,
		(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_MODE_R) |
		(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_OVERRIDE_R));
	mmio_write_32(NITRO_TZPC_TZPCDECPROT0clr,
		/* NITRO_TZPC */
		1 << NITRO_TZPC_TZPCDECPROT0clr__DECPROT0_chimp_m_clr_R);
}

static int bcm_chimp_reset_and_initial_setup(void)
{

	int err;
	uint32_t handshake_reg;

	err = bcm_chimp_nitro_reset();
	if (err)
		return err;

	/* Enable Nitro secure mode */
	bcm_nitro_secure_mode_enable();

	/* Force ChiMP back into reset */
	bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
		1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);

	handshake_reg = (1 << SR_IN_SMARTNIC_MODE_BIT);

	/* Get OTP secure Chimp boot status */
	if (mmio_read_32(CRMU_OTP_STATUS) & (1 << CRMU_OTP_STATUS_BIT))
		handshake_reg |= (1 << SR_CHIMP_SECURE_BOOT_BIT);

	bcm_chimp_write(CHIMP_REG_ECO_RESERVED, handshake_reg);

	CHIMP_DBG("ChiMP reset and initial handshake parameters set\n");

	return 0;
}

static void bcm_nitro_chimp_release_reset(void)
{
	bcm_chimp_clrbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
		1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);

	CHIMP_DBG("Nitro Reset Released\n");
}

static void bcm_chimp_set_fastboot(int mode)
{
	uint32_t fb_entry;

	/* 1. Enable fastboot */
	bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
			(1 << CHIMP_FAST_BOOT_MODE_BIT));
	fb_entry = CHIMP_FASTBOOT_ADDR | mode;
	if (mode == CHIMP_FASTBOOT_JUMP_IN_PLACE)
		fb_entry = CHIMP_FB1_ENTRY;
	/* 2. Write startup address and mode */
	INFO("Setting fastboot type %d entry to 0x%x\n", mode, fb_entry);
	bcm_chimp_write(
			CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_FSTBOOT_PTR_REG),
			fb_entry);
}

#ifndef CHIMPFW_USE_SIDELOAD
static void bcm_chimp_load_fw_from_spi(uintptr_t spi_addr, size_t size)
{
	uintptr_t ape_scpad;
	uintptr_t dest;
	size_t bytes_left;

	ape_scpad = CHIMP_REG_CHIMP_APE_SCPAD;
	dest = CHIMP_INDIRECT_TGT_ADDR(CHIMP_REG_CHIMP_APE_SCPAD);
	bytes_left = size;

	while (bytes_left) {
		uint32_t delta;

		delta = bytes_left > CHIMP_WINDOW_SIZE ?
			bytes_left - CHIMP_WINDOW_SIZE : bytes_left;
		CHIMP_PREPARE_ACCESS_WINDOW(ape_scpad);
		INFO("Transferring %d byte(s) from 0x%lx to 0x%lx\n",
			delta, spi_addr, dest);
	/*
	 * This single memcpy call takes significant amount of time
	 * on Palladium. Be patient
	 */
		memcpy((void *)dest, (void *)spi_addr, delta);
		bytes_left -= delta;
		INFO("Transferred %d byte(s) from 0x%lx to 0x%lx (%lu%%)\n",
			delta, spi_addr, dest,
			((size - bytes_left) * 100)/size);
		spi_addr += delta;
		dest += delta;
		ape_scpad += delta;
	}
}

static int bcm_chimp_find_fw_in_spi(uintptr_t *addr, size_t *size)
{
	int i;
	bnxnvm_master_block_header_t *master_block_hdr;
	bnxnvm_directory_block_header_t *dir_block_hdr;
	bnxnvm_directory_entry_t *dir_entry;
	int found;

	found = 0;

	/* Read the master block */
	master_block_hdr =
		(bnxnvm_master_block_header_t *)(uintptr_t)QSPI_BASE_ADDR;
	if (master_block_hdr->sig != BNXNVM_MASTER_BLOCK_SIG) {
		WARN("Invalid masterblock 0x%x (expected 0x%x)\n",
			master_block_hdr->sig,
			BNXNVM_MASTER_BLOCK_SIG);
		return -NV_NOT_NVRAM;
	}
	if ((master_block_hdr->block_size > NV_MAX_BLOCK_SIZE) ||
		(master_block_hdr->directory_offset >=
			master_block_hdr->nvram_size)) {
		WARN("Invalid masterblock block size 0x%x or directory offset 0x%x\n",
			master_block_hdr->block_size,
			master_block_hdr->directory_offset);
		return -NV_BAD_MB;
	}

	/* Skip to the Directory block start */
	dir_block_hdr =
		(bnxnvm_directory_block_header_t *)
			((uintptr_t)QSPI_BASE_ADDR +
				master_block_hdr->directory_offset);
	if (dir_block_hdr->sig != BNXNVM_DIRECTORY_BLOCK_SIG) {
		WARN("Invalid directory header 0x%x (expected 0x%x)\n",
			dir_block_hdr->sig,
			BNXNVM_DIRECTORY_BLOCK_SIG);
		return -NV_BAD_DIR_HEADER;
	}

	/* Locate the firmware */
	for (i = 0; i < dir_block_hdr->entries; i++) {
		*addr = ((uintptr_t)dir_block_hdr + dir_block_hdr->length +
			i * dir_block_hdr->entry_length);
		dir_entry = (bnxnvm_directory_entry_t *)(*addr);
		if ((dir_entry->type == BNX_DIR_TYPE_BOOTCODE) ||
				(dir_entry->type == BNX_DIR_TYPE_BOOTCODE_2)) {
			found = 1;
			break;
		}
	}

	if (!found)
		return -NV_FW_NOT_FOUND;

	*addr = QSPI_BASE_ADDR + dir_entry->item_location;
	*size = dir_entry->data_length;

	INFO("Found chimp firmware at 0x%lx, size %lu byte(s)\n",
			*addr, *size);

	return NV_OK;
}
#endif

int bcm_chimp_initiate_fastboot(int fastboot_type)
{
	int err;

	if ((fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) &&
			(fastboot_type <= CHIMP_FASTBOOT_JUMP_DECOMPRESS)) {
		CHIMP_DBG("Initiating ChiMP fastboot type %d\n", fastboot_type);
	}

	/*
	 * If we are here, M0 did not setup Nitro because NIC mode
	 * strap was not present
	 */
	err = bcm_chimp_reset_and_initial_setup();
	if (err)
		return err;

	if (fastboot_type > CHIMP_FASTBOOT_JUMP_DECOMPRESS) {
		WARN("ChiMP setup deferred\n");
		return -1;
	}

	if (fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) {

		if ((fastboot_type == CHIMP_FASTBOOT_JUMP_IN_PLACE) &&
			(CHIMP_FB1_ENTRY == 0)) {
			ERROR("Missing ESAL entry point for fastboot type 1.\n"
			"Fastboot failed\n");
			return -1;
		}

		/*
		 * TODO: We need to think of the way to load the ChiMP fw.
		 * This could be SPI, NAND, etc.
		 * For now we temporarily stick to the SPI load unless
		 * CHIMPFW_USE_SIDELOAD is defined. Note that for the SPI NVRAM
		 * image we need to parse directory and get the image.
		 * When we load image from other media there is no need to
		 * parse because fw image can be directly placed into the APE's
		 * scratchpad.
		 * For sideload method we simply reset the ChiMP, set bpe_reg
		 * to do fastboot with the type we define, and release from
		 * reset so that ROM loader would initiate fastboot immediately
		 */
#ifndef CHIMPFW_USE_SIDELOAD
		{
			uintptr_t spi_addr;
			size_t size;

			err = bcm_chimp_find_fw_in_spi(&spi_addr, &size);
			if (!err) {
				INFO("Loading ChiMP firmware, addr 0x%lx, size %lu byte(s)\n",
					spi_addr, size);
				bcm_chimp_load_fw_from_spi(spi_addr, size);
			} else {
				ERROR("Error %d ChiMP firmware not in NVRAM directory!\n",
					err);
			}
		}
#else
		INFO("Skip ChiMP QSPI fastboot type %d due to sideload requested\n",
		     fastboot_type);
#endif
		if (!err) {
			INFO("Instruct ChiMP to fastboot\n");
			bcm_chimp_set_fastboot(fastboot_type);
			INFO("Fastboot mode set\n");
		}
	}

	bcm_nitro_chimp_release_reset();

	return err;
}