diff options
Diffstat (limited to 'src/avrdude/avr.c')
-rw-r--r-- | src/avrdude/avr.c | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/src/avrdude/avr.c b/src/avrdude/avr.c new file mode 100644 index 000000000..73dcaf4ff --- /dev/null +++ b/src/avrdude/avr.c @@ -0,0 +1,1254 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2000-2004 Brian S. Dean <bsd@bsdhome.com> + * Copyright (C) 2011 Darell Tan <darell.tan@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* $Id$ */ + +#include "ac_cfg.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> + +#if !defined(WIN32NATIVE) +# include <sys/time.h> +#endif + +#include "avrdude.h" +#include "libavrdude.h" + +#include "tpi.h" + +FP_UpdateProgress update_progress; + +#define DEBUG 0 + +/* TPI: returns 1 if NVM controller busy, 0 if free */ +int avr_tpi_poll_nvmbsy(PROGRAMMER *pgm) +{ + unsigned char cmd; + unsigned char res; + + cmd = TPI_CMD_SIN | TPI_SIO_ADDR(TPI_IOREG_NVMCSR); + (void)pgm->cmd_tpi(pgm, &cmd, 1, &res, 1); + return (res & TPI_IOREG_NVMCSR_NVMBSY); +} + +/* TPI chip erase sequence */ +int avr_tpi_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + int err; + AVRMEM *mem; + + if (p->flags & AVRPART_HAS_TPI) { + pgm->pgm_led(pgm, ON); + + /* Set Pointer Register */ + mem = avr_locate_mem(p, "flash"); + if (mem == NULL) { + avrdude_message(MSG_INFO, "No flash memory to erase for part %s\n", + p->desc); + return -1; + } + + unsigned char cmd[] = { + /* write pointer register high byte */ + (TPI_CMD_SSTPR | 0), + ((mem->offset & 0xFF) | 1), + /* and low byte */ + (TPI_CMD_SSTPR | 1), + ((mem->offset >> 8) & 0xFF), + /* write CHIP_ERASE command to NVMCMD register */ + (TPI_CMD_SOUT | TPI_SIO_ADDR(TPI_IOREG_NVMCMD)), + TPI_NVMCMD_CHIP_ERASE, + /* write dummy value to start erase */ + TPI_CMD_SST, + 0xFF + }; + + while (avr_tpi_poll_nvmbsy(pgm)); + + err = pgm->cmd_tpi(pgm, cmd, sizeof(cmd), NULL, 0); + if(err) + return err; + + while (avr_tpi_poll_nvmbsy(pgm)); + + pgm->pgm_led(pgm, OFF); + + return 0; + } else { + avrdude_message(MSG_INFO, "%s called for a part that has no TPI\n", __func__); + return -1; + } +} + +/* TPI program enable sequence */ +int avr_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p, unsigned char guard_time) +{ + int err, retry; + unsigned char cmd[2]; + unsigned char response; + + if(p->flags & AVRPART_HAS_TPI) { + /* set guard time */ + cmd[0] = (TPI_CMD_SSTCS | TPI_REG_TPIPCR); + cmd[1] = guard_time; + + err = pgm->cmd_tpi(pgm, cmd, sizeof(cmd), NULL, 0); + if(err) + return err; + + /* read TPI ident reg */ + cmd[0] = (TPI_CMD_SLDCS | TPI_REG_TPIIR); + err = pgm->cmd_tpi(pgm, cmd, 1, &response, sizeof(response)); + if (err || response != TPI_IDENT_CODE) { + avrdude_message(MSG_INFO, "TPIIR not correct\n"); + return -1; + } + + /* send SKEY command + SKEY */ + err = pgm->cmd_tpi(pgm, tpi_skey_cmd, sizeof(tpi_skey_cmd), NULL, 0); + if(err) + return err; + + /* check if device is ready */ + for(retry = 0; retry < 10; retry++) + { + cmd[0] = (TPI_CMD_SLDCS | TPI_REG_TPISR); + err = pgm->cmd_tpi(pgm, cmd, 1, &response, sizeof(response)); + if(err || !(response & TPI_REG_TPISR_NVMEN)) + continue; + + return 0; + } + + avrdude_message(MSG_INFO, "Error enabling TPI external programming mode:"); + avrdude_message(MSG_INFO, "Target does not reply\n"); + return -1; + + } else { + avrdude_message(MSG_INFO, "%s called for a part that has no TPI\n", __func__); + return -1; + } +} + +/* TPI: setup NVMCMD register and pointer register (PR) for read/write/erase */ +static int avr_tpi_setup_rw(PROGRAMMER * pgm, AVRMEM * mem, + unsigned long addr, unsigned char nvmcmd) +{ + unsigned char cmd[4]; + int rc; + + /* set NVMCMD register */ + cmd[0] = TPI_CMD_SOUT | TPI_SIO_ADDR(TPI_IOREG_NVMCMD); + cmd[1] = nvmcmd; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + /* set Pointer Register (PR) */ + cmd[0] = TPI_CMD_SSTPR | 0; + cmd[1] = (mem->offset + addr) & 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + cmd[0] = TPI_CMD_SSTPR | 1; + cmd[1] = ((mem->offset + addr) >> 8) & 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + if (rc == -1) + return -1; + + return 0; +} + +int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char * value) +{ + unsigned char cmd[4]; + unsigned char res[4]; + unsigned char data; + int r; + OPCODE * readop, * lext; + + if (pgm->cmd == NULL) { + avrdude_message(MSG_INFO, "%s: Error: %s programmer uses avr_read_byte_default() but does not\n" + "provide a cmd() method.\n", + progname, pgm->type); + return -1; + } + + pgm->pgm_led(pgm, ON); + pgm->err_led(pgm, OFF); + + if (p->flags & AVRPART_HAS_TPI) { + if (pgm->cmd_tpi == NULL) { + avrdude_message(MSG_INFO, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for read */ + avr_tpi_setup_rw(pgm, mem, addr, TPI_NVMCMD_NO_OPERATION); + + /* load byte */ + cmd[0] = TPI_CMD_SLD; + r = pgm->cmd_tpi(pgm, cmd, 1, value, 1); + if (r == -1) + return -1; + + return 0; + } + + /* + * figure out what opcode to use + */ + if (mem->op[AVR_OP_READ_LO]) { + if (addr & 0x00000001) + readop = mem->op[AVR_OP_READ_HI]; + else + readop = mem->op[AVR_OP_READ_LO]; + addr = addr / 2; + } + else { + readop = mem->op[AVR_OP_READ]; + } + + if (readop == NULL) { +#if DEBUG + avrdude_message(MSG_INFO, "avr_read_byte(): operation not supported on memory type \"%s\"\n", + mem->desc); +#endif + return -1; + } + + /* + * If this device has a "load extended address" command, issue it. + */ + lext = mem->op[AVR_OP_LOAD_EXT_ADDR]; + if (lext != NULL) { + memset(cmd, 0, sizeof(cmd)); + + avr_set_bits(lext, cmd); + avr_set_addr(lext, cmd, addr); + r = pgm->cmd(pgm, cmd, res); + if (r < 0) + return r; + } + + memset(cmd, 0, sizeof(cmd)); + + avr_set_bits(readop, cmd); + avr_set_addr(readop, cmd, addr); + r = pgm->cmd(pgm, cmd, res); + if (r < 0) + return r; + data = 0; + avr_get_output(readop, res, &data); + + pgm->pgm_led(pgm, OFF); + + *value = data; + + return 0; +} + + +/* + * Return the number of "interesting" bytes in a memory buffer, + * "interesting" being defined as up to the last non-0xff data + * value. This is useful for determining where to stop when dealing + * with "flash" memory, since writing 0xff to flash is typically a + * no-op. Always return an even number since flash is word addressed. + */ +int avr_mem_hiaddr(AVRMEM * mem) +{ + int i, n; + + /* return the highest non-0xff address regardless of how much + memory was read */ + for (i=mem->size-1; i>0; i--) { + if (mem->buf[i] != 0xff) { + n = i+1; + if (n & 0x01) + return n+1; + else + return n; + } + } + + return 0; +} + + +/* + * Read the entirety of the specified memory type into the + * corresponding buffer of the avrpart pointed to by 'p'. + * If v is non-NULL, verify against v's memory area, only + * those cells that are tagged TAG_ALLOCATED are verified. + * + * Return the number of bytes read, or < 0 if an error occurs. + */ +int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, + AVRPART * v) +{ + unsigned long i, lastaddr; + unsigned char cmd[4]; + AVRMEM * mem, * vmem = NULL; + int rc; + + mem = avr_locate_mem(p, memtype); + if (v != NULL) + vmem = avr_locate_mem(v, memtype); + if (mem == NULL) { + avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n", + memtype, p->desc); + return -1; + } + + /* + * start with all 0xff + */ + memset(mem->buf, 0xff, mem->size); + + /* supports "paged load" thru post-increment */ + if ((p->flags & AVRPART_HAS_TPI) && mem->page_size != 0 && + pgm->cmd_tpi != NULL) { + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for read (NOOP) */ + avr_tpi_setup_rw(pgm, mem, 0, TPI_NVMCMD_NO_OPERATION); + + /* load bytes */ + for (lastaddr = i = 0; i < mem->size; i++) { + RETURN_IF_CANCEL(); + if (vmem == NULL || + (vmem->tags[i] & TAG_ALLOCATED) != 0) + { + if (lastaddr != i) { + /* need to setup new address */ + avr_tpi_setup_rw(pgm, mem, i, TPI_NVMCMD_NO_OPERATION); + lastaddr = i; + } + cmd[0] = TPI_CMD_SLD_PI; + rc = pgm->cmd_tpi(pgm, cmd, 1, mem->buf + i, 1); + lastaddr++; + if (rc == -1) { + avrdude_message(MSG_INFO, "avr_read(): error reading address 0x%04lx\n", i); + return -1; + } + } + report_progress(i, mem->size, NULL); + } + return avr_mem_hiaddr(mem); + } + + if (pgm->paged_load != NULL && mem->page_size != 0) { + /* + * the programmer supports a paged mode read + */ + int need_read, failure; + unsigned int pageaddr; + unsigned int npages, nread; + + /* quickly scan number of pages to be written to first */ + for (pageaddr = 0, npages = 0; + pageaddr < mem->size; + pageaddr += mem->page_size) { + /* check whether this page must be read */ + for (i = pageaddr; + i < pageaddr + mem->page_size; + i++) + if (vmem == NULL /* no verify, read everything */ || + (mem->tags[i] & TAG_ALLOCATED) != 0 /* verify, do only + read pages that + are needed in + input file */) { + npages++; + break; + } + } + + for (pageaddr = 0, failure = 0, nread = 0; + !failure && pageaddr < mem->size; + pageaddr += mem->page_size) { + RETURN_IF_CANCEL(); + /* check whether this page must be read */ + for (i = pageaddr, need_read = 0; + i < pageaddr + mem->page_size; + i++) + if (vmem == NULL /* no verify, read everything */ || + (vmem->tags[i] & TAG_ALLOCATED) != 0 /* verify, do only + read pages that + are needed in + input file */) { + need_read = 1; + break; + } + if (need_read) { + rc = pgm->paged_load(pgm, p, mem, mem->page_size, + pageaddr, mem->page_size); + if (rc < 0) + /* paged load failed, fall back to byte-at-a-time read below */ + failure = 1; + } else { + avrdude_message(MSG_DEBUG, "%s: avr_read(): skipping page %u: no interesting data\n", + progname, pageaddr / mem->page_size); + } + nread++; + report_progress(nread, npages, NULL); + } + if (!failure) { + if (strcasecmp(mem->desc, "flash") == 0 || + strcasecmp(mem->desc, "application") == 0 || + strcasecmp(mem->desc, "apptable") == 0 || + strcasecmp(mem->desc, "boot") == 0) + return avr_mem_hiaddr(mem); + else + return mem->size; + } + /* else: fall back to byte-at-a-time write, for historical reasons */ + } + + if (strcmp(mem->desc, "signature") == 0) { + if (pgm->read_sig_bytes) { + return pgm->read_sig_bytes(pgm, p, mem); + } + } + + for (i=0; i < mem->size; i++) { + RETURN_IF_CANCEL(); + if (vmem == NULL || + (vmem->tags[i] & TAG_ALLOCATED) != 0) + { + rc = pgm->read_byte(pgm, p, mem, i, mem->buf + i); + if (rc != 0) { + avrdude_message(MSG_INFO, "avr_read(): error reading address 0x%04lx\n", i); + if (rc == -1) + avrdude_message(MSG_INFO, " read operation not supported for memory \"%s\"\n", + memtype); + return -2; + } + } + report_progress(i, mem->size, NULL); + } + + if (strcasecmp(mem->desc, "flash") == 0 || + strcasecmp(mem->desc, "application") == 0 || + strcasecmp(mem->desc, "apptable") == 0 || + strcasecmp(mem->desc, "boot") == 0) + return avr_mem_hiaddr(mem); + else + return i; +} + + +/* + * write a page data at the specified address + */ +int avr_write_page(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr) +{ + unsigned char cmd[4]; + unsigned char res[4]; + OPCODE * wp, * lext; + + if (pgm->cmd == NULL) { + avrdude_message(MSG_INFO, "%s: Error: %s programmer uses avr_write_page() but does not\n" + "provide a cmd() method.\n", + progname, pgm->type); + return -1; + } + + wp = mem->op[AVR_OP_WRITEPAGE]; + if (wp == NULL) { + avrdude_message(MSG_INFO, "avr_write_page(): memory \"%s\" not configured for page writes\n", + mem->desc); + return -1; + } + + /* + * if this memory is word-addressable, adjust the address + * accordingly + */ + if ((mem->op[AVR_OP_LOADPAGE_LO]) || (mem->op[AVR_OP_READ_LO])) + addr = addr / 2; + + pgm->pgm_led(pgm, ON); + pgm->err_led(pgm, OFF); + + /* + * If this device has a "load extended address" command, issue it. + */ + lext = mem->op[AVR_OP_LOAD_EXT_ADDR]; + if (lext != NULL) { + memset(cmd, 0, sizeof(cmd)); + + avr_set_bits(lext, cmd); + avr_set_addr(lext, cmd, addr); + pgm->cmd(pgm, cmd, res); + } + + memset(cmd, 0, sizeof(cmd)); + + avr_set_bits(wp, cmd); + avr_set_addr(wp, cmd, addr); + pgm->cmd(pgm, cmd, res); + + /* + * since we don't know what voltage the target AVR is powered by, be + * conservative and delay the max amount the spec says to wait + */ + usleep(mem->max_write_delay); + + pgm->pgm_led(pgm, OFF); + return 0; +} + + +int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data) +{ + unsigned char cmd[4]; + unsigned char res[4]; + unsigned char r; + int ready; + int tries; + unsigned long start_time; + unsigned long prog_time; + unsigned char b; + unsigned short caddr; + OPCODE * writeop; + int rc; + int readok=0; + struct timeval tv; + + if (pgm->cmd == NULL) { + avrdude_message(MSG_INFO, "%s: Error: %s programmer uses avr_write_byte_default() but does not\n" + "provide a cmd() method.\n", + progname, pgm->type); + return -1; + } + + if (p->flags & AVRPART_HAS_TPI) { + if (pgm->cmd_tpi == NULL) { + avrdude_message(MSG_INFO, "%s: Error: %s programmer does not support TPI\n", + progname, pgm->type); + return -1; + } + + if (strcmp(mem->desc, "flash") == 0) { + avrdude_message(MSG_INFO, "Writing a byte to flash is not supported for %s\n", p->desc); + return -1; + } else if ((mem->offset + addr) & 1) { + avrdude_message(MSG_INFO, "Writing a byte to an odd location is not supported for %s\n", p->desc); + return -1; + } + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* must erase fuse first */ + if (strcmp(mem->desc, "fuse") == 0) { + /* setup for SECTION_ERASE (high byte) */ + avr_tpi_setup_rw(pgm, mem, addr | 1, TPI_NVMCMD_SECTION_ERASE); + + /* write dummy byte */ + cmd[0] = TPI_CMD_SST; + cmd[1] = 0xFF; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + while (avr_tpi_poll_nvmbsy(pgm)); + } + + /* setup for WORD_WRITE */ + avr_tpi_setup_rw(pgm, mem, addr, TPI_NVMCMD_WORD_WRITE); + + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = data; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + /* dummy high byte to start WORD_WRITE */ + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = data; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + while (avr_tpi_poll_nvmbsy(pgm)); + + return 0; + } + + if (!mem->paged && + (p->flags & AVRPART_IS_AT90S1200) == 0) { + /* + * check to see if the write is necessary by reading the existing + * value and only write if we are changing the value; we can't + * use this optimization for paged addressing. + * + * For mysterious reasons, on the AT90S1200, this read operation + * sometimes causes the high byte of the same word to be + * programmed to the value of the low byte that has just been + * programmed before. Avoid that optimization on this device. + */ + rc = pgm->read_byte(pgm, p, mem, addr, &b); + if (rc != 0) { + if (rc != -1) { + return -2; + } + /* + * the read operation is not support on this memory type + */ + } + else { + readok = 1; + if (b == data) { + return 0; + } + } + } + + /* + * determine which memory opcode to use + */ + if (mem->op[AVR_OP_WRITE_LO]) { + if (addr & 0x01) + writeop = mem->op[AVR_OP_WRITE_HI]; + else + writeop = mem->op[AVR_OP_WRITE_LO]; + caddr = addr / 2; + } + else if (mem->paged && mem->op[AVR_OP_LOADPAGE_LO]) { + if (addr & 0x01) + writeop = mem->op[AVR_OP_LOADPAGE_HI]; + else + writeop = mem->op[AVR_OP_LOADPAGE_LO]; + caddr = addr / 2; + } + else { + writeop = mem->op[AVR_OP_WRITE]; + caddr = addr; + } + + if (writeop == NULL) { +#if DEBUG + avrdude_message(MSG_INFO, "avr_write_byte(): write not supported for memory type \"%s\"\n", + mem->desc); +#endif + return -1; + } + + + pgm->pgm_led(pgm, ON); + pgm->err_led(pgm, OFF); + + memset(cmd, 0, sizeof(cmd)); + + avr_set_bits(writeop, cmd); + avr_set_addr(writeop, cmd, caddr); + avr_set_input(writeop, cmd, data); + pgm->cmd(pgm, cmd, res); + + if (mem->paged) { + /* + * in paged addressing, single bytes to be written to the memory + * page complete immediately, we only need to delay when we commit + * the whole page via the avr_write_page() routine. + */ + pgm->pgm_led(pgm, OFF); + return 0; + } + + if (readok == 0) { + /* + * read operation not supported for this memory type, just wait + * the max programming time and then return + */ + usleep(mem->max_write_delay); /* maximum write delay */ + pgm->pgm_led(pgm, OFF); + return 0; + } + + tries = 0; + ready = 0; + while (!ready) { + + if ((data == mem->readback[0]) || + (data == mem->readback[1])) { + /* + * use an extra long delay when we happen to be writing values + * used for polled data read-back. In this case, polling + * doesn't work, and we need to delay the worst case write time + * specified for the chip. + */ + usleep(mem->max_write_delay); + rc = pgm->read_byte(pgm, p, mem, addr, &r); + if (rc != 0) { + pgm->pgm_led(pgm, OFF); + pgm->err_led(pgm, OFF); + return -5; + } + } + else { + gettimeofday (&tv, NULL); + start_time = (tv.tv_sec * 1000000) + tv.tv_usec; + do { + /* + * Do polling, but timeout after max_write_delay. + */ + rc = pgm->read_byte(pgm, p, mem, addr, &r); + if (rc != 0) { + pgm->pgm_led(pgm, OFF); + pgm->err_led(pgm, ON); + return -4; + } + gettimeofday (&tv, NULL); + prog_time = (tv.tv_sec * 1000000) + tv.tv_usec; + } while ((r != data) && + ((prog_time-start_time) < mem->max_write_delay)); + } + + /* + * At this point we either have a valid readback or the + * max_write_delay is expired. + */ + + if (r == data) { + ready = 1; + } + else if (mem->pwroff_after_write) { + /* + * The device has been flagged as power-off after write to this + * memory type. The reason we don't just blindly follow the + * flag is that the power-off advice may only apply to some + * memory bits but not all. We only actually power-off the + * device if the data read back does not match what we wrote. + */ + pgm->pgm_led(pgm, OFF); + avrdude_message(MSG_INFO, "%s: this device must be powered off and back on to continue\n", + progname); + if (pgm->pinno[PPI_AVR_VCC]) { + avrdude_message(MSG_INFO, "%s: attempting to do this now ...\n", progname); + pgm->powerdown(pgm); + usleep(250000); + rc = pgm->initialize(pgm, p); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: initialization failed, rc=%d\n", progname, rc); + avrdude_message(MSG_INFO, "%s: can't re-initialize device after programming the " + "%s bits\n", progname, mem->desc); + avrdude_message(MSG_INFO, "%s: you must manually power-down the device and restart\n" + "%s: %s to continue.\n", + progname, progname, progname); + return -3; + } + + avrdude_message(MSG_INFO, "%s: device was successfully re-initialized\n", + progname); + return 0; + } + } + + tries++; + if (!ready && tries > 5) { + /* + * we wrote the data, but after waiting for what should have + * been plenty of time, the memory cell still doesn't match what + * we wrote. Indicate a write error. + */ + pgm->pgm_led(pgm, OFF); + pgm->err_led(pgm, ON); + + return -6; + } + } + + pgm->pgm_led(pgm, OFF); + return 0; +} + + +/* + * write a byte of data at the specified address + */ +int avr_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data) +{ + + unsigned char safemode_lfuse; + unsigned char safemode_hfuse; + unsigned char safemode_efuse; + unsigned char safemode_fuse; + + /* If we write the fuses, then we need to tell safemode that they *should* change */ + safemode_memfuses(0, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse); + + if (strcmp(mem->desc, "fuse")==0) { + safemode_fuse = data; + } + if (strcmp(mem->desc, "lfuse")==0) { + safemode_lfuse = data; + } + if (strcmp(mem->desc, "hfuse")==0) { + safemode_hfuse = data; + } + if (strcmp(mem->desc, "efuse")==0) { + safemode_efuse = data; + } + + safemode_memfuses(1, &safemode_lfuse, &safemode_hfuse, &safemode_efuse, &safemode_fuse); + + return pgm->write_byte(pgm, p, mem, addr, data); +} + + +/* + * Write the whole memory region of the specified memory from the + * corresponding buffer of the avrpart pointed to by 'p'. Write up to + * 'size' bytes from the buffer. Data is only written if the new data + * value is different from the existing data value. Data beyond + * 'size' bytes is not affected. + * + * Return the number of bytes written, or -1 if an error occurs. + */ +int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, + int auto_erase) +{ + int rc; + int newpage, page_tainted, flush_page, do_write; + int wsize; + unsigned int i, lastaddr; + unsigned char data; + int werror; + unsigned char cmd[4]; + AVRMEM * m; + + m = avr_locate_mem(p, memtype); + if (m == NULL) { + avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n", + memtype, p->desc); + return -1; + } + + pgm->err_led(pgm, OFF); + + werror = 0; + + wsize = m->size; + if (size < wsize) { + wsize = size; + } + else if (size > wsize) { + avrdude_message(MSG_INFO, "%s: WARNING: %d bytes requested, but memory region is only %d" + "bytes\n" + "%sOnly %d bytes will actually be written\n", + progname, size, wsize, + progbuf, wsize); + } + + + if ((p->flags & AVRPART_HAS_TPI) && m->page_size != 0 && + pgm->cmd_tpi != NULL) { + + while (avr_tpi_poll_nvmbsy(pgm)); + + /* setup for WORD_WRITE */ + avr_tpi_setup_rw(pgm, m, 0, TPI_NVMCMD_WORD_WRITE); + + /* make sure it's aligned to a word boundary */ + if (wsize & 0x1) { + wsize++; + } + + /* write words, low byte first */ + for (lastaddr = i = 0; i < wsize; i += 2) { + RETURN_IF_CANCEL(); + if ((m->tags[i] & TAG_ALLOCATED) != 0 || + (m->tags[i + 1] & TAG_ALLOCATED) != 0) { + + if (lastaddr != i) { + /* need to setup new address */ + avr_tpi_setup_rw(pgm, m, i, TPI_NVMCMD_WORD_WRITE); + lastaddr = i; + } + + cmd[0] = TPI_CMD_SST_PI; + cmd[1] = m->buf[i]; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + cmd[1] = m->buf[i + 1]; + rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0); + + lastaddr += 2; + + while (avr_tpi_poll_nvmbsy(pgm)); + } + report_progress(i, wsize, NULL); + } + return i; + } + + if (pgm->paged_write != NULL && m->page_size != 0) { + /* + * the programmer supports a paged mode write + */ + int need_write, failure; + unsigned int pageaddr; + unsigned int npages, nwritten; + + /* quickly scan number of pages to be written to first */ + for (pageaddr = 0, npages = 0; + pageaddr < wsize; + pageaddr += m->page_size) { + /* check whether this page must be written to */ + for (i = pageaddr; + i < pageaddr + m->page_size; + i++) + if ((m->tags[i] & TAG_ALLOCATED) != 0) { + npages++; + break; + } + } + + for (pageaddr = 0, failure = 0, nwritten = 0; + !failure && pageaddr < wsize; + pageaddr += m->page_size) { + RETURN_IF_CANCEL(); + /* check whether this page must be written to */ + for (i = pageaddr, need_write = 0; + i < pageaddr + m->page_size; + i++) + if ((m->tags[i] & TAG_ALLOCATED) != 0) { + need_write = 1; + break; + } + if (need_write) { + rc = 0; + if (auto_erase) + rc = pgm->page_erase(pgm, p, m, pageaddr); + if (rc >= 0) + rc = pgm->paged_write(pgm, p, m, m->page_size, pageaddr, m->page_size); + if (rc < 0) + /* paged write failed, fall back to byte-at-a-time write below */ + failure = 1; + } else { + avrdude_message(MSG_DEBUG, "%s: avr_write(): skipping page %u: no interesting data\n", + progname, pageaddr / m->page_size); + } + nwritten++; + report_progress(nwritten, npages, NULL); + } + if (!failure) + return wsize; + /* else: fall back to byte-at-a-time write, for historical reasons */ + } + + if (pgm->write_setup) { + pgm->write_setup(pgm, p, m); + } + + newpage = 1; + page_tainted = 0; + flush_page = 0; + + for (i=0; i<wsize; i++) { + RETURN_IF_CANCEL(); + data = m->buf[i]; + report_progress(i, wsize, NULL); + + /* + * Find out whether the write action must be invoked for this + * byte. + * + * For non-paged memory, this only happens if TAG_ALLOCATED is + * set for the byte. + * + * For paged memory, TAG_ALLOCATED also invokes the write + * operation, which is actually a page buffer fill only. This + * "taints" the page, and upon encountering the last byte of each + * tainted page, the write operation must also be invoked in order + * to actually write the page buffer to memory. + */ + do_write = (m->tags[i] & TAG_ALLOCATED) != 0; + if (m->paged) { + if (newpage) { + page_tainted = do_write; + } else { + page_tainted |= do_write; + } + if (i % m->page_size == m->page_size - 1 || + i == wsize - 1) { + /* last byte this page */ + flush_page = page_tainted; + newpage = 1; + } else { + flush_page = newpage = 0; + } + } + + if (!do_write && !flush_page) { + continue; + } + + if (do_write) { + rc = avr_write_byte(pgm, p, m, i, data); + if (rc) { + avrdude_message(MSG_INFO, " ***failed; "); + avrdude_message(MSG_INFO, "\n"); + pgm->err_led(pgm, ON); + werror = 1; + } + } + + /* + * check to see if it is time to flush the page with a page + * write + */ + if (flush_page) { + rc = avr_write_page(pgm, p, m, i); + if (rc) { + avrdude_message(MSG_INFO, " *** page %d (addresses 0x%04x - 0x%04x) failed " + "to write\n", + i % m->page_size, + i - m->page_size + 1, i); + avrdude_message(MSG_INFO, "\n"); + pgm->err_led(pgm, ON); + werror = 1; + } + } + + if (werror) { + /* + * make sure the error led stay on if there was a previous write + * error, otherwise it gets cleared in avr_write_byte() + */ + pgm->err_led(pgm, ON); + return -1; + } + } + + return i; +} + + + +/* + * read the AVR device's signature bytes + */ +int avr_signature(PROGRAMMER * pgm, AVRPART * p) +{ + int rc; + + report_progress(0,1,"Reading"); + rc = avr_read(pgm, p, "signature", 0); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: error reading signature data for part \"%s\", rc=%d\n", + progname, p->desc, rc); + return -1; + } + report_progress(1,1,NULL); + + return 0; +} + + +/* + * Verify the memory buffer of p with that of v. The byte range of v, + * may be a subset of p. The byte range of p should cover the whole + * chip's memory size. + * + * Return the number of bytes verified, or -1 if they don't match. + */ +int avr_verify(AVRPART * p, AVRPART * v, char * memtype, int size) +{ + int i; + unsigned char * buf1, * buf2; + int vsize; + AVRMEM * a, * b; + + a = avr_locate_mem(p, memtype); + if (a == NULL) { + avrdude_message(MSG_INFO, "avr_verify(): memory type \"%s\" not defined for part %s\n", + memtype, p->desc); + return -1; + } + + b = avr_locate_mem(v, memtype); + if (b == NULL) { + avrdude_message(MSG_INFO, "avr_verify(): memory type \"%s\" not defined for part %s\n", + memtype, v->desc); + return -1; + } + + buf1 = a->buf; + buf2 = b->buf; + vsize = a->size; + + if (vsize < size) { + avrdude_message(MSG_INFO, "%s: WARNING: requested verification for %d bytes\n" + "%s%s memory region only contains %d bytes\n" + "%sOnly %d bytes will be verified.\n", + progname, size, + progbuf, memtype, vsize, + progbuf, vsize); + size = vsize; + } + + for (i=0; i<size; i++) { + RETURN_IF_CANCEL(); + if ((b->tags[i] & TAG_ALLOCATED) != 0 && + buf1[i] != buf2[i]) { + avrdude_message(MSG_INFO, "%s: verification error, first mismatch at byte 0x%04x\n" + "%s0x%02x != 0x%02x\n", + progname, i, + progbuf, buf1[i], buf2[i]); + return -1; + } + } + + return size; +} + + +int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles) +{ + AVRMEM * a; + unsigned int cycle_count = 0; + unsigned char v1; + int rc; + int i; + + a = avr_locate_mem(p, "eeprom"); + if (a == NULL) { + return -1; + } + + for (i=4; i>0; i--) { + rc = pgm->read_byte(pgm, p, a, a->size-i, &v1); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: WARNING: can't read memory for cycle count, rc=%d\n", + progname, rc); + return -1; + } + cycle_count = (cycle_count << 8) | v1; + } + + /* + * If the EEPROM is erased, the cycle count reads 0xffffffff. + * In this case we return a cycle_count of zero. + * So, the calling function don't have to care about whether or not + * the cycle count was initialized. + */ + if (cycle_count == 0xffffffff) { + cycle_count = 0; + } + + *cycles = (int) cycle_count; + + return 0; +} + + +int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles) +{ + AVRMEM * a; + unsigned char v1; + int rc; + int i; + + a = avr_locate_mem(p, "eeprom"); + if (a == NULL) { + return -1; + } + + for (i=1; i<=4; i++) { + v1 = cycles & 0xff; + cycles = cycles >> 8; + + rc = avr_write_byte(pgm, p, a, a->size-i, v1); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: WARNING: can't write memory for cycle count, rc=%d\n", + progname, rc); + return -1; + } + } + + return 0; + } + +int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + int rc; + + rc = pgm->chip_erase(pgm, p); + + return rc; +} + +/* + * Report the progress of a read or write operation from/to the + * device. + * + * The first call of report_progress() should look like this (for a write op): + * + * report_progress (0, 1, "Writing"); + * + * Then hdr should be passed NULL on subsequent calls while the + * operation is progressing. Once the operation is complete, a final + * call should be made as such to ensure proper termination of the + * progress report: + * + * report_progress (1, 1, NULL); + * + * It would be nice if we could reduce the usage to one and only one + * call for each of start, during and end cases. As things stand now, + * that is not possible and makes maintenance a bit more work. + */ +void report_progress (int completed, int total, char *hdr) +{ + static int last = 0; + static double start_time; + int percent = (total > 0) ? ((completed * 100) / total) : 100; + struct timeval tv; + double t; + + if (update_progress == NULL) + return; + + gettimeofday(&tv, NULL); + t = tv.tv_sec + ((double)tv.tv_usec)/1000000; + + if (hdr) { + last = 0; + start_time = t; + update_progress (percent, t - start_time, hdr); + } + + if (percent > 100) + percent = 100; + + if (percent > last) { + last = percent; + update_progress (percent, t - start_time, hdr); + } + + if (percent == 100) + last = 0; /* Get ready for next time. */ +} |