pic32mx.c 23.1 KB
Newer Older
oharboe's avatar
oharboe committed
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
/***************************************************************************
 *   Copyright (C) 2005 by Dominic Rath                                    *
 *   Dominic.Rath@gmx.de                                                   *
 *                                                                         *
 *   Copyright (C) 2008 by Spencer Oliver                                  *
 *   spen@spen-soft.co.uk                                                  *
 *                                                                         *
 *   Copyright (C) 2008 by John McCarthy                                   *
 *   jgmcc@magma.ca                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

Zachary T Welch's avatar
Zachary T Welch committed
30
#include "imp.h"
31
#include <target/algorithm.h>
32
#include <target/mips32.h>
Spencer Oliver's avatar
Spencer Oliver committed
33
#include <target/mips_m4k.h>
oharboe's avatar
oharboe committed
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

#define PIC32MX_MANUF_ID	0x029

/* pic32mx memory locations */

#define PIC32MX_PHYS_RAM			0x00000000
#define PIC32MX_PHYS_PGM_FLASH		0x1D000000
#define PIC32MX_PHYS_PERIPHERALS	0x1F800000
#define PIC32MX_PHYS_BOOT_FLASH		0x1FC00000

/*
 * Translate Virtual and Physical addresses.
 * Note: These macros only work for KSEG0/KSEG1 addresses.
 */

#define Virt2Phys(v) 	((v) & 0x1FFFFFFF)

/* pic32mx configuration register locations */

#define PIC32MX_DEVCFG0		0xBFC02FFC
#define PIC32MX_DEVCFG1		0xBFC02FF8
#define PIC32MX_DEVCFG2		0xBFC02FF4
#define PIC32MX_DEVCFG3		0xBFC02FF0
#define PIC32MX_DEVID		0xBF80F220

#define PIC32MX_BMXPFMSZ	0xBF882060
#define PIC32MX_BMXBOOTSZ	0xBF882070
#define PIC32MX_BMXDRMSZ	0xBF882040

/* pic32mx flash controller register locations */

#define PIC32MX_NVMCON		0xBF80F400
#define PIC32MX_NVMCONCLR	0xBF80F404
#define PIC32MX_NVMCONSET	0xBF80F408
#define PIC32MX_NVMCONINV	0xBF80F40C
#define NVMCON_NVMWR		(1 << 15)
#define NVMCON_NVMWREN		(1 << 14)
#define NVMCON_NVMERR		(1 << 13)
#define NVMCON_LVDERR		(1 << 12)
#define NVMCON_LVDSTAT		(1 << 11)
#define NVMCON_OP_PFM_ERASE		0x5
#define NVMCON_OP_PAGE_ERASE	0x4
#define NVMCON_OP_ROW_PROG		0x3
#define NVMCON_OP_WORD_PROG		0x1
#define NVMCON_OP_NOP			0x0

#define PIC32MX_NVMKEY		0xBF80F410
#define PIC32MX_NVMADDR		0xBF80F420
#define PIC32MX_NVMADDRCLR	0xBF80F424
#define PIC32MX_NVMADDRSET	0xBF80F428
#define PIC32MX_NVMADDRINV	0xBF80F42C
#define PIC32MX_NVMDATA		0xBF80F430
#define PIC32MX_NVMSRCADDR	0xBF80F440

/* flash unlock keys */

#define NVMKEY1			0xAA996655
#define NVMKEY2			0x556699AA

struct pic32mx_flash_bank
{
	struct working_area *write_algorithm;
	int probed;
};

100
static const struct pic32mx_devs_s {
101
	uint8_t	devid;
102
	const char *name;
oharboe's avatar
oharboe committed
103
} pic32mx_devs[] = {
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
	{0x38, "360F512L"},
	{0x34, "360F256L"},
	{0x2D, "340F128L"},
	{0x2A, "320F128L"},
	{0x16, "340F512H"},
	{0x12, "340F256H"},
	{0x0D, "340F128H"},
	{0x0A, "320F128H"},
	{0x06, "320F064H"},
	{0x02, "320F032H"},
	{0x07, "795F512L"},
	{0x0E, "795F512H"},
	{0x11, "675F512L"},
	{0x0C, "675F512H"},
	{0x0F, "575F512L"},
	{0x09, "575F512H"},
	{0x17, "575F256H"},
	{0x78, "460F512L"},
	{0x74, "460F256L"},
	{0x6D, "440F128L"},
	{0x56, "440F512H"},
	{0x52, "440F256H"},
	{0x4D, "440F128H"},
	{0x42, "420F032H"},
	{0x00, NULL}
oharboe's avatar
oharboe committed
129
130
131
132
};

