pic32mx.c 27.6 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
/***************************************************************************
 *   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     *
22
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
oharboe's avatar
oharboe committed
23
 ***************************************************************************/
24

oharboe's avatar
oharboe committed
25
26
27
28
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

29
#include <jtag/jtag.h>
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
#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.
 */

49
#define Virt2Phys(v)	((v) & 0x1FFFFFFF)
50
51
52

/* pic32mx configuration register locations */

53
#define PIC32MX_DEVCFG0_1xx_2xx	0xBFC00BFC
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
#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

94
95
#define MX_1xx_2xx			1	/* PIC32mx1xx/2xx */
#define MX_17x_27x			2	/* PIC32mx17x/27x */
96

97
struct pic32mx_flash_bank {
98
	int probed;
99
	int dev_type;		/* Default 0. 1 for Pic32MX1XX/2XX variant */
100
101
};

102
/*
103
 * DEVID values as per PIC32MX Flash Programming Specification Rev N
104
105
 */

106
static const struct pic32mx_devs_s {
107
	uint32_t devid;
108
	const char *name;
oharboe's avatar
oharboe committed
109
} pic32mx_devs[] = {
110
111
112
113
114
115
	{0x04A07053, "110F016B"},
	{0x04A09053, "110F016C"},
	{0x04A0B053, "110F016D"},
	{0x04A06053, "120F032B"},
	{0x04A08053, "120F032C"},
	{0x04A0A053, "120F032D"},
116
117
118
119
120
121
	{0x04D07053, "130F064B"},
	{0x04D09053, "130F064C"},
	{0x04D0B053, "130F064D"},
	{0x04D06053, "150F128B"},
	{0x04D08053, "150F128C"},
	{0x04D0A053, "150F128D"},
122
123
	{0x06610053, "170F256B"},
	{0x0661A053, "170F256D"},
124
125
126
127
128
129
	{0x04A01053, "210F016B"},
	{0x04A03053, "210F016C"},
	{0x04A05053, "210F016D"},
	{0x04A00053, "220F032B"},
	{0x04A02053, "220F032C"},
	{0x04A04053, "220F032D"},
130
131
132
133
134
135
	{0x04D01053, "230F064B"},
	{0x04D03053, "230F064C"},
	{0x04D05053, "230F064D"},
	{0x04D00053, "250F128B"},
	{0x04D02053, "250F128C"},
	{0x04D04053, "250F128D"},
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
	{0x06600053, "270F256B"},
	{0x0660A053, "270F256D"},
	{0x05600053, "330F064H"},
	{0x05601053, "330F064L"},
	{0x05602053, "430F064H"},
	{0x05603053, "430F064L"},
	{0x0570C053, "350F128H"},
	{0x0570D053, "350F128L"},
	{0x0570E053, "450F128H"},
	{0x0570F053, "450F128L"},
	{0x05704053, "350F256H"},
	{0x05705053, "350F256L"},
	{0x05706053, "450F256H"},
	{0x05707053, "450F256L"},
	{0x05808053, "370F512H"},
	{0x05809053, "370F512L"},
	{0x0580A053, "470F512H"},
	{0x0580B053, "470F512L"},
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
	{0x00938053, "360F512L"},
	{0x00934053, "360F256L"},
	{0x0092D053, "340F128L"},
	{0x0092A053, "320F128L"},
	{0x00916053, "340F512H"},
	{0x00912053, "340F256H"},
	{0x0090D053, "340F128H"},
	{0x0090A053, "320F128H"},
	{0x00906053, "320F064H"},
	{0x00902053, "320F032H"},
	{0x00978053, "460F512L"},
	{0x00974053, "460F256L"},
	{0x0096D053, "440F128L"},
	{0x00952053, "440F256H"},
	{0x00956053, "440F512H"},
	{0x0094D053, "440F128H"},
	{0x00942053, "420F032H"},
	{0x04307053, "795F512L"},
	{0x0430E053, "795F512H"},
	{0x04306053, "775F512L"},
	{0x0430D053, "775F512H"},
	{0x04312053, "775F256L"},
	{0x04303053, "775F256H"},
	{0x04417053, "764F128L"},
	{0x0440B053, "764F128H"},
	{0x04341053, "695F512L"},
	{0x04325053, "695F512H"},
	{0x04311053, "675F512L"},
	{0x0430C053, "675F512H"},
	{0x04305053, "675F256L"},
	{0x0430B053, "675F256H"},
	{0x04413053, "664F128L"},
	{0x04407053, "664F128H"},
	{0x04411053, "664F064L"},
	{0x04405053, "664F064H"},
	{0x0430F053, "575F512L"},
	{0x04309053, "575F512H"},
	{0x04333053, "575F256L"},
	{0x04317053, "575F256H"},
	{0x0440F053, "564F128L"},
	{0x04403053, "564F128H"},
	{0x0440D053, "564F064L"},
	{0x04401053, "564F064H"},
	{0x04400053, "534F064H"},
	{0x0440C053, "534F064L"},
	{0x00000000, NULL}
oharboe's avatar
oharboe committed
200
201
202
203
};

