Root/
Source at commit 6a1f486 created 13 years 3 months ago. By Peter Zotov, Reworked directory structure. | |
---|---|
1 | /* |
2 | * JzBoot: an USB bootloader for JZ series of Ingenic(R) microprocessors. |
3 | * Copyright (C) 2010 Sergey Gridassov <grindars@gmail.com>, |
4 | * Peter Zotov <whitequark@whitequark.org> |
5 | * |
6 | * This program is free software: you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation, either version 3 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include <stdlib.h> |
21 | #include <errno.h> |
22 | #include <string.h> |
23 | #include <stdio.h> |
24 | #include <unistd.h> |
25 | #include <ctype.h> |
26 | |
27 | #include "ingenic.h" |
28 | #include "usbdev.h" |
29 | #include "debug.h" |
30 | #include "config.h" |
31 | |
32 | #define HANDLE ingenic_handle_t *handle = hndl |
33 | #define BUILDTYPE(cmdset, id) (((cmdset) << 16) | (0x##id & 0xFFFF)) |
34 | #define CPUID(id) ((id) & 0xFFFF) |
35 | #define CMDSET(id) (((id) & 0xFFFF0000) >> 16) |
36 | |
37 | #define CFGOPT(name, var, exp) { char *str = cfg_getenv(name); if(str == NULL) { debug(LEVEL_ERROR, "%s is not set\n", name); errno = EINVAL; return -1; }; int v = atoi(str); if(!(exp)) { debug(LEVEL_ERROR, "%s must be in %s\n", name, #exp); return -1; }; handle->cfg.var = v; } |
38 | |
39 | #define NOPT(name, var, exp) { char *str = cfg_getenv("NAND_" name); if(str == NULL) { debug(LEVEL_ERROR, "%s is not set\n", "NAND_" name); errno = EINVAL; return -1; }; int v = atoi(str); if(!(exp)) { debug(LEVEL_ERROR, "%s must be in %s\n", "NAND_" name, #exp); return -1; }; handle->nand.nand_##var = v; } |
40 | |
41 | #define CALLBACK(function, ...) if(handle->callbacks && handle->callbacks->function) handle->callbacks->function(__VA_ARGS__, handle->callbacks_data) |
42 | |
43 | typedef struct { |
44 | void *usb; |
45 | uint32_t type; |
46 | uint32_t total_sdram_size; |
47 | |
48 | const ingenic_callbacks_t *callbacks; |
49 | void *callbacks_data; |
50 | |
51 | firmware_config_t cfg; |
52 | nand_config_t nand; |
53 | } ingenic_handle_t; |
54 | |
55 | static const struct { |
56 | const char * const magic; |
57 | uint32_t id; |
58 | } magic_list[] = { |
59 | { "JZ4740V1", BUILDTYPE(CMDSET_SPL, 4740) }, |
60 | { "JZ4750V1", BUILDTYPE(CMDSET_SPL, 4750) }, |
61 | { "JZ4760V1", BUILDTYPE(CMDSET_SPL, 4760) }, |
62 | |
63 | { "Boot4740", BUILDTYPE(CMDSET_USBBOOT, 4740) }, |
64 | { "Boot4750", BUILDTYPE(CMDSET_USBBOOT, 4750) }, |
65 | { "Boot4760", BUILDTYPE(CMDSET_USBBOOT, 4760) }, |
66 | |
67 | { NULL, 0 } |
68 | }; |
69 | |
70 | static void hexdump(const void *data, size_t size) { |
71 | const unsigned char *bytes = data; |
72 | |
73 | for(int i = 0; i < size; i+= 16) { |
74 | debug(LEVEL_DEBUG, "%04X ", i); |
75 | |
76 | int chunk_size = size - i; |
77 | if(chunk_size > 16) |
78 | chunk_size = 16; |
79 | |
80 | for(int j = 0; j < chunk_size; j++) { |
81 | debug(LEVEL_DEBUG, "%02X ", bytes[i + j]); |
82 | |
83 | if(j == 7) |
84 | debug(LEVEL_DEBUG, " "); |
85 | } |
86 | |
87 | for(int j = 0; j < 16 - chunk_size; j++) { |
88 | debug(LEVEL_DEBUG, " "); |
89 | |
90 | if(j == 8) |
91 | debug(LEVEL_DEBUG, " "); |
92 | } |
93 | |
94 | debug(LEVEL_DEBUG, "|"); |
95 | |
96 | for(int j = 0; j < chunk_size; j++) { |
97 | debug(LEVEL_DEBUG, "%c", isprint(bytes[i + j]) ? bytes[i + j] : '.'); |
98 | } |
99 | |
100 | debug(LEVEL_DEBUG, "|\n"); |
101 | } |
102 | } |
103 | |
104 | static uint32_t ingenic_probe(void *usb_hndl) { |
105 | char magic[9]; |
106 | |
107 | if(usbdev_vendor(usb_hndl, USBDEV_FROMDEV, VR_GET_CPU_INFO, 0, 0, magic, 8) == -1) |
108 | return 0; |
109 | |
110 | magic[8] = 0; |
111 | |
112 | for(int i = 0; magic_list[i].magic != NULL; i++) |
113 | if(strcmp(magic_list[i].magic, magic) == 0) |
114 | return magic_list[i].id; |
115 | |
116 | debug(LEVEL_ERROR, "Unknown CPU: '%s'\n", magic); |
117 | errno = EINVAL; |
118 | return 0; |
119 | } |
120 | |
121 | void *ingenic_open(void *usb_hndl) { |
122 | uint32_t type = ingenic_probe(usb_hndl); |
123 | |
124 | if(type == 0) |
125 | return NULL; |
126 | |
127 | ingenic_handle_t *ret = malloc(sizeof(ingenic_handle_t)); |
128 | ret->usb = usb_hndl; |
129 | ret->type = type; |
130 | ret->callbacks = NULL; |
131 | |
132 | return ret; |
133 | } |
134 | |
135 | int ingenic_redetect(void *hndl) { |
136 | HANDLE; |
137 | |
138 | uint32_t type = ingenic_probe(handle->usb); |
139 | |
140 | if(type == 0) |
141 | return -1; |
142 | |
143 | uint32_t prev = handle->type; |
144 | |
145 | handle->type = type; |
146 | |
147 | if(CMDSET(prev) != CMDSET(type)) { |
148 | CALLBACK(cmdset_change, CMDSET(type)); |
149 | } |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | void ingenic_set_callbacks(void *hndl, const ingenic_callbacks_t *callbacks, void *arg) { |
155 | HANDLE; |
156 | |
157 | handle->callbacks = callbacks; |
158 | handle->callbacks_data = arg; |
159 | } |
160 | |
161 | int ingenic_cmdset(void *hndl) { |
162 | HANDLE; |
163 | |
164 | return CMDSET(handle->type); |
165 | } |
166 | |
167 | int ingenic_type(void *hndl) { |
168 | HANDLE; |
169 | |
170 | return CPUID(handle->type); |
171 | } |
172 | |
173 | void ingenic_close(void *hndl) { |
174 | HANDLE; |
175 | |
176 | free(handle); |
177 | } |
178 | |
179 | int ingenic_rebuild(void *hndl) { |
180 | HANDLE; |
181 | |
182 | handle->cfg.cpu_id = CPUID(handle->type); |
183 | |
184 | CFGOPT("EXTCLK", ext_clk, v <= 27 && v >= 12); |
185 | CFGOPT("CPUSPEED", cpu_speed, (v % 12) == 0); |
186 | handle->cfg.cpu_speed /= handle->cfg.ext_clk; |
187 | CFGOPT("PHMDIV", phm_div, v <= 32 && v >= 2); |
188 | CFGOPT("USEUART", use_uart, 1); |
189 | CFGOPT("BAUDRATE", baudrate, 1); |
190 | |
191 | CFGOPT("SDRAM_BUSWIDTH", bus_width, (v == 16) || (v == 32)); |
192 | handle->cfg.bus_width = handle->cfg.bus_width == 16 ? 1 : 0; |
193 | CFGOPT("SDRAM_BANKS", bank_num, (v >= 4) && ((v % 4) == 0)); |
194 | handle->cfg.bank_num /= 4; |
195 | CFGOPT("SDRAM_ROWADDR", row_addr, 1); |
196 | CFGOPT("SDRAM_COLADDR", col_addr, 1); |
197 | CFGOPT("SDRAM_ISMOBILE", is_mobile, v == 0 || v == 1); |
198 | CFGOPT("SDRAM_ISBUSSHARE", is_busshare, v == 0 || v == 1); |
199 | |
200 | memset(&handle->cfg.debug, 0, sizeof(ingenic_stage1_debug_t)); |
201 | |
202 | handle->total_sdram_size = (uint32_t) |
203 | (2 << (handle->cfg.row_addr + handle->cfg.col_addr - 1)) * 2 |
204 | * (handle->cfg.bank_num + 1) * 2 |
205 | * (2 - handle->cfg.bus_width); |
206 | |
207 | handle->nand.cpuid = CPUID(handle->type); |
208 | |
209 | NOPT("BUSWIDTH", bw, 1); |
210 | NOPT("ROWCYCLES", rc, 1); |
211 | NOPT("PAGESIZE", ps, 1); |
212 | NOPT("PAGEPERBLOCK", ppb, 1); |
213 | NOPT("FORCEERASE", force_erase, 1); |
214 | // FIXME: pn is not set by xburst-tools usbboot. Is this intended? |
215 | NOPT("OOBSIZE", os, 1); |
216 | NOPT("ECCPOS", eccpos, 1); |
217 | NOPT("BADBLOCKPOS", bbpos, 1); |
218 | NOPT("BADBLOCKPAGE", bbpage, 1); |
219 | NOPT("PLANENUM", plane, 1); |
220 | NOPT("BCHBIT", bchbit, 1); |
221 | NOPT("WPPIN", wppin, 1); |
222 | NOPT("BLOCKPERCHIP", bpc, 1); |
223 | |
224 | return 0; |
225 | } |
226 | |
227 | uint32_t ingenic_sdram_size(void *hndl) { |
228 | HANDLE; |
229 | |
230 | return handle->total_sdram_size; |
231 | } |
232 | |
233 | static int ingenic_wordop(void *usb, int op, uint32_t base) { |
234 | return usbdev_vendor(usb, USBDEV_TODEV, op, (base >> 16), base & 0xFFFF, 0, 0); |
235 | } |
236 | |
237 | int ingenic_stage1_debugop(void *hndl, const char *filename, uint32_t op, uint32_t pin, uint32_t base, uint32_t size) { |
238 | HANDLE; |
239 | |
240 | handle->cfg.debug.debug_ops = op; |
241 | handle->cfg.debug.pin_num = pin; |
242 | handle->cfg.debug.start = base; |
243 | handle->cfg.debug.size = size; |
244 | |
245 | int ret = ingenic_loadstage(handle, INGENIC_STAGE1, filename); |
246 | |
247 | memset(&handle->cfg.debug, 0, sizeof(ingenic_stage1_debug_t)); |
248 | |
249 | return ret; |
250 | } |
251 | |
252 | int ingenic_loadstage(void *hndl, int id, const char *file) { |
253 | HANDLE; |
254 | |
255 | if(file == NULL) { |
256 | errno = EINVAL; |
257 | |
258 | return -1; |
259 | } |
260 | |
261 | uint32_t base; |
262 | int cmd; |
263 | |
264 | switch(id) { |
265 | case INGENIC_STAGE1: |
266 | base = SDRAM_BASE + STAGE1_BASE; |
267 | cmd = VR_PROGRAM_START1; |
268 | |
269 | break; |
270 | |
271 | case INGENIC_STAGE2: |
272 | base = SDRAM_BASE + handle->total_sdram_size - STAGE2_CODESIZE; |
273 | cmd = VR_PROGRAM_START2; |
274 | |
275 | break; |
276 | |
277 | default: |
278 | errno = EINVAL; |
279 | |
280 | return -1; |
281 | } |
282 | |
283 | FILE *fd = fopen(file, "rb"); |
284 | |
285 | if(fd == NULL) |
286 | return -1; |
287 | |
288 | fseek(fd, 0, SEEK_END); |
289 | int size = ftell(fd); |
290 | fseek(fd, 0, SEEK_SET); |
291 | |
292 | void *data = malloc(size); |
293 | size_t read_bytes = fread(data, 1, size, fd); |
294 | |
295 | fclose(fd); |
296 | |
297 | if(read_bytes != size) { |
298 | free(data); |
299 | |
300 | errno = EIO; |
301 | |
302 | return -1; |
303 | } |
304 | |
305 | memcpy(data + 8, &handle->cfg, sizeof(firmware_config_t)); |
306 | |
307 | if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, base) == -1) { |
308 | free(data); |
309 | |
310 | return -1; |
311 | } |
312 | |
313 | int ret = usbdev_sendbulk(handle->usb, data, size); |
314 | |
315 | free(data); |
316 | |
317 | if(ret == -1) |
318 | return -1; |
319 | |
320 | if(id == INGENIC_STAGE2) { |
321 | ret = usbdev_vendor(handle->usb, USBDEV_TODEV, VR_FLUSH_CACHES, 0, 0, 0, 0); |
322 | |
323 | if(ret == -1) |
324 | return -1; |
325 | } |
326 | |
327 | ret = usbdev_vendor(handle->usb, USBDEV_TODEV, cmd, (base >> 16), base & 0xFFFF, 0, 0); |
328 | |
329 | if(ret == -1) |
330 | return -1; |
331 | |
332 | usleep(250); |
333 | |
334 | if(id == INGENIC_STAGE2) |
335 | return ingenic_redetect(hndl); |
336 | else |
337 | return 0; |
338 | } |
339 | |
340 | int ingenic_memtest(void *hndl, const char *filename, uint32_t base, uint32_t size, uint32_t *fail) { |
341 | HANDLE; |
342 | |
343 | int ret = ingenic_stage1_debugop(handle, filename, STAGE1_DEBUG_MEMTEST, 0, base, size); |
344 | |
345 | if(ret == -1) |
346 | return -1; |
347 | |
348 | uint32_t data[2]; |
349 | |
350 | ret = usbdev_recvbulk(handle->usb, &data, sizeof(data)); |
351 | |
352 | if(ret == -1) |
353 | return -1; |
354 | |
355 | hexdump(data, ret); |
356 | |
357 | if(ret < 4) { |
358 | errno = EIO; |
359 | |
360 | return -1; |
361 | } |
362 | |
363 | if(data[0] != 0) { |
364 | errno = EFAULT; |
365 | |
366 | *fail = data[0]; |
367 | |
368 | return -1; |
369 | } |
370 | |
371 | return 0; |
372 | } |
373 | |
374 | int ingenic_configure_stage2(void *hndl) { |
375 | HANDLE; |
376 | |
377 | // DS_flash_info (nand_config_t only) is not implemented in stage2, so using DS_hand (nand_config_t + firmware_config_t) |
378 | uint8_t *hand = malloc(sizeof(nand_config_t) + sizeof(firmware_config_t)); |
379 | |
380 | memcpy(hand, &handle->nand, sizeof(nand_config_t)); |
381 | memcpy(hand + sizeof(nand_config_t), &handle->cfg, sizeof(firmware_config_t)); |
382 | |
383 | int ret = usbdev_sendbulk(handle->usb, hand, sizeof(nand_config_t) + sizeof(firmware_config_t)); |
384 | |
385 | free(hand); |
386 | |
387 | if(ret == -1) |
388 | return -1; |
389 | |
390 | if(usbdev_vendor(handle->usb, USBDEV_TODEV, VR_CONFIGRATION, DS_hand, 0, 0, 0) == -1) |
391 | return -1; |
392 | |
393 | uint32_t result[8]; |
394 | |
395 | ret = usbdev_recvbulk(handle->usb, result, sizeof(result)); |
396 | |
397 | if(ret == -1) |
398 | return -1; |
399 | |
400 | return 0; |
401 | } |
402 | |
403 | int ingenic_load_sdram(void *hndl, void *data, uint32_t base, uint32_t size) { |
404 | HANDLE; |
405 | |
406 | int max = size, value = 0; |
407 | |
408 | CALLBACK(progress, PROGRESS_INIT, 0, max); |
409 | |
410 | while(size) { |
411 | int block = size > STAGE2_IOBUF ? STAGE2_IOBUF : size; |
412 | |
413 | debug(LEVEL_DEBUG, "Loading %d bytes from %p to 0x%08X\n", block, data, base); |
414 | |
415 | if(usbdev_sendbulk(handle->usb, data, block) == -1) |
416 | return -1; |
417 | |
418 | if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, base) == -1) |
419 | return -1; |
420 | |
421 | if(ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, block) == -1) |
422 | return -1; |
423 | |
424 | |
425 | if(usbdev_vendor(handle->usb, USBDEV_TODEV, VR_SDRAM_OPS, SDRAM_LOAD, 0, 0, 0) == -1) |
426 | return -1; |
427 | |
428 | uint32_t result[8]; |
429 | |
430 | int ret = usbdev_recvbulk(handle->usb, result, sizeof(result)); |
431 | if(ret == -1) |
432 | return -1; |
433 | |
434 | hexdump(result, ret); |
435 | |
436 | data += block; |
437 | base += block; |
438 | size -= block; |
439 | value += block; |
440 | |
441 | CALLBACK(progress, PROGRESS_UPDATE, value, max); |
442 | |
443 | } |
444 | |
445 | CALLBACK(progress, PROGRESS_FINI, 0, 0); |
446 | |
447 | debug(LEVEL_DEBUG, "Load done\n"); |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | int ingenic_load_sdram_file(void *hndl, uint32_t base, const char *file) { |
453 | HANDLE; |
454 | |
455 | FILE *fd = fopen(file, "rb"); |
456 | |
457 | if(fd == NULL) |
458 | return -1; |
459 | |
460 | fseek(fd, 0, SEEK_END); |
461 | int size = ftell(fd); |
462 | fseek(fd, 0, SEEK_SET); |
463 | |
464 | void *data = malloc(size); |
465 | size_t bytes = fread(data, 1, size, fd); |
466 | |
467 | fclose(fd); |
468 | |
469 | if(bytes != size) { |
470 | free(data); |
471 | |
472 | errno = EIO; |
473 | |
474 | return -1; |
475 | } |
476 | |
477 | int ret = ingenic_load_sdram(handle, data, base, size); |
478 | |
479 | free(data); |
480 | |
481 | return ret; |
482 | } |
483 | |
484 | int ingenic_go(void *hndl, uint32_t address) { |
485 | HANDLE; |
486 | |
487 | debug(LEVEL_DEBUG, "Go to 0x%08X\n", address); |
488 | |
489 | return ingenic_wordop(handle->usb, VR_PROGRAM_START2, address); |
490 | } |
491 | |
492 | static inline int ingenic_nandop(void *usb, uint8_t cs, uint8_t request, uint8_t param) { |
493 | return usbdev_vendor(usb, USBDEV_TODEV, VR_NAND_OPS, (param << 12) | (cs << 4) | (request & 0x0F), 0, 0, 0); |
494 | } |
495 | |
496 | int ingenic_query_nand(void *hndl, int cs, nand_info_t *info) { |
497 | HANDLE; |
498 | |
499 | if(ingenic_nandop(handle->usb, cs, NAND_QUERY, 0) == -1) |
500 | return -1; |
501 | |
502 | uint32_t dummy[8]; |
503 | |
504 | int ret = usbdev_recvbulk(handle->usb, dummy, sizeof(dummy)); |
505 | |
506 | if(ret == -1) |
507 | return -1; |
508 | |
509 | if(ret < sizeof(nand_info_t)) { |
510 | errno = EIO; |
511 | |
512 | return -1; |
513 | } |
514 | |
515 | memcpy(info, dummy, sizeof(nand_info_t)); |
516 | |
517 | if(usbdev_recvbulk(handle->usb, dummy, sizeof(dummy)) == -1) |
518 | return -1; |
519 | |
520 | return 0; |
521 | } |
522 | |
523 | int ingenic_dump_nand(void *hndl, int cs, int start, int pages, int type, const char *filename) { |
524 | HANDLE; |
525 | |
526 | int raw = type & NAND_RAW; |
527 | type &= ~NAND_RAW; |
528 | |
529 | int page_size = (handle->nand.nand_ps + (type == NO_OOB ? 0 : handle->nand.nand_os)); |
530 | int chunk_pages = STAGE2_IOBUF / page_size; |
531 | |
532 | FILE *dest = fopen(filename, "wb"); |
533 | |
534 | if(dest == NULL) |
535 | return -1; |
536 | |
537 | void *iobuf = malloc(chunk_pages * page_size); |
538 | |
539 | int ret = 0; |
540 | int value = 0, max = pages; |
541 | |
542 | CALLBACK(progress, PROGRESS_INIT, 0, max); |
543 | |
544 | while(pages > 0) { |
545 | int chunk = pages < chunk_pages ? pages : chunk_pages; |
546 | int bytes = chunk * page_size; |
547 | |
548 | ret = ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, start); |
549 | |
550 | if(ret == -1) |
551 | break; |
552 | |
553 | ret = ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, chunk); |
554 | |
555 | if(ret == -1) |
556 | break; |
557 | |
558 | ret = ingenic_nandop(handle->usb, cs, raw ? NAND_READ_RAW : NAND_READ, type); |
559 | |
560 | if(ret == -1) |
561 | break; |
562 | |
563 | int ret = usbdev_recvbulk(handle->usb, iobuf, bytes); |
564 | |
565 | if(ret == -1) |
566 | return -1; |
567 | |
568 | if(ret != bytes) { |
569 | debug(LEVEL_ERROR, "Ingenic: NAND dump truncated: expected %d bytes, received %d\n", bytes, ret); |
570 | |
571 | errno = EIO; |
572 | |
573 | break; |
574 | } |
575 | |
576 | uint16_t result[4]; |
577 | |
578 | |
579 | ret = usbdev_recvbulk(handle->usb, result, sizeof(result)); |
580 | |
581 | if(ret == -1) |
582 | return -1; |
583 | |
584 | if(result[3] != 0 && !raw) { |
585 | debug(LEVEL_ERROR, "Ingenic: ECC failure while reading NAND. See UART output for details\n"); |
586 | |
587 | errno = EIO; |
588 | |
589 | break; |
590 | } |
591 | |
592 | fwrite(iobuf, bytes, 1, dest); |
593 | |
594 | start += chunk; |
595 | pages -= chunk; |
596 | |
597 | value += chunk; |
598 | |
599 | CALLBACK(progress, PROGRESS_UPDATE, value, max); |
600 | } |
601 | |
602 | free(iobuf); |
603 | fclose(dest); |
604 | |
605 | CALLBACK(progress, PROGRESS_FINI, 0, 0); |
606 | |
607 | return ret; |
608 | } |
609 | |
610 | int ingenic_erase_nand(void *hndl, int cs, int start, int blocks) { |
611 | HANDLE; |
612 | |
613 | if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, start) == -1) |
614 | return -1; |
615 | |
616 | if(ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, blocks) == -1) |
617 | return -1; |
618 | |
619 | if(ingenic_nandop(handle->usb, cs, NAND_ERASE, 0) == -1) |
620 | return -1; |
621 | |
622 | uint16_t result[4]; |
623 | |
624 | int ret = usbdev_recvbulk(handle->usb, result, sizeof(result)); |
625 | |
626 | if(ret == -1) |
627 | return -1; |
628 | |
629 | hexdump(result, ret); |
630 | |
631 | return 0; |
632 | } |
633 | |
634 | int ingenic_program_nand(void *hndl, int cs, int start, int type, const char *filename) { |
635 | HANDLE; |
636 | |
637 | int page_size = (handle->nand.nand_ps + (type == NO_OOB ? 0 : handle->nand.nand_os)); |
638 | int chunk_pages = STAGE2_IOBUF / page_size; |
639 | |
640 | FILE *in = fopen(filename, "rb"); |
641 | |
642 | if(in == NULL) |
643 | return -1; |
644 | |
645 | fseek(in, 0, SEEK_END); |
646 | int file_size = ftell(in); |
647 | fseek(in, 0, SEEK_SET); |
648 | |
649 | int tail = file_size % page_size; |
650 | |
651 | if(tail) { |
652 | tail = page_size - tail; |
653 | |
654 | file_size += tail; |
655 | } |
656 | |
657 | int pages = file_size / page_size; |
658 | int ret = 0; |
659 | |
660 | void *iobuf = malloc(chunk_pages * page_size); |
661 | |
662 | debug(LEVEL_INFO, "Programming %d pages from %d (%d bytes, %d bytes/page)\n", pages, start, file_size, page_size); |
663 | |
664 | int value = 0, max = pages; |
665 | |
666 | CALLBACK(progress, PROGRESS_INIT, 0, max); |
667 | |
668 | while(pages > 0) { |
669 | int chunk = pages < chunk_pages ? pages : chunk_pages; |
670 | int bytes = chunk * page_size; |
671 | |
672 | ret = fread(iobuf, 1, bytes, in); |
673 | |
674 | if(pages < chunk_pages && tail) { |
675 | memset(iobuf + ret, 0xFF, tail); |
676 | |
677 | ret += tail; |
678 | } |
679 | |
680 | if(ret != bytes) { |
681 | debug(LEVEL_ERROR, "fread: %d\n", ret); |
682 | |
683 | ret = -1; |
684 | errno = EIO; |
685 | |
686 | break; |
687 | } |
688 | |
689 | if(usbdev_sendbulk(handle->usb, iobuf, bytes) == -1) |
690 | return -1; |
691 | |
692 | ret = ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, start); |
693 | |
694 | if(ret == -1) |
695 | break; |
696 | |
697 | ret = ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, chunk); |
698 | |
699 | if(ret == -1) |
700 | break; |
701 | |
702 | ret = ingenic_nandop(handle->usb, cs, NAND_PROGRAM, type); |
703 | |
704 | if(ret == -1) |
705 | break; |
706 | |
707 | uint16_t result[4]; |
708 | |
709 | ret = usbdev_recvbulk(handle->usb, result, sizeof(result)); |
710 | |
711 | if(ret == -1) |
712 | return -1; |
713 | |
714 | hexdump(result, ret); |
715 | |
716 | start += chunk; |
717 | pages -= chunk; |
718 | value += chunk; |
719 | |
720 | CALLBACK(progress, PROGRESS_UPDATE, value, max); |
721 | } |
722 | |
723 | free(iobuf); |
724 | fclose(in); |
725 | |
726 | CALLBACK(progress, PROGRESS_FINI, 0, 0); |
727 | |
728 | return ret; |
729 | } |
730 | |
731 | int ingenic_load_nand(void *hndl, int cs, int start, int pages, uint32_t base) { |
732 | HANDLE; |
733 | |
734 | if(ingenic_wordop(handle->usb, VR_PROGRAM_START1, base) == -1) |
735 | return -1; |
736 | |
737 | if(ingenic_wordop(handle->usb, VR_SET_DATA_ADDRESS, start) == -1) |
738 | return -1; |
739 | |
740 | if(ingenic_wordop(handle->usb, VR_SET_DATA_LENGTH, pages) == -1) |
741 | return -1; |
742 | |
743 | if(ingenic_nandop(handle->usb, cs, NAND_READ_TO_RAM, 0) == -1) |
744 | return -1; |
745 | |
746 | uint16_t result[4]; |
747 | |
748 | if(usbdev_recvbulk(handle->usb, result, sizeof(result)) == -1) |
749 | return -1; |
750 | |
751 | return 0; |
752 | } |
753 | |
754 |