/* flash bank pic32mx <base> <size> 0 0 <target#>
 */
133
FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command)
oharboe's avatar
oharboe committed
134
{
135
	struct pic32mx_flash_bank *pic32mx_info;
oharboe's avatar
oharboe committed
136

137
	if (CMD_ARGC < 6)
oharboe's avatar
oharboe committed
138
139
140
141
142
	{
		LOG_WARNING("incomplete flash_bank pic32mx configuration");
		return ERROR_FLASH_BANK_INVALID;
	}

143
	pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank));
oharboe's avatar
oharboe committed
144
145
146
147
148
149
150
151
	bank->driver_priv = pic32mx_info;

	pic32mx_info->write_algorithm = NULL;
	pic32mx_info->probed = 0;

	return ERROR_OK;
}

152
static uint32_t pic32mx_get_flash_status(struct flash_bank *bank)
oharboe's avatar
oharboe committed
153
{
Zachary T Welch's avatar
Zachary T Welch committed
154
	struct target *target = bank->target;
155
	uint32_t status;
oharboe's avatar
oharboe committed
156
157
158
159
160
161

	target_read_u32(target, PIC32MX_NVMCON, &status);

	return status;
}

162
static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout)
oharboe's avatar
oharboe committed
163
{
164
	uint32_t status;
oharboe's avatar
oharboe committed
165
166
167
168

	/* wait for busy to clear */
	while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0))
	{
169
		LOG_DEBUG("status: 0x%" PRIx32, status);
oharboe's avatar
oharboe committed
170
171
		alive_sleep(1);
	}
zwelch's avatar
zwelch committed
172
	if (timeout <= 0)
173
		LOG_DEBUG("timeout: status: 0x%" PRIx32, status);
oharboe's avatar
oharboe committed
174
175
176
177

	return status;
}

178
static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout)
oharboe's avatar
oharboe committed
179
{
Zachary T Welch's avatar
Zachary T Welch committed
180
	struct target *target = bank->target;
181
	uint32_t status;
oharboe's avatar
oharboe committed
182

zwelch's avatar
zwelch committed
183
	target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op);
oharboe's avatar
oharboe committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

	/* unlock flash registers */
	target_write_u32(target, PIC32MX_NVMKEY, NVMKEY1);
	target_write_u32(target, PIC32MX_NVMKEY, NVMKEY2);

	/* start operation */
	target_write_u32(target, PIC32MX_NVMCONSET, NVMCON_NVMWR);

	status = pic32mx_wait_status_busy(bank, timeout);

	/* lock flash registers */
	target_write_u32(target, PIC32MX_NVMCONCLR, NVMCON_NVMWREN);

	return status;
}

200
static int pic32mx_protect_check(struct flash_bank *bank)
oharboe's avatar
oharboe committed
201
{
Zachary T Welch's avatar
Zachary T Welch committed
202
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
203

204
	uint32_t devcfg0;
oharboe's avatar
oharboe committed
205
206
207
208
209
210
211
212
213
214
	int s;
	int num_pages;

	if (target->state != TARGET_HALTED)
	{
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

	target_read_u32(target, PIC32MX_DEVCFG0, &devcfg0);
215

zwelch's avatar
zwelch committed
216
	if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */
oharboe's avatar
oharboe committed
217
		num_pages = 0xffff;  /* All pages protected */
218
	else if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH)
oharboe's avatar
oharboe committed
219
	{
zwelch's avatar
zwelch committed
220
		if (devcfg0 & (1 << 24))
oharboe's avatar
oharboe committed
221
222
223
224
225
226
			num_pages = 0;       /* All pages unprotected */
		else
			num_pages = 0xffff;  /* All pages protected */
	}
	else /* pgm flash */
		num_pages = (~devcfg0 >> 12) & 0xff;
227

oharboe's avatar
oharboe committed
228
229
230
231
232
233
234
235
	for (s = 0; s < bank->num_sectors && s < num_pages; s++)
		bank->sectors[s].is_protected = 1;
	for (; s < bank->num_sectors; s++)
		bank->sectors[s].is_protected = 0;

	return ERROR_OK;
}

