/* V3 Flasher - temporary version */ #include #include #include #include #include #include #include #include #include #include "serial.h" #include "applet.h" #define BOARD_REV_V2 2 #define BOARD_REV_V3 3 #define ID_SAM9263 0x019607A2 #define ID_SAM9G45 0x819b05a2 #define SERIAL_TIMEOUT 10000000 #define MBOX_COMMAND 0x4 #define MBOX_STATUS 0x8 #define MBOX_COMTYPE 0xc #define MBOX_TRACELEVEL 0x10 #define MBOX_EXTRAM_VDDMEM 0x14 #define MBOX_EXTRAM_RAMTYPE 0x18 #define MBOX_EXTRAM_BUSWIDTH 0x1c #define MBOX_EXTRAM_DDRMODEL 0x20 #define MBOX_DATAFLASH_INDEX 0x14 #define MBOX_DATAFLASH_BUFFER_ADDR 0x10 #define MBOX_PROG_BUF_SIZE 0x #define MBOX_MEM_BUF_ADDR 0xc #define MBOX_MEM_BUF_SIZE 0x10 #define MBOX_MEM_BUF_OFFSET 0x14 #define MBOX_MEM_ERASE_TYPE 0xc #define MBOX_MEM_ERASE_START 0x10 #define MBOX_MEM_ERASE_END 0x14 #define INTERNAL_SRAM_BUF 0x300000 #define SDRAM_START 0x70000000 #define DF_HWINFO_START 0x000000094800 #define DF_HWINFO_END 0x000000095040 #define PORT_SPEED 115200 #define MEMTYPE_DDR 0 #define MEMTYPE_DF 1 #define MEMTYPE_NAND 2 #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) #endif const char* memName[3] = { "DDR", "DataFlash", "NandFlash" }; typedef struct { char *fname; uint32_t offset; } sndfile; sndfile filearray[16]; //External variable from version.c extern const char build_time[]; extern const char git_user[]; extern const char git_revision[]; char *program_path; char *program_name; int applet_silent_mode = 1; unsigned int buffer_size = 0x3000; unsigned int buffer_addr ; int mem_write(int type, uint32_t offset, uint32_t buf_addr, uint32_t size); int crc16(int value, int crcin) { int k = (((crcin >> 8) ^ value) & 255) << 8; int crc = 0; int bits = 8; do { if (( crc ^ k ) & 0x8000) crc = (crc << 1) ^ 0x1021; else crc <<= 1; k <<= 1; } while (--bits); return ((crcin << 8) ^ crc); } int write_xmodem(int index, char *p) { unsigned char data[133]; unsigned short crc=0; int i; data[0]=1; data[1]=index; data[2]=0xff-index; memcpy(data+3,p,128); for(i=3;i<131;i++){ crc=crc16(data[i],crc); } data[131] = (crc>>8)&0xff; data[132] = (crc)&0xff; printf("XMDump: "); for(i=0;i<133;i++) printf("%02x ", data[i]); printf("\n\n"); serial_write(data,133); return serial_read_byte(); } void die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "Error: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n\n"); va_end(ap); exit(-1); } void samba_write (uint32_t addr, uint32_t data, int size, int timeout) { char tmpbuf[1024]; uint32_t tstart; serial_purge(); snprintf(tmpbuf, 1024, "%c%08x,%08x#", size==1?'O':(size==2?'H':'W'), addr, data); serial_write(tmpbuf, strlen(tmpbuf)); tstart = sys_get_clock_usec(); while(1) { if(serial_data_avail()) { if(serial_read_byte() =='>') break; } if(sys_get_clock_usec() - tstart > timeout) die("tx timeout"); } } uint32_t samba_read (uint32_t addr, int size, int timeout) { char tmpbuf[1024],c=0; int i,xpos=-1; uint32_t rval; uint32_t tstart; serial_purge(); snprintf(tmpbuf, 1024, "%c%08x,#", size==1?'o':(size==2?'h':'w'), addr); // printf("--> cmd: '%s'\n", tmpbuf); serial_write(tmpbuf, strlen(tmpbuf)); tstart = sys_get_clock_usec(); for(i=0;i<1024 && c!='>';) { if(serial_data_avail()) { tmpbuf[i]=c=serial_read_byte(); // fprintf(stderr,"%c", c); if(c=='x') xpos=i; i++; } if(sys_get_clock_usec()-tstart>timeout) die("rx timeout"); } if(xpos<0) die("invalid response from samba"); sscanf(tmpbuf, "%x", &rval); // fprintf(stderr,"samba_read: %x\n", rval); return rval; } /** * filename: name of the file in the host * address: place to send to file on the device * offset: internal offset, can be use with size * size: can be 0 */ static int samba_send_file(const char *filename, uint32_t address, uint32_t offset, uint32_t size, int quiet) { FILE *f; unsigned char *buf; int boffset = 0; char tmp[4097]; // printf("SendFile: %s\n", filename); f = fopen(filename, "rb"); if(!f) die("file open error: '%s'",filename); if(!size) { fseek(f, 0, SEEK_END); size = ftell(f); offset = 0; rewind(f); } else fseek(f, offset, SEEK_SET); if(!quiet) printf("size 0x%x (%dKb)\n",size,size/1024); buf = malloc(size+1); if(!buf) die("malloc failed"); fread(buf, 1, size, f); fclose(f); // printf("Send: %s\n", filename); while(size > 0) { int tosend = size > 1024 ? 1024 : size; // printf("Sending %d bytes\n", tosend); snprintf(tmp, 128, "S%08x,%x#", address, tosend); serial_write(tmp, strlen(tmp)); memcpy(tmp, buf + boffset, tosend); tmp[tosend]='\n'; serial_write(tmp, tosend+1); size -= tosend; boffset +=tosend; address += tosend; while(1) { if(serial_data_avail()) if(serial_read_byte() == '>') break; } } free(buf); return boffset; } static void samba_load_applet(char *applet_name, uint32_t address) { char namebuf[1024]; printf("loading applet %s at 0x%08x\n", applet_name, address); snprintf(namebuf, 1024, "%s/samba_applets/%s.bin", program_path, applet_name); samba_send_file(namebuf, address, 0, 0, 1); } static void mbox_write(uint32_t base, uint32_t offset, uint32_t value) { samba_write(base+offset, value, 4, SERIAL_TIMEOUT); } void samba_run(uint32_t addr, int timeout) { char tmpbuf[1024],c; uint32_t tstart,t1; serial_purge(); snprintf(tmpbuf, 1024, "G%08x#", addr); serial_write(tmpbuf, strlen(tmpbuf)); t1 = tstart = sys_get_clock_usec(); while((timeout && (sys_get_clock_usec()- tstart < timeout)) || !timeout) { if(serial_data_avail()) { c = serial_read_byte(); if(c == '>') break; if(!applet_silent_mode )fprintf(stderr,"%c", c); t1 = sys_get_clock_usec(); } else if(sys_get_clock_usec() - t1 > 10000) { // fprintf(stderr,"Phase1\n"); // serial_write_byte('\n'); serial_write_byte(0x0a); serial_write_byte(0x0d); serial_write_byte('T'); serial_write_byte('#'); serial_write_byte(0x0a); serial_write_byte(0x0d); usleep(10000); while(serial_data_avail()) { c=serial_read_byte(); if( c == '>') return; else if(!applet_silent_mode) fprintf(stderr,"%c", c); } } usleep(1000); //TODO: Improve data available for DDR load } } int samba_connect(int board_rev) { char handshake[] = {0x80, 0x80, 0x23}; serial_write(handshake,3); sys_delay(100); uint32_t id = samba_read(0xffffee40, 4, SERIAL_TIMEOUT); printf("CPU ID: 0x%08x\n",id); if(board_rev == BOARD_REV_V2 && id != ID_SAM9263) die ("Not a 9263 CPU"); if(board_rev == BOARD_REV_V3 && id != ID_SAM9G45) die ("Not a 9G45 CPU"); return 0; } int ddr_init(int board_rev) { if(board_rev == BOARD_REV_V2) samba_load_applet("isp-extram-at91sam9263", INTERNAL_SRAM_BUF); else if(board_rev == BOARD_REV_V3) samba_load_applet("isp-extram-at91sam9g45", INTERNAL_SRAM_BUF); mbox_write(INTERNAL_SRAM_BUF, MBOX_COMMAND, APPLET_CMD_INIT); mbox_write(INTERNAL_SRAM_BUF, MBOX_TRACELEVEL, 0); mbox_write(INTERNAL_SRAM_BUF, MBOX_EXTRAM_RAMTYPE, 1); mbox_write(INTERNAL_SRAM_BUF, MBOX_EXTRAM_VDDMEM, 0); mbox_write(INTERNAL_SRAM_BUF, MBOX_EXTRAM_BUSWIDTH, 16); mbox_write(INTERNAL_SRAM_BUF, MBOX_EXTRAM_DDRMODEL, 0); samba_run(INTERNAL_SRAM_BUF, 100000000); if((samba_read(INTERNAL_SRAM_BUF + MBOX_COMMAND, 4, 10000000)) != ~APPLET_CMD_INIT) die("invalid response from applet init"); if((samba_read(INTERNAL_SRAM_BUF + MBOX_STATUS, 4, 10000000)) != APPLET_SUCCESS) die("invalid response from applet status"); return 0; } int ddr_load(int nFile, const sndfile* pFileArray) { int i; uint32_t len=0; fprintf(stderr,"Loading DDR...\n"); for(i=0;i Done! \n\n",memName[type]); return 0; } int mem_program(int type, int nFile, const sndfile* pFileArray) { int i; uint32_t len=0; if(type==MEMTYPE_DDR) return ddr_load(nFile,pFileArray); else { fprintf(stderr,"Programming %s...\n",memName[type]); for(i=0;i DONE\n\n",action,memName[type]); } int mem_write(int type, uint32_t offset, uint32_t buf_addr, uint32_t size) { uint32_t timeout; applet_silent_mode=1; fprintf(stderr, "%s: Writing %d bytes at offset 0x%x buffer %x....", memName[type], size, offset, buf_addr); mbox_write(INTERNAL_SRAM_BUF, MBOX_MEM_BUF_ADDR, buf_addr); fprintf(stderr,"A"); mbox_write(INTERNAL_SRAM_BUF, MBOX_MEM_BUF_ADDR, buf_addr); fprintf(stderr,"B"); mbox_write(INTERNAL_SRAM_BUF, MBOX_MEM_BUF_SIZE, size); fprintf(stderr,"C"); mbox_write(INTERNAL_SRAM_BUF, MBOX_MEM_BUF_OFFSET, offset); fprintf(stderr,"D"); mbox_write(INTERNAL_SRAM_BUF, MBOX_COMMAND, APPLET_CMD_WRITE); fprintf(stderr,"E"); if(type==MEMTYPE_DDR) timeout=10; else timeout=0; samba_run(INTERNAL_SRAM_BUF, timeout); fprintf(stderr,"F"); if(type == MEMTYPE_DDR) // booting a barebox/kernel will fuck up USB, so we'll never get any response return 0; if(samba_read(INTERNAL_SRAM_BUF + MBOX_COMMAND, 4, 10000000) != ~APPLET_CMD_WRITE) die(" invalid response from applet"); if(samba_read(INTERNAL_SRAM_BUF + MBOX_STATUS, 4, 10000000) != APPLET_SUCCESS) die(" write failure"); printf(" OK\n"); return 0; } uint32_t getOffset(const char *str) { uint32_t ret=0; if (str == NULL || *str == '\0') return ret; if(str[0]=='0' && (str[1]=='X' || str[1]=='x')) { str+=2; sscanf(str, "%x", &ret); } else sscanf(str, "%d", &ret); return ret; } void show_help(const char* serial_port) { printf("WhiteRabbit MCH DataFlash programmer (c) T.W. 2010\n"); printf("Compiled by %s (%s)\ngit rev:%s\n\n",git_user,build_time,git_revision); printf("Usage: %s [options] [ ]\n", program_name); printf("if FILEx set it will write the FILEx at the given OFFSETx (by default 0x0), " "otherwise you can use this binary to erase, check or scrub a memory\n"); printf("Options:\n"); printf("\t-m \t Select the memory type: ddr, nf, df\n"); printf("\t-e \t\t erase the memory\n"); printf("\t-c \t\t check the memory (not available for df)\n"); printf("\t-s \t\t scrub the memory (WARNING: full erase)\n"); printf("\t-r \t\t addr run the image at address addr\n"); printf("\t-p SERIAL_PORT\t By default it is: -p %s\n",serial_port); printf("\t-h \t\t Show this little help\n"); printf("\n"); } int main(int argc, char *argv[]) { int board_rev = BOARD_REV_V3; char *serial_port = "/dev/ttyACM0"; char * mode_str ="df"; char opt; int erase=0, check=0, scrub=0; int type=-1; unsigned int run_addr=0; int nFile=0; int run =0 ; program_name = basename(strdup(argv[0])); program_path = dirname(strdup(argv[0])); if(argc==1) { show_help(serial_port); return 1; } //Parse options while ((opt = getopt (argc, argv, "m:p:r:ecsh")) != -1) switch (opt) { case 'm': mode_str = optarg; break; case 'p': serial_port = optarg; break; case 'e': erase = 1; break; case 'c': check = 1; break; case 's': scrub = 1; break; case 'r': run = 1; sscanf(optarg,"%i", &run_addr); break; case 'h': case '?': show_help(serial_port); return 0; default: printf("find: illegal option %c\n", opt); break; } //Obtain the various filename & offset (3 MAX) for(nFile=0; optind < argc; nFile++) { if (nFile >= ARRAY_SIZE(filearray)) { fprintf(stderr, "%s: too may arguments\n", argv[0]); exit(1); } filearray[nFile].fname=argv[optind++]; filearray[nFile].offset=getOffset(argv[optind++]); } printf("n=%d, mode=%s, nFile=%d, serial=%s, (e=%d,c=%d,s=%d)\n", argc, mode_str, nFile, serial_port, erase,check,scrub); if (serial_open(serial_port, PORT_SPEED) < 0) { fprintf(stderr, "%s: %s: %s\n", argv[0], serial_port, strerror(errno)); exit(1); } fprintf(stderr,"Initializing SAM-BA: "); samba_connect(board_rev); //Init the DDR for every action (used as buffer) fprintf(stderr,"\nInitializing DDR...\n"); ddr_init(board_rev); fprintf(stderr,"Initializing DDR > Done\n\n"); //Obtain the memory type mode if(strcmp(mode_str,"nand")==0 || strcmp(mode_str,"nf")==0) { type=MEMTYPE_NAND; } else if(strcmp(mode_str,"dataflash")==0 || strcmp(mode_str,"df")==0) { type=MEMTYPE_DF; } else if(strcmp(mode_str,"ddr")==0 || strcmp(mode_str,"ram")==0) { type=MEMTYPE_DDR; } else { fprintf(stderr,"Bad memory type %s...\n\n",mode_str); show_help(serial_port); return 0; } //Init the memory (except DDR which is already init) if(type!=MEMTYPE_DDR) memflash_init(type, board_rev); //Run the action if((erase || scrub) && type!=MEMTYPE_DDR) mem_erase(type,scrub); if(check) mem_check(type); if(nFile>0) mem_program(type,nFile,(const sndfile*)&filearray); if(run) { fprintf(stderr,"Executing payload @ %x\n", run_addr); samba_run(run_addr, 0); } serial_close(); printf("Closing...\n"); return 0; }