aboutsummaryrefslogtreecommitdiff
path: root/src/nxp_tgt.c
blob: a22eda81c8bf712841e7276db10e592adf5d3d2e (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
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#include "general.h"
#include "adiv5.h"
#include "target.h"

#define IAP_PGM_CHUNKSIZE	256	/* should fit in RAM on any device */

struct flash_param {
	uint16_t	opcodes[2];	/* two opcodes to return to after calling the ROM */
	uint32_t	command[5];	/* command operands */
	uint32_t	result[4];	/* result data */
};

struct flash_program {
	struct flash_param	p;
	uint8_t			data[IAP_PGM_CHUNKSIZE];
};

static struct flash_program flash_pgm;

#define MSP				17												// Main stack pointer register number
#define MIN_RAM_SIZE_FOR_LPC1xxx			2048
#define RAM_USAGE_FOR_IAP_ROUTINES			32							// IAP routines use 32 bytes at top of ram

#define IAP_ENTRYPOINT	0x1fff1ff1
#define IAP_RAM_BASE	0x10000000


#define IAP_CMD_PREPARE		50
#define IAP_CMD_PROGRAM		51
#define IAP_CMD_ERASE		52
#define IAP_CMD_BLANKCHECK	53

#define IAP_STATUS_CMD_SUCCESS		0
#define IAP_STATUS_INVALID_COMMAND	1
#define IAP_STATUS_SRC_ADDR_ERROR	2
#define IAP_STATUS_DST_ADDR_ERROR	3
#define IAP_STATUS_SRC_ADDR_NOT_MAPPED	4
#define IAP_STATUS_DST_ADDR_NOT_MAPPED	5
#define IAP_STATUS_COUNT_ERROR		6
#define IAP_STATUS_INVALID_SECTOR	7
#define IAP_STATUS_SECTOR_NOT_BLANK	8
#define IAP_STATUS_SECTOR_NOT_PREPARED	9
#define IAP_STATUS_COMPARE_ERROR	10
#define IAP_STATUS_BUSY			11

static void lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len);
static int lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len);
static int lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len);
static int lpc11xx_flash_write(struct target_s *target, uint32_t dest, const uint8_t *src, 
			  int len);

/* 
 * Note that this memory map is actually for the largest of the lpc11xx devices;
 * There seems to be no good way to decode the part number to determine the RAM
 * and flash sizes.
 */
static const char lpc11xx_xml_memory_map[] = "<?xml version=\"1.0\"?>"
/*	"<!DOCTYPE memory-map "
	"             PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
	"                    \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
	"<memory-map>"
	"  <memory type=\"flash\" start=\"0x00000000\" length=\"0x8000\">"
	"    <property name=\"blocksize\">0x1000</property>"
	"  </memory>"
	"  <memory type=\"ram\" start=\"0x10000000\" length=\"0x2000\"/>"
	"</memory-map>";


bool
lpc11xx_probe(struct target_s *target)
{
	uint32_t idcode;

	/* read the device ID register */
	idcode = adiv5_ap_mem_read(adiv5_target_ap(target), 0x400483F4);

	switch (idcode) {

	case 0x041E502B:
	case 0x2516D02B:
	case 0x0416502B:
	case 0x2516902B:	/* lpc1111 */
	case 0x2524D02B:
	case 0x0425502B:
	case 0x2524902B:
	case 0x1421102B:	/* lpc1112 */
	case 0x0434502B:
	case 0x2532902B:
	case 0x0434102B:
	case 0x2532102B:	/* lpc1113 */
	case 0x0444502B:
	case 0x2540902B:
	case 0x0444102B:
	case 0x2540102B:
	case 0x1440102B:	/* lpc1114 */
	case 0x1431102B:	/* lpc11c22 */
	case 0x1430102B:	/* lpc11c24 */
		target->driver = "lpc11xx";
		target->xml_mem_map = lpc11xx_xml_memory_map;
		target->flash_erase = lpc11xx_flash_erase;
		target->flash_write = lpc11xx_flash_write;

		return true;
	}

	return false;
}