236
static int pic32mx_erase(struct flash_bank *bank, int first, int last)
oharboe's avatar
oharboe committed
237
{
Zachary T Welch's avatar
Zachary T Welch committed
238
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
239
	int i;
240
	uint32_t status;
oharboe's avatar
oharboe committed
241
242
243
244
245
246
247

	if (bank->target->state != TARGET_HALTED)
	{
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

248
249
	if ((first == 0) && (last == (bank->num_sectors - 1))
		&& (Virt2Phys(bank->base) == PIC32MX_PHYS_PGM_FLASH))
oharboe's avatar
oharboe committed
250
	{
251
252
		/* this will only erase the Program Flash (PFM), not the Boot Flash (BFM)
		 * we need to use the MTAP to perform a full erase */
253
		LOG_DEBUG("Erasing entire program flash");
oharboe's avatar
oharboe committed
254
		status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50);
255
		if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
256
			return ERROR_FLASH_OPERATION_FAILED;
257
		if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
258
259
260
261
262
263
			return ERROR_FLASH_OPERATION_FAILED;
		return ERROR_OK;
	}

	for (i = first; i <= last; i++)
	{
264
		target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(bank->base + bank->sectors[i].offset));
oharboe's avatar
oharboe committed
265
266
267

		status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10);

268
		if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
269
			return ERROR_FLASH_OPERATION_FAILED;
270
		if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
271
272
273
274
275
276
277
			return ERROR_FLASH_OPERATION_FAILED;
		bank->sectors[i].is_erased = 1;
	}

	return ERROR_OK;
}