/* flash bank pic32mx <base> <size> 0 0 <target#>
 */
204
FLASH_BANK_COMMAND_HANDLER(pic32mx_flash_bank_command)
oharboe's avatar
oharboe committed
205
{
206
	struct pic32mx_flash_bank *pic32mx_info;
oharboe's avatar
oharboe committed
207

208
	if (CMD_ARGC < 6)
Mathias K's avatar
Mathias K committed
209
		return ERROR_COMMAND_SYNTAX_ERROR;
oharboe's avatar
oharboe committed
210

211
	pic32mx_info = malloc(sizeof(struct pic32mx_flash_bank));
oharboe's avatar
oharboe committed
212
213
214
	bank->driver_priv = pic32mx_info;

	pic32mx_info->probed = 0;
215
	pic32mx_info->dev_type = 0;
oharboe's avatar
oharboe committed
216
217
218
219

	return ERROR_OK;
}

220
static uint32_t pic32mx_get_flash_status(struct flash_bank *bank)
oharboe's avatar
oharboe committed
221
{
Zachary T Welch's avatar
Zachary T Welch committed
222
	struct target *target = bank->target;
223
	uint32_t status;
oharboe's avatar
oharboe committed
224
225
226
227
228
229

	target_read_u32(target, PIC32MX_NVMCON, &status);

	return status;
}

230
static uint32_t pic32mx_wait_status_busy(struct flash_bank *bank, int timeout)
oharboe's avatar
oharboe committed
231
{
232
	uint32_t status;
oharboe's avatar
oharboe committed
233
234

	/* wait for busy to clear */
235
	while (((status = pic32mx_get_flash_status(bank)) & NVMCON_NVMWR) && (timeout-- > 0)) {
236
		LOG_DEBUG("status: 0x%" PRIx32, status);
oharboe's avatar
oharboe committed
237
238
		alive_sleep(1);
	}
zwelch's avatar
zwelch committed
239
	if (timeout <= 0)
240
		LOG_DEBUG("timeout: status: 0x%" PRIx32, status);
oharboe's avatar
oharboe committed
241
242
243
244

	return status;
}

245
static int pic32mx_nvm_exec(struct flash_bank *bank, uint32_t op, uint32_t timeout)
oharboe's avatar
oharboe committed
246
{
Zachary T Welch's avatar
Zachary T Welch committed
247
	struct target *target = bank->target;
248
	uint32_t status;
oharboe's avatar
oharboe committed
249

zwelch's avatar
zwelch committed
250
	target_write_u32(target, PIC32MX_NVMCON, NVMCON_NVMWREN | op);
oharboe's avatar
oharboe committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

	/* 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;
}

267
static int pic32mx_protect_check(struct flash_bank *bank)
oharboe's avatar
oharboe committed
268
{
Zachary T Welch's avatar
Zachary T Welch committed
269
	struct target *target = bank->target;
270
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
oharboe's avatar
oharboe committed
271

272
	uint32_t config0_address;
273
	uint32_t devcfg0;
oharboe's avatar
oharboe committed
274
275
276
	int s;
	int num_pages;

277
	if (target->state != TARGET_HALTED) {
oharboe's avatar
oharboe committed
278
279
280
281
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

282
283
284
285
286
287
	switch (pic32mx_info->dev_type) {
	case	MX_1xx_2xx:
	case	MX_17x_27x:
		config0_address = PIC32MX_DEVCFG0_1xx_2xx;
		break;
	default:
288
		config0_address = PIC32MX_DEVCFG0;
289
290
		break;
	}
291
292

	target_read_u32(target, config0_address, &devcfg0);
293

zwelch's avatar
zwelch committed
294
	if ((devcfg0 & (1 << 28)) == 0) /* code protect bit */
295
296
		num_pages = 0xffff;			/* All pages protected */
	else if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH) {
zwelch's avatar
zwelch committed
297
		if (devcfg0 & (1 << 24))
298
			num_pages = 0;			/* All pages unprotected */
oharboe's avatar
oharboe committed
299
		else
300
			num_pages = 0xffff;		/* All pages protected */
301
302
	} else {
		/* pgm flash */
303
304
305
306
307
308
309
310
		switch (pic32mx_info->dev_type) {
		case	MX_1xx_2xx:
			num_pages = (~devcfg0 >> 10) & 0x7f;
			break;
		case	MX_17x_27x:
			num_pages = (~devcfg0 >> 10) & 0x1ff;
			break;
		default:
311
			num_pages = (~devcfg0 >> 12) & 0xff;
312
313
			break;
		}
314
	}