static void
lpc11x_iap_call(struct target_s *target, struct flash_param *param, unsigned param_len)
{
	uint32_t regs[target->regs_size];

	/* fill out the remainder of the parameters and copy the structure to RAM */
	param->opcodes[0] = 0xbe00;
	param->opcodes[1] = 0x0000;
	target_mem_write_words(target, IAP_RAM_BASE, (void *)param, param_len);

	/* set up for the call to the IAP ROM */
	target_regs_read(target, regs);
	regs[0] = IAP_RAM_BASE + offsetof(struct flash_param, command);
	regs[1] = IAP_RAM_BASE + offsetof(struct flash_param, result);

	regs[MSP] = IAP_RAM_BASE + MIN_RAM_SIZE_FOR_LPC1xxx - RAM_USAGE_FOR_IAP_ROUTINES;// stack pointer - top of the smallest ram less 32 for IAP usage
	regs[14] = IAP_RAM_BASE | 1;
	regs[15] = IAP_ENTRYPOINT;
	target_regs_write(target, regs);

	/* start the target and wait for it to halt again */
	target_halt_resume(target, 0);
	while (!target_halt_wait(target));

	/* copy back just the parameters structure */
	target_mem_read_words(target, (void *)param, IAP_RAM_BASE, sizeof(struct flash_param));
}

static int
lpc11xx_flash_prepare(struct target_s *target, uint32_t addr, int len)
{	
	/* prepare the sector(s) to be erased */
	memset(&flash_pgm.p, 0, sizeof(flash_pgm.p));
	flash_pgm.p.command[0] = IAP_CMD_PREPARE;
	flash_pgm.p.command[1] = addr / 4096;
	flash_pgm.p.command[2] = (addr + len - 1) / 4096;

	lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
	if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
		return -1;
	}

	return 0;
}

static int
lpc11xx_flash_erase(struct target_s *target, uint32_t addr, int len)
{

	if (addr % 4096)
		return -1;

	/* prepare... */
	if (lpc11xx_flash_prepare(target, addr, len))
		return -1;

	/* and now erase them */
	flash_pgm.p.command[0] = IAP_CMD_ERASE;
	flash_pgm.p.command[1] = addr / 4096;
	flash_pgm.p.command[2] = (addr + len - 1) / 4096;
	flash_pgm.p.command[3] = 12000;	/* XXX safe to assume this? */
	lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
	if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
		return -1;
	}
	flash_pgm.p.command[0] = IAP_CMD_BLANKCHECK;
	lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm.p));
	if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
		return -1;
	}

	return 0;
}

static int
lpc11xx_flash_write(struct target_s *target, uint32_t dest, const uint8_t *src, int len)
{
	unsigned first_chunk = dest / IAP_PGM_CHUNKSIZE;
	unsigned last_chunk = (dest + len - 1) / IAP_PGM_CHUNKSIZE;
	unsigned chunk_offset = dest % IAP_PGM_CHUNKSIZE;
	unsigned chunk;

	for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
		
		DEBUG("chunk %u len %d\n", chunk, len);
		/* first and last chunk may require special handling */
		if ((chunk == first_chunk) || (chunk == last_chunk)) {

			/* fill with all ff to avoid sector rewrite corrupting other writes */
			memset(flash_pgm.data, 0xff, sizeof(flash_pgm.data));
		
			/* copy as much as fits */				
			int copylen = IAP_PGM_CHUNKSIZE - chunk_offset;
			if (copylen > len)
				copylen = len;
			memcpy(&flash_pgm.data[chunk_offset], src, copylen);

			/* update to suit */
			len -= copylen;
			src += copylen;
			chunk_offset = 0;

			/* if we are programming the vectors, calculate the magic number */
			if (chunk == 0) {
				uint32_t *w = (uint32_t *)(&flash_pgm.data[0]);
				uint32_t sum = 0;

				for (unsigned i = 0; i < 7; i++)
					sum += w[i];
				w[7] = 0 - sum;
			}

		} else {

			/* interior chunk, must be aligned and full-sized */
			memcpy(flash_pgm.data, src, IAP_PGM_CHUNKSIZE);
			len -= IAP_PGM_CHUNKSIZE;
			src += IAP_PGM_CHUNKSIZE;
		}

		/* prepare... */
		if (lpc11xx_flash_prepare(target, chunk * IAP_PGM_CHUNKSIZE, IAP_PGM_CHUNKSIZE))
			return -1;

		/* set the destination address and program */
		flash_pgm.p.command[0] = IAP_CMD_PROGRAM;
		flash_pgm.p.command[1] = chunk * IAP_PGM_CHUNKSIZE;
		flash_pgm.p.command[2] = IAP_RAM_BASE + offsetof(struct flash_program, data);
		flash_pgm.p.command[3] = IAP_PGM_CHUNKSIZE;
		flash_pgm.p.command[4] = 12000;	/* XXX safe to presume this? */
		lpc11x_iap_call(target, &flash_pgm.p, sizeof(flash_pgm));
		if (flash_pgm.p.result[0] != IAP_STATUS_CMD_SUCCESS) {
			return -1;
		}

	}

	return 0;
}