278
static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last)
oharboe's avatar
oharboe committed
279
{
280
	struct pic32mx_flash_bank *pic32mx_info = NULL;
Zachary T Welch's avatar
Zachary T Welch committed
281
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
282
283
284
285
286
287
288
289
290
291
292
293

	pic32mx_info = bank->driver_priv;

	if (target->state != TARGET_HALTED)
	{
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

	return ERROR_OK;
}

294
295
/* see contib/loaders/flash/pic32mx.s for src */

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
static const uint32_t pic32mx_flash_write_code[] = {
					/* write: */
	0x3C08AA99,		/* lui $t0, 0xaa99 */
	0x35086655,		/* ori $t0, 0x6655 */
	0x3C095566,		/* lui $t1, 0x5566 */
	0x352999AA,		/* ori $t1, 0x99aa */
	0x3C0ABF80,		/* lui $t2, 0xbf80 */
	0x354AF400,		/* ori $t2, 0xf400 */
	0x340B4003,		/* ori $t3, $zero, 0x4003 */
	0x340C8000,		/* ori $t4, $zero, 0x8000 */
					/* write_row: */
	0x2CD30080,		/* sltiu $s3, $a2, 128 */
	0x16600008,		/* bne $s3, $zero, write_word */
	0x340D4000,		/* ori $t5, $zero, 0x4000 */
	0xAD450020,		/* sw $a1, 32($t2) */
	0xAD440040,		/* sw $a0, 64($t2) */
	0x04110016,		/* bal progflash */
	0x24840200,		/* addiu $a0, $a0, 512 */
	0x24A50200,		/* addiu $a1, $a1, 512 */
	0x1000FFF7,		/* beq $zero, $zero, write_row */
	0x24C6FF80,		/* addiu $a2, $a2, -128 */
					/* write_word: */
	0x3C15A000,		/* lui $s5, 0xa000 */
	0x36B50000,		/* ori $s5, $s5, 0x0 */
	0x00952025,		/* or $a0, $a0, $s5 */
	0x10000008,		/* beq $zero, $zero, next_word */
	0x340B4001,		/* ori $t3, $zero, 0x4001 */
					/* prog_word: */
	0x8C940000,		/* lw $s4, 0($a0) */
	0xAD540030,		/* sw $s4, 48($t2) */
	0xAD450020,		/* sw $a1, 32($t2) */
	0x04110009,		/* bal progflash */
	0x24840004,		/* addiu $a0, $a0, 4 */
	0x24A50004,		/* addiu $a1, $a1, 4 */
	0x24C6FFFF,		/* addiu $a2, $a2, -1 */
					/* next_word: */
	0x14C0FFF8,		/* bne $a2, $zero, prog_word */
	0x00000000,		/* nop */
					/* done: */
	0x10000002,		/* beq $zero, $zero, exit */
	0x24040000,		/* addiu $a0, $zero, 0 */
					/* error: */
	0x26240000,		/* addiu $a0, $s1, 0 */
					/* exit: */
	0x7000003F,		/* sdbbp */
					/* progflash: */
	0xAD4B0000,		/* sw $t3, 0($t2) */
	0xAD480010,		/* sw $t0, 16($t2) */
	0xAD490010,		/* sw $t1, 16($t2) */
	0xAD4C0008,		/* sw $t4, 8($t2) */
					/* waitflash: */
	0x8D500000,		/* lw $s0, 0($t2) */
	0x020C8024,		/* and $s0, $s0, $t4 */
	0x1600FFFD,		/* bne $s0, $zero, waitflash */
	0x00000000,		/* nop */
	0x00000000,		/* nop */
	0x00000000, 	/* nop */
	0x00000000,		/* nop */
	0x00000000,		/* nop */
	0x8D510000,		/* lw $s1, 0($t2) */
	0x30113000,		/* andi $s1, $zero, 0x3000 */
	0x1620FFEF,		/* bne $s1, $zero, error */
	0xAD4D0004,		/* sw $t5, 4($t2) */
	0x03E00008,		/* jr $ra */
	0x00000000		/* nop */
};

static int pic32mx_write_block(struct flash_bank *bank, uint8_t *buffer,
		uint32_t offset, uint32_t count)
oharboe's avatar
oharboe committed
365
{
Zachary T Welch's avatar
Zachary T Welch committed
366
	struct target *target = bank->target;
367
	uint32_t buffer_size = 16384;
368
	struct working_area *source;
369
	uint32_t address = bank->base + offset;
370
	struct reg_param reg_params[3];
371
	int retval = ERROR_OK;
372

373
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
374
	struct mips32_algorithm mips32_info;
oharboe's avatar
oharboe committed
375
376

	/* flash write code */
377
378
	if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code),
			&pic32mx_info->write_algorithm) != ERROR_OK)
oharboe's avatar
oharboe committed
379
380
381
382
383
	{
		LOG_WARNING("no working area available, can't do block memory writes");
		return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
	};

384
385
386
387
	if ((retval = target_write_buffer(target,
			pic32mx_info->write_algorithm->address,
			sizeof(pic32mx_flash_write_code),
			(uint8_t*)pic32mx_flash_write_code)) != ERROR_OK)
oharboe's avatar
oharboe committed
388
389
390
		return retval;

	/* memory buffer */
Øyvind Harboe's avatar
Øyvind Harboe committed
391
	while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK)
oharboe's avatar
oharboe committed
392
	{
393
394
395
396
397
398
399
		buffer_size /= 2;
		if (buffer_size <= 256)
		{
			/* if we already allocated the writing code, but failed to get a
			 * buffer, free the algorithm */
			if (pic32mx_info->write_algorithm)
				target_free_working_area(target, pic32mx_info->write_algorithm);
oharboe's avatar
oharboe committed
400

401
402
403
404
405
406
407
			LOG_WARNING("no large enough working area available, can't do block memory writes");
			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
		}
	};

	mips32_info.common_magic = MIPS32_COMMON_MAGIC;
	mips32_info.isa_mode = MIPS32_ISA_MIPS32;