315

oharboe's avatar
oharboe committed
316
317
318
319
320
321
322
323
	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;
}

324
static int pic32mx_erase(struct flash_bank *bank, int first, int last)
oharboe's avatar
oharboe committed
325
{
Zachary T Welch's avatar
Zachary T Welch committed
326
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
327
	int i;
328
	uint32_t status;
oharboe's avatar
oharboe committed
329

330
	if (bank->target->state != TARGET_HALTED) {
oharboe's avatar
oharboe committed
331
332
333
334
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

335
	if ((first == 0) && (last == (bank->num_sectors - 1))
336
		&& (Virt2Phys(bank->base) == PIC32MX_PHYS_PGM_FLASH)) {
337
338
		/* this will only erase the Program Flash (PFM), not the Boot Flash (BFM)
		 * we need to use the MTAP to perform a full erase */
339
		LOG_DEBUG("Erasing entire program flash");
oharboe's avatar
oharboe committed
340
		status = pic32mx_nvm_exec(bank, NVMCON_OP_PFM_ERASE, 50);
341
		if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
342
			return ERROR_FLASH_OPERATION_FAILED;
343
		if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
344
345
346
347
			return ERROR_FLASH_OPERATION_FAILED;
		return ERROR_OK;
	}

348
	for (i = first; i <= last; i++) {
349
		target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(bank->base + bank->sectors[i].offset));
oharboe's avatar
oharboe committed
350
351
352

		status = pic32mx_nvm_exec(bank, NVMCON_OP_PAGE_ERASE, 10);

353
		if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
354
			return ERROR_FLASH_OPERATION_FAILED;
355
		if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
356
357
358
359
360
361
362
			return ERROR_FLASH_OPERATION_FAILED;
		bank->sectors[i].is_erased = 1;
	}

	return ERROR_OK;
}

