Root/jzboot/src/ingenic.c

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

Archive Download this file



interactive