oharboe's avatar
oharboe committed
408

409
410
411
412
413
	init_reg_param(&reg_params[0], "a0", 32, PARAM_IN_OUT);
	init_reg_param(&reg_params[1], "a1", 32, PARAM_OUT);
	init_reg_param(&reg_params[2], "a2", 32, PARAM_OUT);

	while (count > 0)
oharboe's avatar
oharboe committed
414
	{
415
		uint32_t status;
416
417
		uint32_t thisrun_count = (count > (buffer_size / 4)) ?
				(buffer_size / 4) : count;
oharboe's avatar
oharboe committed
418

419
420
		if ((retval = target_write_buffer(target, source->address,
				thisrun_count * 4, buffer)) != ERROR_OK)
oharboe's avatar
oharboe committed
421
422
			break;

423
424
425
		buf_set_u32(reg_params[0].value, 0, 32, Virt2Phys(source->address));
		buf_set_u32(reg_params[1].value, 0, 32, Virt2Phys(address));
		buf_set_u32(reg_params[2].value, 0, 32, thisrun_count);
oharboe's avatar
oharboe committed
426

427
428
		if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
				pic32mx_info->write_algorithm->address,
429
				0,
430
				10000, &mips32_info)) != ERROR_OK)
oharboe's avatar
oharboe committed
431
432
433
434
435
436
		{
			LOG_ERROR("error executing pic32mx flash write algorithm");
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}

437
438
439
		status = buf_get_u32(reg_params[0].value, 0, 32);

		if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
440
		{
zwelch's avatar
zwelch committed
441
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
442
443
444
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}
445
446
447

		if (status & NVMCON_LVDERR)
		{
zwelch's avatar
zwelch committed
448
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
449
450
451
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}
oharboe's avatar
oharboe committed
452

453
454
455
		buffer += thisrun_count * 4;
		address += thisrun_count * 4;
		count -= thisrun_count;
oharboe's avatar
oharboe committed
456
457
458
	}

	target_free_working_area(target, source);
459
	target_free_working_area(target, pic32mx_info->write_algorithm);
oharboe's avatar
oharboe committed
460

461
462
463
	destroy_reg_param(&reg_params[0]);
	destroy_reg_param(&reg_params[1]);
	destroy_reg_param(&reg_params[2]);
oharboe's avatar
oharboe committed
464
465
466
467

	return retval;
}

468
static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word)
oharboe's avatar
oharboe committed
469
{
Zachary T Welch's avatar
Zachary T Welch committed
470
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
471

472
	target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(address));
oharboe's avatar
oharboe committed
473
474
475
476
477
	target_write_u32(target, PIC32MX_NVMDATA, word);

	return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5);
}