363
static int pic32mx_protect(struct flash_bank *bank, int set, int first, int last)
oharboe's avatar
oharboe committed
364
{
Zachary T Welch's avatar
Zachary T Welch committed
365
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
366

367
	if (target->state != TARGET_HALTED) {
oharboe's avatar
oharboe committed
368
369
370
371
372
373
374
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

	return ERROR_OK;
}

375
376
/* see contib/loaders/flash/pic32mx.s for src */

377
static uint32_t pic32mx_flash_write_code[] = {
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
					/* 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 */
433
	0x00000000,		/* nop */
434
435
436
437
438
439
440
441
442
443
	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 */
};

444
static int pic32mx_write_block(struct flash_bank *bank, const uint8_t *buffer,
445
		uint32_t offset, uint32_t count)
oharboe's avatar
oharboe committed
446
{
Zachary T Welch's avatar
Zachary T Welch committed
447
	struct target *target = bank->target;
448
	uint32_t buffer_size = 16384;
449
	struct working_area *write_algorithm;
450
	struct working_area *source;
451
	uint32_t address = bank->base + offset;
452
	struct reg_param reg_params[3];
453
	uint32_t row_size;
454
	int retval = ERROR_OK;
455

456
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
457
	struct mips32_algorithm mips32_info;
oharboe's avatar
oharboe committed
458
459

	/* flash write code */
460
	if (target_alloc_working_area(target, sizeof(pic32mx_flash_write_code),
461
			&write_algorithm) != ERROR_OK) {
oharboe's avatar
oharboe committed
462
463
		LOG_WARNING("no working area available, can't do block memory writes");
		return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
464
	}
oharboe's avatar
oharboe committed
465

466
	/* Change values for counters and row size, depending on variant */
467
468
469
	switch (pic32mx_info->dev_type) {
	case	MX_1xx_2xx:
	case	MX_17x_27x:
470
471
472
473
474
		/* 128 byte row */
		pic32mx_flash_write_code[8] = 0x2CD30020;
		pic32mx_flash_write_code[14] = 0x24840080;
		pic32mx_flash_write_code[15] = 0x24A50080;
		pic32mx_flash_write_code[17] = 0x24C6FFE0;
475
		row_size = 128;
476
477
		break;
	default:
478
479
480
481
482
		/* 512 byte row */
		pic32mx_flash_write_code[8] = 0x2CD30080;
		pic32mx_flash_write_code[14] = 0x24840200;
		pic32mx_flash_write_code[15] = 0x24A50200;
		pic32mx_flash_write_code[17] = 0x24C6FF80;
483
		row_size = 512;
484
		break;
485
486
	}

487
488
489
490
	uint8_t code[sizeof(pic32mx_flash_write_code)];
	target_buffer_set_u32_array(target, code, ARRAY_SIZE(pic32mx_flash_write_code),
			pic32mx_flash_write_code);
	retval = target_write_buffer(target, write_algorithm->address, sizeof(code), code);
491
	if (retval != ERROR_OK)
oharboe's avatar
oharboe committed
492
493
494
		return retval;

	/* memory buffer */
495
	while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
496
		buffer_size /= 2;
497
		if (buffer_size <= 256) {
498
			/* we already allocated the writing code, but failed to get a
499
			 * buffer, free the algorithm */
500
			target_free_working_area(target, write_algorithm);
oharboe's avatar
oharboe committed
501

502
503
504
			LOG_WARNING("no large enough working area available, can't do block memory writes");
			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
		}
505
	}
506
507
508

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

510
511
512
	init_reg_param(&reg_params[0], "r4", 32, PARAM_IN_OUT);
	init_reg_param(&reg_params[1], "r5", 32, PARAM_OUT);
	init_reg_param(&reg_params[2], "r6", 32, PARAM_OUT);
513

514
515
516
517
518
519
520
521
522
523
524
525
526
	int row_offset = offset % row_size;
	uint8_t *new_buffer = NULL;
	if (row_offset && (count >= (row_size / 4))) {
		new_buffer = malloc(buffer_size);
		if (new_buffer == NULL) {
			LOG_ERROR("Out of memory");
			return ERROR_FAIL;
		}
		memset(new_buffer,  0xff, row_offset);
		address -= row_offset;
	} else
		row_offset = 0;

527
	while (count > 0) {
528
		uint32_t status;
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
		uint32_t thisrun_count;

		if (row_offset) {
			thisrun_count = (count > ((buffer_size - row_offset) / 4)) ?
				((buffer_size - row_offset) / 4) : count;

			memcpy(new_buffer + row_offset, buffer, thisrun_count * 4);

			retval = target_write_buffer(target, source->address,
				row_offset + thisrun_count * 4, new_buffer);
			if (retval != ERROR_OK)
				break;
		} else {
			thisrun_count = (count > (buffer_size / 4)) ?
					(buffer_size / 4) : count;

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

551
552
		buf_set_u32(reg_params[0].value, 0, 32, Virt2Phys(source->address));
		buf_set_u32(reg_params[1].value, 0, 32, Virt2Phys(address));
553
		buf_set_u32(reg_params[2].value, 0, 32, thisrun_count + row_offset / 4);
oharboe's avatar
oharboe committed
554

555
		retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
556
				write_algorithm->address,
557
558
				0, 10000, &mips32_info);
		if (retval != ERROR_OK) {
oharboe's avatar
oharboe committed
559
560
561
562
563
			LOG_ERROR("error executing pic32mx flash write algorithm");
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}

564
565
		status = buf_get_u32(reg_params[0].value, 0, 32);

566
		if (status & NVMCON_NVMERR) {
zwelch's avatar
zwelch committed
567
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
568
569
570
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}
571

572
		if (status & NVMCON_LVDERR) {
zwelch's avatar
zwelch committed
573
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
574
575
576
			retval = ERROR_FLASH_OPERATION_FAILED;
			break;
		}
oharboe's avatar
oharboe committed
577

578
579
580
		buffer += thisrun_count * 4;
		address += thisrun_count * 4;
		count -= thisrun_count;
581
582
583
584
		if (row_offset) {
			address += row_offset;
			row_offset = 0;
		}
oharboe's avatar
oharboe committed
585
586
587
	}

	target_free_working_area(target, source);
588
	target_free_working_area(target, write_algorithm);
oharboe's avatar
oharboe committed
589

590
591
592
	destroy_reg_param(&reg_params[0]);
	destroy_reg_param(&reg_params[1]);
	destroy_reg_param(&reg_params[2]);
oharboe's avatar
oharboe committed
593

594
595
	if (new_buffer != NULL)
		free(new_buffer);
oharboe's avatar
oharboe committed
596
597
598
	return retval;
}

