-
Notifications
You must be signed in to change notification settings - Fork 18
/
eefc.c
349 lines (301 loc) · 9.24 KB
/
eefc.c
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
/*
* Copyright (c) 2015-2016, Atmel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include "chipid.h"
#include "comm.h"
#include "eefc.h"
#include "utils.h"
#define PAGE_SIZE 512
#define EEFC_FMR 0x00000000
#define EEFC_FCR 0x00000004
#define EEFC_FSR 0x00000008
#define EEFC_FRR 0x0000000c
#define EEFC_FCR_FKEY (0x5a << 24)
#define EEFC_FCR_FCMD_GETD 0x00 // Get Flash descriptor
#define EEFC_FCR_FCMD_WP 0x01 // Write page
#define EEFC_FCR_FCMD_WPL 0x02 // Write page and lock
#define EEFC_FCR_FCMD_EWP 0x03 // Erase page and write page
#define EEFC_FCR_FCMD_EWPL 0x04 // Erase page and write page then lock
#define EEFC_FCR_FCMD_EA 0x05 // Erase all
#define EEFC_FCR_FCMD_EPA 0x07 // Erase pages
#define EEFC_FCR_FCMD_SLB 0x08 // Set lock bit
#define EEFC_FCR_FCMD_CLB 0x09 // Clear lock bit
#define EEFC_FCR_FCMD_GLB 0x0A // Get lock bit
#define EEFC_FCR_FCMD_SGPB 0x0B // Set GPNVM bit
#define EEFC_FCR_FCMD_CGPB 0x0C // Clear GPNVM bit
#define EEFC_FCR_FCMD_GGPB 0x0D // Get GPNVM bit
#define EEFC_FSR_FRDY (1 << 0)
#define EEFC_FSR_CMDE (1 << 1)
#define EEFC_FSR_FLOCKE (1 << 2)
#define EEFC_FSR_FLERR (1 << 3)
static bool eefc_wait_ready(int fd, const struct _chip* chip, uint32_t* status)
{
uint32_t value;
do {
if (!samba_read_word(fd, chip->eefc_base + EEFC_FSR, &value))
return false;
} while (!(value & EEFC_FSR_FRDY));
if (status)
*status = value;
return true;
}
static bool eefc_read_result(int fd, const struct _chip* chip, uint32_t* result)
{
return samba_read_word(fd, chip->eefc_base + EEFC_FRR, result);
}
static bool eefc_send_command(int fd, const struct _chip* chip, uint8_t cmd,
uint16_t arg, uint32_t* status)
{
if (!samba_write_word(fd, chip->eefc_base + EEFC_FCR,
EEFC_FCR_FKEY | (arg << 8) | cmd))
return false;
if (!eefc_wait_ready(fd, chip, status))
return false;
return true;
}
bool eefc_read_flash_info(int fd, const struct _chip* chip,
struct _eefc_locks* locks)
{
// send GETD command
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_GETD, 0, NULL))
return false;
// flash ID (discarded)
uint32_t flash_id;
if (!eefc_read_result(fd, chip, &flash_id))
return false;
// flash size
uint32_t flash_size;
if (!eefc_read_result(fd, chip, &flash_size))
return false;
if (flash_size != chip->flash_size * 1024) {
fprintf(stderr, "Invalid flash size: detected %d bytes but expected %d bytes\n",
flash_size, chip->flash_size * 1024);
return false;
}
// page size
uint32_t page_size;
if (!eefc_read_result(fd, chip, &page_size))
return false;
if (page_size != PAGE_SIZE) {
fprintf(stderr, "Invalid page size: detected %d bytes but expected %d bytes\n",
page_size, PAGE_SIZE);
return false;
}
// number of planes and plane sizes (discarded)
uint32_t nb_planes, plane_size;
if (!eefc_read_result(fd, chip, &nb_planes))
return false;
for (int i = 0; i < nb_planes; i++)
if (!eefc_read_result(fd, chip, &plane_size))
return false;
// number of locks and lock sizes
if (!eefc_read_result(fd, chip, &locks->count))
return false;
if (locks->count > MAX_EEFC_LOCKS)
return false;
for (int i = 0; i < locks->count; i++)
if (!eefc_read_result(fd, chip, &locks->size[i]))
return false;
return true;
}
static bool set_page_lock(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t lock, bool enable)
{
if (lock > locks->count)
return false;
uint8_t cmd = enable ? EEFC_FCR_FCMD_SLB : EEFC_FCR_FCMD_CLB;
return eefc_send_command(fd, chip, cmd, lock, NULL);
}
bool eefc_lock_page(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t lock)
{
return set_page_lock(fd, chip, locks, lock, true);
}
bool eefc_unlock_page(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t lock)
{
return set_page_lock(fd, chip, locks, lock, false);
}
static bool set_lock(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t addr, uint32_t size,
bool enable)
{
if (addr + size > chip->flash_size * 1024)
return false;
uint32_t addr_end = addr + size;
uint32_t offset = 0;
for (int lock = 0; lock < locks->count; lock++) {
uint32_t next_offset = offset + locks->size[lock];
if (addr >= offset && addr < next_offset) {
if (!set_page_lock(fd, chip, locks, lock, enable))
return false;
addr = next_offset;
if (addr >= addr_end)
return true;
}
offset = next_offset;
}
return false;
}
bool eefc_lock(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t addr, uint32_t size)
{
return set_lock(fd, chip, locks, addr, size, true);
}
bool eefc_unlock(int fd, const struct _chip* chip,
const struct _eefc_locks* locks, uint32_t addr, uint32_t size)
{
return set_lock(fd, chip, locks, addr, size, false);
}
bool eefc_erase_all(int fd, const struct _chip* chip)
{
// send erase all command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_EA, 0, &status))
return false;
if (status & EEFC_FSR_FLOCKE) {
fprintf(stderr, "Erase error: at least one page is locked\n");
return false;
}
if (status & EEFC_FSR_FLERR) {
fprintf(stderr, "Erase error: flash error\n");
return false;
}
return true;
}
bool eefc_erase_16pages(int fd, const struct _chip* chip,
uint32_t first_page)
{
uint32_t arg;
if (first_page & 15) {
fprintf(stderr, "Erase pages error: first page must be multiple of 16\n");
return false;
}
arg = (first_page & ~15) | 2;
// send erase pages command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_EPA, arg, &status))
return false;
if (status & EEFC_FSR_FLOCKE) {
fprintf(stderr, "Erase pages error: at least one page is locked\n");
return false;
}
if (status & EEFC_FSR_FLERR) {
fprintf(stderr, "Erase pages error: flash error\n");
return false;
}
return true;
}
bool eefc_read(int fd, const struct _chip* chip,
uint8_t* buffer, uint32_t addr, uint32_t size)
{
if (addr + size > chip->flash_size * 1024)
return false;
return samba_read(fd, buffer, chip->flash_addr + addr, size);
}
bool eefc_write(int fd, const struct _chip* chip,
uint8_t* buffer, uint32_t addr, uint32_t size)
{
if (addr + size > chip->flash_size * 1024)
return false;
while (size > 0) {
uint16_t page = addr / PAGE_SIZE;
uint32_t head = addr & (PAGE_SIZE - 1);
uint32_t count = MIN(size, PAGE_SIZE - head);
// write to latch buffer
// we cannot use the SAM-BA Monitor send command because it
// does byte writes and the flash controller needs word writes
uint32_t* wbuffer = (uint32_t*)buffer;
for (uint32_t i = 0; i < (count + 3) / 4; i++)
if (!samba_write_word(fd, chip->flash_addr + addr + i * 4, wbuffer[i]))
return false;
// send write command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_WP, page, &status))
return false;
if (status & EEFC_FSR_FLOCKE) {
fprintf(stderr, "Write error on page %d: page locked\n", page);
return false;
}
if (status & EEFC_FSR_FLERR) {
fprintf(stderr, "Write error on page %d: flash error\n", page);
return false;
}
buffer += count;
addr += count;
size -= count;
}
return true;
}
extern bool eefc_get_gpnvm(int fd, const struct _chip* chip,
uint8_t gpnvm, bool* value)
{
if (gpnvm >= chip->gpnvm) {
fprintf(stderr, "Get GPNVM%d error: invalid GPNVM\n", gpnvm);
return false;
}
// send Get GPNVM command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_GGPB, gpnvm, &status))
return false;
if (status & EEFC_FSR_CMDE) {
fprintf(stderr, "Get GPNVM%d error: command error\n", gpnvm);
return false;
}
uint32_t bits;
if (!eefc_read_result(fd, chip, &bits))
return false;
*value = bits & (1 << gpnvm);
return true;
}
extern bool eefc_set_gpnvm(int fd, const struct _chip* chip, uint8_t gpnvm)
{
if (gpnvm >= chip->gpnvm) {
fprintf(stderr, "Set GPNVM%d error: invalid GPNVM\n", gpnvm);
return false;
}
// send Set GPNVM command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_SGPB, gpnvm, &status))
return false;
if (status & EEFC_FSR_CMDE) {
fprintf(stderr, "Set GPNVM%d error: command error\n", gpnvm);
return false;
}
if (status & EEFC_FSR_FLERR) {
fprintf(stderr, "Set GPNVM%d error: flash error\n", gpnvm);
return false;
}
return true;
}
extern bool eefc_clear_gpnvm(int fd, const struct _chip* chip, uint8_t gpnvm)
{
if (gpnvm >= chip->gpnvm) {
fprintf(stderr, "Clear GPNVM%d error: invalid GPNVM\n", gpnvm);
return false;
}
// send Clear GPNVM command to flash controller
uint32_t status;
if (!eefc_send_command(fd, chip, EEFC_FCR_FCMD_CGPB, gpnvm, &status))
return false;
if (status & EEFC_FSR_CMDE) {
fprintf(stderr, "Clear GPNVM%d error: command error\n", gpnvm);
return false;
}
if (status & EEFC_FSR_FLERR) {
fprintf(stderr, "Clear GPNVM%d error: flash error\n", gpnvm);
return false;
}
return true;
}