478
static int pic32mx_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
479
{
480
481
482
483
484
	uint32_t words_remaining = (count / 4);
	uint32_t bytes_remaining = (count & 0x00000003);
	uint32_t address = bank->base + offset;
	uint32_t bytes_written = 0;
	uint32_t status;
485
	int retval;
oharboe's avatar
oharboe committed
486
487
488
489
490
491
492

	if (bank->target->state != TARGET_HALTED)
	{
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

493
494
495
	LOG_DEBUG("writing to flash at address 0x%08" PRIx32 " at offset 0x%8.8" PRIx32
			" count: 0x%8.8" PRIx32 "", bank->base, offset, count);

oharboe's avatar
oharboe committed
496
497
	if (offset & 0x3)
	{
duane's avatar
duane committed
498
		LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset);
oharboe's avatar
oharboe committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
	}

	/* multiple words (4-byte) to be programmed? */
	if (words_remaining > 0)
	{
		/* try using a block write */
		if ((retval = pic32mx_write_block(bank, buffer, offset, words_remaining)) != ERROR_OK)
		{
			if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
			{
				/* if block write failed (no sufficient working area),
				 * we use normal (slow) single dword accesses */
				LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
			}
			else if (retval == ERROR_FLASH_OPERATION_FAILED)
			{
516
517
				LOG_ERROR("flash writing failed");
				return retval;
oharboe's avatar
oharboe committed
518
519
520
521
522
523
524
525
526
527
528
529
			}
		}
		else
		{
			buffer += words_remaining * 4;
			address += words_remaining * 4;
			words_remaining = 0;
		}
	}

	while (words_remaining > 0)
	{
530
531
		uint32_t value;
		memcpy(&value, buffer + bytes_written, sizeof(uint32_t));
oharboe's avatar
oharboe committed
532

533
		status = pic32mx_write_word(bank, address, value);
534

535
		if (status & NVMCON_NVMERR)
536
537
		{
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
538
			return ERROR_FLASH_OPERATION_FAILED;
539
540
		}

541
		if (status & NVMCON_LVDERR)
542
543
		{
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
544
			return ERROR_FLASH_OPERATION_FAILED;
545
		}
oharboe's avatar
oharboe committed
546
547
548
549
550
551
552
553

		bytes_written += 4;
		words_remaining--;
		address += 4;
	}

	if (bytes_remaining)
	{
554
		uint32_t value = 0xffffffff;
555
		memcpy(&value, buffer + bytes_written, bytes_remaining);
oharboe's avatar
oharboe committed
556

557
		status = pic32mx_write_word(bank, address, value);
558

559
		if (status & NVMCON_NVMERR)
560
561
		{
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
562
			return ERROR_FLASH_OPERATION_FAILED;
563
564
		}

565
		if (status & NVMCON_LVDERR)
566
567
		{
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
568
			return ERROR_FLASH_OPERATION_FAILED;
569
		}
oharboe's avatar
oharboe committed
570
571
572
573
574
	}

	return ERROR_OK;
}

575
static int pic32mx_probe(struct flash_bank *bank)
oharboe's avatar
oharboe committed
576
{
Zachary T Welch's avatar
Zachary T Welch committed
577
	struct target *target = bank->target;
578
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
579
	struct mips32_common *mips32 = target->arch_info;
580
	struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
oharboe's avatar
oharboe committed
581
	int i;
582
	uint32_t num_pages = 0;
583
	uint32_t device_id;
oharboe's avatar
oharboe committed
584
585
586
587
588
	int page_size;

	pic32mx_info->probed = 0;

	device_id = ejtag_info->idcode;
589
	LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%02x, ver 0x%02x)",
duane's avatar
duane committed
590
			  device_id,
591
592
593
			  (unsigned)((device_id >> 1) & 0x7ff),
			  (unsigned)((device_id >> 12) & 0xff),
			  (unsigned)((device_id >> 28) & 0xf));
oharboe's avatar
oharboe committed
594

595
	if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
596
		LOG_WARNING("Cannot identify target as a PIC32MX family.");
oharboe's avatar
oharboe committed
597
598
599
600
601
		return ERROR_FLASH_OPERATION_FAILED;
	}

	page_size = 4096;

602
	if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH)
oharboe's avatar
oharboe committed
603
	{
604
605
606
607
608
609
610
611
612
613
614
615
616
		/* 0x1FC00000: Boot flash size */
#if 0
		/* for some reason this register returns 8k for the boot bank size
		 * this does not match the docs, so for now set the boot bank at a
		 * fixed 12k */
		if (target_read_u32(target, PIC32MX_BMXBOOTSZ, &num_pages) != ERROR_OK) {
			LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 12k flash");
			num_pages = (12 * 1024);
		}
#else
		/* fixed 12k boot bank - see comments above */
		num_pages = (12 * 1024);
#endif
oharboe's avatar
oharboe committed
617
	}
618
	else
oharboe's avatar
oharboe committed
619
	{
620
621
622
623
624
		/* read the flash size from the device */
		if (target_read_u32(target, PIC32MX_BMXPFMSZ, &num_pages) != ERROR_OK) {
			LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 512k flash");
			num_pages = (512 * 1024);
		}
oharboe's avatar
oharboe committed
625
626
	}

627
	LOG_INFO("flash size = %" PRId32 "kbytes", num_pages / 1024);
oharboe's avatar
oharboe committed
628