599
static int pic32mx_write_word(struct flash_bank *bank, uint32_t address, uint32_t word)
oharboe's avatar
oharboe committed
600
{
Zachary T Welch's avatar
Zachary T Welch committed
601
	struct target *target = bank->target;
oharboe's avatar
oharboe committed
602

603
	target_write_u32(target, PIC32MX_NVMADDR, Virt2Phys(address));
oharboe's avatar
oharboe committed
604
605
606
607
608
	target_write_u32(target, PIC32MX_NVMDATA, word);

	return pic32mx_nvm_exec(bank, NVMCON_OP_WORD_PROG, 5);
}

609
static int pic32mx_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
610
{
611
612
613
614
615
	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;
616
	int retval;
oharboe's avatar
oharboe committed
617

618
	if (bank->target->state != TARGET_HALTED) {
oharboe's avatar
oharboe committed
619
620
621
622
		LOG_ERROR("Target not halted");
		return ERROR_TARGET_NOT_HALTED;
	}

623
	LOG_DEBUG("writing to flash at address " TARGET_ADDR_FMT " at offset 0x%8.8" PRIx32
624
625
			" count: 0x%8.8" PRIx32 "", bank->base, offset, count);

626
	if (offset & 0x3) {
duane's avatar
duane committed
627
		LOG_WARNING("offset 0x%" PRIx32 "breaks required 4-byte alignment", offset);
oharboe's avatar
oharboe committed
628
629
630
631
		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
	}

	/* multiple words (4-byte) to be programmed? */
632
	if (words_remaining > 0) {
oharboe's avatar
oharboe committed
633
		/* try using a block write */
634
635
636
		retval = pic32mx_write_block(bank, buffer, offset, words_remaining);
		if (retval != ERROR_OK) {
			if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
oharboe's avatar
oharboe committed
637
638
639
				/* 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");
640
			} else if (retval == ERROR_FLASH_OPERATION_FAILED) {
641
642
				LOG_ERROR("flash writing failed");
				return retval;
oharboe's avatar
oharboe committed
643
			}
644
		} else {
oharboe's avatar
oharboe committed
645
646
647
648
649
650
			buffer += words_remaining * 4;
			address += words_remaining * 4;
			words_remaining = 0;
		}
	}

651
	while (words_remaining > 0) {
652
653
		uint32_t value;
		memcpy(&value, buffer + bytes_written, sizeof(uint32_t));
oharboe's avatar
oharboe committed
654

655
		status = pic32mx_write_word(bank, address, value);
656

657
		if (status & NVMCON_NVMERR) {
658
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
659
			return ERROR_FLASH_OPERATION_FAILED;
660
661
		}

662
		if (status & NVMCON_LVDERR) {
663
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
664
			return ERROR_FLASH_OPERATION_FAILED;
665
		}
oharboe's avatar
oharboe committed
666
667
668
669
670
671

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

672
	if (bytes_remaining) {
673
		uint32_t value = 0xffffffff;
674
		memcpy(&value, buffer + bytes_written, bytes_remaining);
oharboe's avatar
oharboe committed
675

676
		status = pic32mx_write_word(bank, address, value);
677

678
		if (status & NVMCON_NVMERR) {
679
			LOG_ERROR("Flash write error NVMERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
680
			return ERROR_FLASH_OPERATION_FAILED;
681
682
		}

683
		if (status & NVMCON_LVDERR) {
684
			LOG_ERROR("Flash write error LVDERR (status = 0x%08" PRIx32 ")", status);
oharboe's avatar
oharboe committed
685
			return ERROR_FLASH_OPERATION_FAILED;
686
		}
oharboe's avatar
oharboe committed
687
688
689
690
691
	}

	return ERROR_OK;
}

692
static int pic32mx_probe(struct flash_bank *bank)
oharboe's avatar
oharboe committed
693
{
Zachary T Welch's avatar
Zachary T Welch committed
694
	struct target *target = bank->target;
695
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
696
	struct mips32_common *mips32 = target->arch_info;
697
	struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
oharboe's avatar
oharboe committed
698
	int i;
699
	uint32_t num_pages = 0;
700
	uint32_t device_id;
oharboe's avatar
oharboe committed
701
702
703
704
705
	int page_size;

	pic32mx_info->probed = 0;

	device_id = ejtag_info->idcode;
706
	LOG_INFO("device id = 0x%08" PRIx32 " (manuf 0x%03x dev 0x%04x, ver 0x%02x)",
duane's avatar
duane committed
707
			  device_id,
708
			  (unsigned)((device_id >> 1) & 0x7ff),
709
			  (unsigned)((device_id >> 12) & 0xffff),
710
			  (unsigned)((device_id >> 28) & 0xf));
oharboe's avatar
oharboe committed
711

712
	if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
713
		LOG_WARNING("Cannot identify target as a PIC32MX family.");
oharboe's avatar
oharboe committed
714
715
716
		return ERROR_FLASH_OPERATION_FAILED;
	}

717
718
719
	/* Check for PIC32mx1xx/2xx */
	for (i = 0; pic32mx_devs[i].name != NULL; i++) {
		if (pic32mx_devs[i].devid == (device_id & 0x0fffffff)) {
720
721
			if ((pic32mx_devs[i].name[0] == '1') || (pic32mx_devs[i].name[0] == '2'))
				pic32mx_info->dev_type = (pic32mx_devs[i].name[1] == '7') ? MX_17x_27x : MX_1xx_2xx;
722
723
724
725
			break;
		}
	}

726
727
728
	switch (pic32mx_info->dev_type) {
	case	MX_1xx_2xx:
	case	MX_17x_27x:
729
		page_size = 1024;
730
731
		break;
	default:
732
		page_size = 4096;
733
734
		break;
	}
oharboe's avatar
oharboe committed
735

736
	if (Virt2Phys(bank->base) == PIC32MX_PHYS_BOOT_FLASH) {
737
738
739
740
741
742
743
744
745
746
747
		/* 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 */
748
749
750
		switch (pic32mx_info->dev_type) {
		case	MX_1xx_2xx:
		case	MX_17x_27x:
751
			num_pages = (3 * 1024);
752
753
			break;
		default:
754
			num_pages = (12 * 1024);
755
756
			break;
		}
757
#endif
758
	} else {
759
760
		/* read the flash size from the device */
		if (target_read_u32(target, PIC32MX_BMXPFMSZ, &num_pages) != ERROR_OK) {
761
762
763
			switch (pic32mx_info->dev_type) {
			case	MX_1xx_2xx:
			case	MX_17x_27x:
764
765
				LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 32k flash");
				num_pages = (32 * 1024);
766
767
				break;
			default:
768
769
				LOG_WARNING("PIC32MX flash size failed, probe inaccurate - assuming 512k flash");
				num_pages = (512 * 1024);
770
				break;
771
			}
772
		}
oharboe's avatar
oharboe committed
773
774
	}

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

777
	if (bank->sectors) {
778
779
780
781
		free(bank->sectors);
		bank->sectors = NULL;
	}

oharboe's avatar
oharboe committed
782
	/* calculate numbers of pages */
783
	num_pages /= page_size;
oharboe's avatar
oharboe committed
784
785
	bank->size = (num_pages * page_size);
	bank->num_sectors = num_pages;
786
	bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
oharboe's avatar
oharboe committed
787

788
	for (i = 0; i < (int)num_pages; i++) {
oharboe's avatar
oharboe committed
789
790
791
792
793
794
795
796
797
798
799
		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;
}

800
static int pic32mx_auto_probe(struct flash_bank *bank)
oharboe's avatar
oharboe committed
801
{
802
	struct pic32mx_flash_bank *pic32mx_info = bank->driver_priv;
oharboe's avatar
oharboe committed
803
804
805
806
807
	if (pic32mx_info->probed)
		return ERROR_OK;
	return pic32mx_probe(bank);
}

808
static int pic32mx_info(struct flash_bank *bank, char *buf, int buf_size)
oharboe's avatar
oharboe committed
809
{
Zachary T Welch's avatar
Zachary T Welch committed
810
	struct target *target = bank->target;
811
	struct mips32_common *mips32 = target->arch_info;
812
	struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
813
	uint32_t device_id;
814
	int printed = 0, i;
oharboe's avatar
oharboe committed
815
816
817

	device_id = ejtag_info->idcode;

818
	if (((device_id >> 1) & 0x7ff) != PIC32MX_MANUF_ID) {
819
820
		snprintf(buf, buf_size,
				 "Cannot identify target as a PIC32MX family (manufacturer 0x%03d != 0x%03d)\n",
821
				 (unsigned)((device_id >> 1) & 0x7ff),
duane's avatar
duane committed
822
				 PIC32MX_MANUF_ID);
oharboe's avatar
oharboe committed
823
824
		return ERROR_FLASH_OPERATION_FAILED;
	}
825

826
	for (i = 0; pic32mx_devs[i].name != NULL; i++) {
827
		if (pic32mx_devs[i].devid == (device_id & 0x0fffffff)) {
oharboe's avatar
oharboe committed
828
829
830
831
832
			printed = snprintf(buf, buf_size, "PIC32MX%s", pic32mx_devs[i].name);
			break;
		}
	}

833
	if (pic32mx_devs[i].name == NULL)
834
		printed = snprintf(buf, buf_size, "Unknown");
oharboe's avatar
oharboe committed
835

836
837
	buf += printed;
	buf_size -= printed;
838
	snprintf(buf, buf_size, " Ver: 0x%02x",
839
			(unsigned)((device_id >> 28) & 0xf));
oharboe's avatar
oharboe committed
840
841
842
843

	return ERROR_OK;
}

844
COMMAND_HANDLER(pic32mx_handle_pgm_word_command)
oharboe's avatar
oharboe committed
845
{
846
	uint32_t address, value;
oharboe's avatar
oharboe committed
847
848
	int status, res;

849
	if (CMD_ARGC != 3)
Mathias K's avatar
Mathias K committed
850
		return ERROR_COMMAND_SYNTAX_ERROR;
oharboe's avatar
oharboe committed
851

852
853
	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
	COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
854

855
	struct flash_bank *bank;
856
	int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 2, &bank);
857
858
	if (ERROR_OK != retval)
		return retval;
oharboe's avatar
oharboe committed
859

860
	if (address < bank->base || address >= (bank->base + bank->size)) {
861
		command_print(CMD_CTX, "flash address '%s' is out of bounds", CMD_ARGV[0]);
oharboe's avatar
oharboe committed
862
863
864
865
866
		return ERROR_OK;
	}

	res = ERROR_OK;
	status = pic32mx_write_word(bank, address, value);
867
	if (status & NVMCON_NVMERR)
oharboe's avatar
oharboe committed
868
		res = ERROR_FLASH_OPERATION_FAILED;
869
	if (status & NVMCON_LVDERR)
oharboe's avatar
oharboe committed
870
871
872
		res = ERROR_FLASH_OPERATION_FAILED;

	if (res == ERROR_OK)
873
		command_print(CMD_CTX, "pic32mx pgm word complete");
oharboe's avatar
oharboe committed
874
	else
875
		command_print(CMD_CTX, "pic32mx pgm word failed (status = 0x%x)", status);
oharboe's avatar
oharboe committed
876
877
878

	return ERROR_OK;
}
879

Spencer Oliver's avatar
Spencer Oliver committed
880
881
882
883
884
885
886
COMMAND_HANDLER(pic32mx_handle_unlock_command)
{
	struct target *target = NULL;
	struct mips_m4k_common *mips_m4k;
	struct mips_ejtag *ejtag_info;
	int timeout = 10;

887
	if (CMD_ARGC < 1) {
Spencer Oliver's avatar
Spencer Oliver committed
888
		command_print(CMD_CTX, "pic32mx unlock <bank>");
Mathias K's avatar
Mathias K committed
889
		return ERROR_COMMAND_SYNTAX_ERROR;
Spencer Oliver's avatar
Spencer Oliver committed
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
	}

	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 */
906
	uint8_t mchip_cmd = MCHP_STATUS;
Spencer Oliver's avatar
Spencer Oliver committed
907
	mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
908
	if (mchip_cmd & (1 << 7)) {
Spencer Oliver's avatar
Spencer Oliver committed
909
910
911
912
913
		/* device is not locked */
		command_print(CMD_CTX, "pic32mx is already unlocked, erasing anyway");
	}

	/* unlock/erase device */
914
	mips_ejtag_drscan_8_out(ejtag_info, MCHP_ASERT_RST);
915
	jtag_add_sleep(200);
Spencer Oliver's avatar
Spencer Oliver committed
916

917
	mips_ejtag_drscan_8_out(ejtag_info, MCHP_ERASE);
Spencer Oliver's avatar
Spencer Oliver committed
918
919
920
921

	do {
		mchip_cmd = MCHP_STATUS;
		mips_ejtag_drscan_8(ejtag_info, &mchip_cmd);
922
		if (timeout-- == 0) {
Spencer Oliver's avatar
Spencer Oliver committed
923
924
925
926
927
928
			LOG_DEBUG("timeout waiting for unlock: 0x%" PRIx32 "", mchip_cmd);
			break;
		}
		alive_sleep(1);
	} while ((mchip_cmd & (1 << 2)) || (!(mchip_cmd & (1 << 3))));

929
	mips_ejtag_drscan_8_out(ejtag_info, MCHP_DE_ASSERT_RST);
Spencer Oliver's avatar
Spencer Oliver committed
930
931
932
933
934
935
936
937
938
939
940

	/* 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;
}

941
942
943
static const struct command_registration pic32mx_exec_command_handlers[] = {
	{
		.name = "pgm_word",
Mathias K's avatar
Mathias K committed
944
		.usage = "<addr> <value> <bank>",
David Brownell's avatar
David Brownell committed
945
		.handler = pic32mx_handle_pgm_word_command,
946
947
948
		.mode = COMMAND_EXEC,
		.help = "program a word",
	},
Spencer Oliver's avatar
Spencer Oliver committed
949
950
951
952
953
954
955
	{
		.name = "unlock",
		.handler = pic32mx_handle_unlock_command,
		.mode = COMMAND_EXEC,
		.usage = "[bank_id]",
		.help = "Unlock/Erase entire device.",
	},
956
957
	COMMAND_REGISTRATION_DONE
};
958

959
960
961
962
963
static const struct command_registration pic32mx_command_handlers[] = {
	{
		.name = "pic32mx",
		.mode = COMMAND_ANY,
		.help = "pic32mx flash command group",
Spencer Oliver's avatar
Spencer Oliver committed
964
		.usage = "",
965
966
967
968
		.chain = pic32mx_exec_command_handlers,
	},
	COMMAND_REGISTRATION_DONE
};
969

970
struct flash_driver pic32mx_flash = {
David Brownell's avatar
David Brownell committed
971
972
973
974
975
976
	.name = "pic32mx",
	.commands = pic32mx_command_handlers,
	.flash_bank_command = pic32mx_flash_bank_command,
	.erase = pic32mx_erase,
	.protect = pic32mx_protect,
	.write = pic32mx_write,
977
	.read = default_flash_read,
David Brownell's avatar
David Brownell committed
978
979
	.probe = pic32mx_probe,
	.auto_probe = pic32mx_auto_probe,
980
	.erase_check = default_flash_blank_check,
David Brownell's avatar
David Brownell committed
981
982
	.protect_check = pic32mx_protect_check,
	.info = pic32mx_info,
983
	.free_driver_priv = default_flash_free_driver_priv,
David Brownell's avatar
David Brownell committed
984
};