629
630
631
632
633
634
	if (bank->sectors)
	{
		free(bank->sectors);
		bank->sectors = NULL;
	}

oharboe's avatar
oharboe committed
635
	/* calculate numbers of pages */
636
	num_pages /= page_size;
oharboe's avatar
oharboe committed
637
638
	bank->size = (num_pages * page_size);
	bank->num_sectors = num_pages;
639
	bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
oharboe's avatar
oharboe committed
640

641
	for (i = 0; i < (int)num_pages; i++)
oharboe's avatar
oharboe committed
642
643
644
645
646
647
648
649
650
651
652
653
	{
		bank->sectors[i].offset = i * page_size;
		bank->sectors[i].size = page_size;
		bank->sectors[i].is_erased = -1;
		bank->sectors[i].is_protected = 1;
	}

	pic32mx_info->probed = 1;

	return ERROR_OK;
}

654
static int pic32mx_auto_probe(struct flash_bank *bank)
oharboe's avatar
oharboe committed
655
{
656
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
oharboe's avatar
oharboe committed
657
658
659
660
661
	if (pic32mx_info->probed)
		return ERROR_OK;
	return pic32mx_probe(bank);
}

662
static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size)
oharboe's avatar
oharboe committed
663
{
Zachary T Welch's avatar
Zachary T Welch committed
664
	struct target *target = bank->target;
665
	struct mips32_common *mips32 = target->arch_info;
666
	struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
667
	uint32_t device_id;
668
	int printed = 0, i;
oharboe's avatar
oharboe committed
669
670
671

	device_id = ejtag_info->idcode;

672
	if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
673
674
		snprintf(buf, buf_size,
				 "Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n",
675
				 (unsigned)((device_id >> 1) & 0x7ff),
duane's avatar
duane committed
676
				 PIC32MX_MANUF_ID);
oharboe's avatar
oharboe committed
677
678
		return ERROR_FLASH_OPERATION_FAILED;
	}
679

zwelch's avatar
zwelch committed
680
	for (i = 0; pic32mx_devs[i].name != NULL; i++)
681
	{
zwelch's avatar
zwelch committed
682
		if (pic32mx_devs[i].devid == ((device_id >> 12) & 0xff)) {
oharboe's avatar
oharboe committed
683
684
685
686
687
			printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name);
			break;
		}
	}

688
689
	if (pic32mx_devs[i].name == NULL) {
		printed = snprintf(buf, buf_size, "Unknown");
oharboe's avatar
oharboe committed
690
691
	}

692
693
694
695
	buf += printed;
	buf_size -= printed;
	printed = snprintf(buf, buf_size, "  Ver: 0x%02x",
			(unsigned)((device_id >> 28) & 0xf));
oharboe's avatar
oharboe committed
696
697
698
699

	return ERROR_OK;
}

700
COMMAND_HANDLER(pic32mx_handle_pgm_word_command)
oharboe's avatar
oharboe committed
701
{
702
	uint32_t address, value;
oharboe's avatar
oharboe committed
703
704
	int status, res;

705
	if (CMD_ARGC != 3)
oharboe's avatar
oharboe committed
706
	{
707
		command_print(CMD_CTX, "pic32mx pgm_word <addr> <value> <bank>");
oharboe's avatar
oharboe committed
708
709
710
		return ERROR_OK;
	}

711
712
	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
713

714
	struct flash_bank *bank;
715
	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank);
716
717
	if (ERROR_OK != retval)
		return retval;
oharboe's avatar
oharboe committed
718

zwelch's avatar
zwelch committed
719
	if (address < bank->base || address >= (bank->base + bank->size))
oharboe's avatar
oharboe committed
720
	{
721
		command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]);
oharboe's avatar
oharboe committed
722
723
724
725
726
		return ERROR_OK;
	}

	res = ERROR_OK;
	status = pic32mx_write_word(bank, address, value);
727
	if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
728
		res = ERROR_FLASH_OPERATION_FAILED;
729
	if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
730
731
732
		res = ERROR_FLASH_OPERATION_FAILED;

	if (res == ERROR_OK)
733
		command_print(CMD_CTX, "pic32mx pgm word complete");
oharboe's avatar
oharboe committed
734
	else
735
		command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status);
oharboe's avatar
oharboe committed
736
737
738

	return ERROR_OK;
}
739

Spencer Oliver's avatar
Spencer Oliver committed
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
COMMAND_HANDLER(pic32mx_handle_unlock_command)
{
	uint32_t mchip_cmd;
	struct target *target = NULL;
	struct mips_m4k_common *mips_m4k;
	struct mips_ejtag *ejtag_info;
	int timeout = 10;

	if (CMD_ARGC < 1)
	{
		command_print(CMD_CTX, "pic32mx unlock <bank>");
		return ERROR_OK;
	}

	struct flash_bank *bank;
	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
	if (ERROR_OK != retval)
		return retval;

	target = bank->target;
	mips_m4k = target_to_m4k(target);
	ejtag_info = &mips_m4k->mips32.ejtag_info;

	/* we have to use the MTAP to perform a full erase */
	mips_ejtag_set_instr(ejtag_info, MTAP_SW_MTAP);
	mips_ejtag_set_instr(ejtag_info, MTAP_COMMAND);

	/* first check status of device */
	mchip_cmd = MCHP_STATUS;
	mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
	if (mchip_cmd & (1 << 7))
	{
		/* device is not locked */
		command_print(CMD_CTX, "pic32mx is already unlocked, erasing anyway");
	}

	/* unlock/erase device */
	mchip_cmd = MCHP_ASERT_RST;
	mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);

	mchip_cmd = MCHP_ERASE;
	mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);

	do {
		mchip_cmd = MCHP_STATUS;
		mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
		if (timeout-- == 0)
		{
			LOG_DEBUG("timeout waiting for unlock: 0x%" PRIx32 "", mchip_cmd);
			break;
		}
		alive_sleep(1);
	} while ((mchip_cmd & (1 << 2)) || (!(mchip_cmd & (1 << 3))));

	mchip_cmd = MCHP_DE_ASSERT_RST;
	mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);

	/* select ejtag tap */
	mips_ejtag_set_instr(ejtag_info, MTAP_SW_ETAP);

	command_print(CMD_CTX, "pic32mx unlocked.\n"
			"INFO: a reset or power cycle is required "
			"for the new settings to take effect.");

	return ERROR_OK;
}

807
808
809
static const struct command_registration pic32mx_exec_command_handlers[] = {
	{
		.name = "pgm_word",
David Brownell's avatar
David Brownell committed
810
		.handler = pic32mx_handle_pgm_word_command,
811
812
813
		.mode = COMMAND_EXEC,
		.help = "program a word",
	},
Spencer Oliver's avatar
Spencer Oliver committed
814
815
816
817
818
819
820
	{
		.name = "unlock",
		.handler = pic32mx_handle_unlock_command,
		.mode = COMMAND_EXEC,
		.usage = "[bank_id]",
		.help = "Unlock/Erase entire device.",
	},
821
822
	COMMAND_REGISTRATION_DONE
};
823

824
825
826
827
828
829
830
831
832
static const struct command_registration pic32mx_command_handlers[] = {
	{
		.name = "pic32mx",
		.mode = COMMAND_ANY,
		.help = "pic32mx flash command group",
		.chain = pic32mx_exec_command_handlers,
	},
	COMMAND_REGISTRATION_DONE
};
833

834
struct flash_driver pic32mx_flash = {
David Brownell's avatar
David Brownell committed
835
836
837
838
839
840
	.name = "pic32mx",
	.commands = pic32mx_command_handlers,
	.flash_bank_command = pic32mx_flash_bank_command,
	.erase = pic32mx_erase,
	.protect = pic32mx_protect,
	.write = pic32mx_write,
841
	.read = default_flash_read,
David Brownell's avatar
David Brownell committed
842
843
844
845
846
847
	.probe = pic32mx_probe,
	.auto_probe = pic32mx_auto_probe,
	.erase_check = default_flash_mem_blank_check,
	.protect_check = pic32mx_protect_check,
	.info = pic32mx_info,
};