diff options
Diffstat (limited to 'src/avrdude/jtagmkI.c')
-rw-r--r-- | src/avrdude/jtagmkI.c | 1365 |
1 files changed, 1365 insertions, 0 deletions
diff --git a/src/avrdude/jtagmkI.c b/src/avrdude/jtagmkI.c new file mode 100644 index 000000000..2a5f27e4a --- /dev/null +++ b/src/avrdude/jtagmkI.c @@ -0,0 +1,1365 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2005, 2007 Joerg Wunsch <j@uriah.heep.sax.de> + * + * 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$ */ + +/* + * avrdude interface for Atmel JTAG ICE (mkI) programmer + */ + +#include "ac_cfg.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> + +#include "avrdude.h" +#include "libavrdude.h" + +#include "crc16.h" +#include "jtagmkI.h" +#include "jtagmkI_private.h" + +/* + * Private data for this programmer. + */ +struct pdata +{ + int initial_baudrate; + + /* + * See jtagmkI_read_byte() for an explanation of the flash and + * EEPROM page caches. + */ + unsigned char *flash_pagecache; + unsigned long flash_pageaddr; + unsigned int flash_pagesize; + + unsigned char *eeprom_pagecache; + unsigned long eeprom_pageaddr; + unsigned int eeprom_pagesize; + + int prog_enabled; /* Cached value of PROGRAMMING status. */ +}; + +#define PDATA(pgm) ((struct pdata *)(pgm->cookie)) + +/* + * The OCDEN fuse is bit 7 of the high fuse (hfuse). In order to + * perform memory operations on MTYPE_SPM and MTYPE_EEPROM, OCDEN + * needs to be programmed. + * + * OCDEN should probably rather be defined via the configuration, but + * if this ever changes to a different fuse byte for one MCU, quite + * some code here needs to be generalized anyway. + */ +#define OCDEN (1 << 7) + +/* + * Table of baud rates supported by the mkI ICE, accompanied by their + * internal parameter value. + * + * 19200 is the initial value of the ICE after powerup, and virtually + * all connections then switch to 115200. As the table is also used + * to try connecting at startup, we keep these two entries on top to + * speedup the program start. + */ +const static struct { + long baud; + unsigned char val; +} baudtab[] = { + { 19200L, 0xfa }, + { 115200L, 0xff }, + { 9600L, 0xf4 }, + { 38400L, 0xfd }, + { 57600L, 0xfe }, +/* { 14400L, 0xf8 }, */ /* not supported by serial driver */ +}; + +static int jtagmkI_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char * value); +static int jtagmkI_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data); +static int jtagmkI_set_sck_period(PROGRAMMER * pgm, double v); +static int jtagmkI_getparm(PROGRAMMER * pgm, unsigned char parm, + unsigned char * value); +static int jtagmkI_setparm(PROGRAMMER * pgm, unsigned char parm, + unsigned char value); +static void jtagmkI_print_parms1(PROGRAMMER * pgm, const char * p); + +static int jtagmkI_resync(PROGRAMMER *pgm, int maxtries, int signon); + +static void jtagmkI_setup(PROGRAMMER * pgm) +{ + if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_setup(): Out of memory allocating private data\n", + progname); + exit(1); + } + memset(pgm->cookie, 0, sizeof(struct pdata)); +} + +static void jtagmkI_teardown(PROGRAMMER * pgm) +{ + free(pgm->cookie); +} + + +static void +u32_to_b3(unsigned char *b, unsigned long l) +{ + b[2] = l & 0xff; + b[1] = (l >> 8) & 0xff; + b[0] = (l >> 16) & 0xff; +} + +static void +u16_to_b2(unsigned char *b, unsigned short l) +{ + b[0] = l & 0xff; + b[1] = (l >> 8) & 0xff; +} + +static void jtagmkI_prmsg(PROGRAMMER * pgm, unsigned char * data, size_t len) +{ + int i; + + if (verbose >= 4) { + avrdude_message(MSG_TRACE, "Raw message:\n"); + + for (i = 0; i < len; i++) { + avrdude_message(MSG_TRACE, "0x%02x ", data[i]); + if (i % 16 == 15) + putc('\n', stderr); + else + putc(' ', stderr); + } + if (i % 16 != 0) + putc('\n', stderr); + } + + switch (data[0]) { + case RESP_OK: + avrdude_message(MSG_INFO, "OK\n"); + break; + + case RESP_FAILED: + avrdude_message(MSG_INFO, "FAILED\n"); + break; + + case RESP_BREAK: + avrdude_message(MSG_INFO, "breakpoint hit\n"); + break; + + case RESP_INFO: + avrdude_message(MSG_INFO, "IDR dirty\n"); + break; + + case RESP_SYNC_ERROR: + avrdude_message(MSG_INFO, "Synchronization lost\n"); + break; + + case RESP_SLEEP: + avrdude_message(MSG_INFO, "sleep instruction hit\n"); + break; + + case RESP_POWER: + avrdude_message(MSG_INFO, "target power lost\n"); + + default: + avrdude_message(MSG_INFO, "unknown message 0x%02x\n", data[0]); + } + + putc('\n', stderr); +} + + +static int jtagmkI_send(PROGRAMMER * pgm, unsigned char * data, size_t len) +{ + unsigned char *buf; + + avrdude_message(MSG_DEBUG, "\n%s: jtagmkI_send(): sending %u bytes\n", + progname, (unsigned int)len); + + if ((buf = malloc(len + 2)) == NULL) + { + avrdude_message(MSG_INFO, "%s: jtagmkI_send(): out of memory", + progname); + exit(1); + } + + memcpy(buf, data, len); + buf[len] = ' '; /* "CRC" */ + buf[len + 1] = ' '; /* EOP */ + + if (serial_send(&pgm->fd, buf, len + 2) != 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_send(): failed to send command to serial port\n", + progname); + return -1; + } + + free(buf); + + return 0; +} + +static int jtagmkI_recv(PROGRAMMER * pgm, unsigned char * buf, size_t len) +{ + if (serial_recv(&pgm->fd, buf, len) != 0) { + avrdude_message(MSG_INFO, "\n%s: jtagmkI_recv(): failed to send command to serial port\n", + progname); + return -1; + } + if (verbose >= 3) { + putc('\n', stderr); + jtagmkI_prmsg(pgm, buf, len); + } + return 0; +} + + +static int jtagmkI_drain(PROGRAMMER * pgm, int display) +{ + return serial_drain(&pgm->fd, display); +} + + +static int jtagmkI_resync(PROGRAMMER * pgm, int maxtries, int signon) +{ + int tries; + unsigned char buf[4], resp[9]; + long otimeout = serial_recv_timeout; + + serial_recv_timeout = 200; + + avrdude_message(MSG_TRACE, "%s: jtagmkI_resync()\n", progname); + + jtagmkI_drain(pgm, 0); + + for (tries = 0; tries < maxtries; tries++) { + + /* Get the sign-on information. */ + buf[0] = CMD_GET_SYNC; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_resync(): Sending sync command: ", + progname); + + if (serial_send(&pgm->fd, buf, 1) != 0) { + avrdude_message(MSG_INFO, "\n%s: jtagmkI_resync(): failed to send command to serial port\n", + progname); + serial_recv_timeout = otimeout; + return -1; + } + if (serial_recv(&pgm->fd, resp, 1) == 0 && resp[0] == RESP_OK) { + avrdude_message(MSG_NOTICE2, "got RESP_OK\n"); + break; + } + + if (signon) { + /* + * The following is black magic, the idea has been taken from + * AVaRICE. + * + * Apparently, the ICE behaves differently right after a + * power-up vs. when reconnecting to an ICE that has already + * been worked with. The undocumented 'E' command (or + * subcommand) occasionally helps in getting the connection into + * sync. + */ + buf[0] = CMD_GET_SIGNON; + buf[1] = 'E'; + buf[2] = ' '; + buf[3] = ' '; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_resync(): Sending sign-on command: ", + progname); + + if (serial_send(&pgm->fd, buf, 4) != 0) { + avrdude_message(MSG_INFO, "\n%s: jtagmkI_resync(): failed to send command to serial port\n", + progname); + serial_recv_timeout = otimeout; + return -1; + } + if (serial_recv(&pgm->fd, resp, 9) == 0 && resp[0] == RESP_OK) { + avrdude_message(MSG_NOTICE2, "got RESP_OK\n"); + break; + } + } + } + if (tries >= maxtries) { + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_resync(): " + "timeout/error communicating with programmer\n", + progname); + serial_recv_timeout = otimeout; + return -1; + } + + serial_recv_timeout = otimeout; + return 0; +} + +static int jtagmkI_getsync(PROGRAMMER * pgm) +{ + unsigned char buf[1], resp[9]; + + if (jtagmkI_resync(pgm, 5, 1) < 0) { + jtagmkI_drain(pgm, 0); + return -1; + } + + jtagmkI_drain(pgm, 0); + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_getsync(): Sending sign-on command: ", + progname); + + buf[0] = CMD_GET_SIGNON; + jtagmkI_send(pgm, buf, 1); + if (jtagmkI_recv(pgm, resp, 9) < 0) + return -1; + if (verbose >= 2) { + resp[8] = '\0'; + avrdude_message(MSG_NOTICE2, "got %s\n", resp + 1); + } + + return 0; +} + +/* + * issue the 'chip erase' command to the AVR device + */ +static int jtagmkI_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char buf[1], resp[2]; + + buf[0] = CMD_CHIP_ERASE; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_chip_erase(): Sending chip erase command: ", + progname); + jtagmkI_send(pgm, buf, 1); + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_chip_erase(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + pgm->initialize(pgm, p); + + return 0; +} + +static void jtagmkI_set_devdescr(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char resp[2]; + LNODEID ln; + AVRMEM * m; + struct { + unsigned char cmd; + struct device_descriptor dd; + } sendbuf; + + memset(&sendbuf, 0, sizeof sendbuf); + sendbuf.cmd = CMD_SET_DEVICE_DESCRIPTOR; + sendbuf.dd.ucSPMCRAddress = p->spmcr; + sendbuf.dd.ucRAMPZAddress = p->rampz; + sendbuf.dd.ucIDRAddress = p->idr; + for (ln = lfirst(p->mem); ln; ln = lnext(ln)) { + m = ldata(ln); + if (strcmp(m->desc, "flash") == 0) { + PDATA(pgm)->flash_pagesize = m->page_size; + u16_to_b2(sendbuf.dd.uiFlashPageSize, PDATA(pgm)->flash_pagesize); + } else if (strcmp(m->desc, "eeprom") == 0) { + sendbuf.dd.ucEepromPageSize = PDATA(pgm)->eeprom_pagesize = m->page_size; + } + } + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_set_devdescr(): " + "Sending set device descriptor command: ", + progname); + jtagmkI_send(pgm, (unsigned char *)&sendbuf, sizeof(sendbuf)); + + if (jtagmkI_recv(pgm, resp, 2) < 0) + return; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_set_devdescr(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } +} + +/* + * Reset the target. + */ +static int jtagmkI_reset(PROGRAMMER * pgm) +{ + unsigned char buf[1], resp[2]; + + buf[0] = CMD_RESET; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_reset(): Sending reset command: ", + progname); + jtagmkI_send(pgm, buf, 1); + + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_reset(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + return 0; +} + +static int jtagmkI_program_enable_dummy(PROGRAMMER * pgm, AVRPART * p) +{ + + return 0; +} + +static int jtagmkI_program_enable(PROGRAMMER * pgm) +{ + unsigned char buf[1], resp[2]; + + if (PDATA(pgm)->prog_enabled) + return 0; + + buf[0] = CMD_ENTER_PROGMODE; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_program_enable(): " + "Sending enter progmode command: ", + progname); + jtagmkI_send(pgm, buf, 1); + + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_program_enable(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + PDATA(pgm)->prog_enabled = 1; + + return 0; +} + +static int jtagmkI_program_disable(PROGRAMMER * pgm) +{ + unsigned char buf[1], resp[2]; + + if (!PDATA(pgm)->prog_enabled) + return 0; + + if (pgm->fd.ifd != -1) { + buf[0] = CMD_LEAVE_PROGMODE; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_program_disable(): " + "Sending leave progmode command: ", + progname); + jtagmkI_send(pgm, buf, 1); + + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_program_disable(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + } + PDATA(pgm)->prog_enabled = 0; + + return 0; +} + +static unsigned char jtagmkI_get_baud(long baud) +{ + int i; + + for (i = 0; i < sizeof baudtab / sizeof baudtab[0]; i++) + if (baud == baudtab[i].baud) + return baudtab[i].val; + + return 0; +} + +/* + * initialize the AVR device and prepare it to accept commands + */ +static int jtagmkI_initialize(PROGRAMMER * pgm, AVRPART * p) +{ + AVRMEM hfuse; + unsigned char cmd[1], resp[5]; + unsigned char b; + + if (!(p->flags & AVRPART_HAS_JTAG)) { + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): part %s has no JTAG interface\n", + progname, p->desc); + return -1; + } + + jtagmkI_drain(pgm, 0); + + if ((serdev->flags & SERDEV_FL_CANSETSPEED) && PDATA(pgm)->initial_baudrate != pgm->baudrate) { + if ((b = jtagmkI_get_baud(pgm->baudrate)) == 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): unsupported baudrate %d\n", + progname, pgm->baudrate); + } else { + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_initialize(): " + "trying to set baudrate to %d\n", + progname, pgm->baudrate); + if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) { + PDATA(pgm)->initial_baudrate = pgm->baudrate; /* don't adjust again later */ + serial_setspeed(&pgm->fd, pgm->baudrate); + } + } + } + + if (pgm->bitclock != 0.0) { + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_initialize(): " + "trying to set JTAG clock period to %.1f us\n", + progname, pgm->bitclock); + if (jtagmkI_set_sck_period(pgm, pgm->bitclock) != 0) + return -1; + } + + cmd[0] = CMD_STOP; + jtagmkI_send(pgm, cmd, 1); + if (jtagmkI_recv(pgm, resp, 5) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + /* + * Must set the device descriptor before entering programming mode. + */ + jtagmkI_set_devdescr(pgm, p); + + jtagmkI_setparm(pgm, PARM_FLASH_PAGESIZE_LOW, PDATA(pgm)->flash_pagesize & 0xff); + jtagmkI_setparm(pgm, PARM_FLASH_PAGESIZE_HIGH, PDATA(pgm)->flash_pagesize >> 8); + jtagmkI_setparm(pgm, PARM_EEPROM_PAGESIZE, PDATA(pgm)->eeprom_pagesize & 0xff); + + free(PDATA(pgm)->flash_pagecache); + free(PDATA(pgm)->eeprom_pagecache); + if ((PDATA(pgm)->flash_pagecache = malloc(PDATA(pgm)->flash_pagesize)) == NULL) { + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): Out of memory\n", + progname); + return -1; + } + if ((PDATA(pgm)->eeprom_pagecache = malloc(PDATA(pgm)->eeprom_pagesize)) == NULL) { + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): Out of memory\n", + progname); + free(PDATA(pgm)->flash_pagecache); + return -1; + } + PDATA(pgm)->flash_pageaddr = PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; + + if (jtagmkI_reset(pgm) < 0) + return -1; + + strcpy(hfuse.desc, "hfuse"); + if (jtagmkI_read_byte(pgm, p, &hfuse, 1, &b) < 0) + return -1; + if ((b & OCDEN) != 0) + avrdude_message(MSG_INFO, "%s: jtagmkI_initialize(): warning: OCDEN fuse not programmed, " + "single-byte EEPROM updates not possible\n", + progname); + + return 0; +} + + +static void jtagmkI_disable(PROGRAMMER * pgm) +{ + + free(PDATA(pgm)->flash_pagecache); + PDATA(pgm)->flash_pagecache = NULL; + free(PDATA(pgm)->eeprom_pagecache); + PDATA(pgm)->eeprom_pagecache = NULL; + + (void)jtagmkI_program_disable(pgm); +} + +static void jtagmkI_enable(PROGRAMMER * pgm) +{ + return; +} + + +static int jtagmkI_open(PROGRAMMER * pgm, char * port) +{ + size_t i; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open()\n", progname); + + strcpy(pgm->port, port); + PDATA(pgm)->initial_baudrate = -1L; + + for (i = 0; i < sizeof(baudtab) / sizeof(baudtab[0]); i++) { + union pinfo pinfo; + pinfo.baud = baudtab[i].baud; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open(): trying to sync at baud rate %ld:\n", + progname, pinfo.baud); + if (serial_open(port, pinfo, &pgm->fd)==-1) { + return -1; + } + + /* + * drain any extraneous input + */ + jtagmkI_drain(pgm, 0); + + if (jtagmkI_getsync(pgm) == 0) { + PDATA(pgm)->initial_baudrate = baudtab[i].baud; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open(): succeeded\n", progname); + return 0; + } + + serial_close(&pgm->fd); + } + + avrdude_message(MSG_INFO, "%s: jtagmkI_open(): failed to synchronize to ICE\n", + progname); + pgm->fd.ifd = -1; + + return -1; +} + + +static void jtagmkI_close(PROGRAMMER * pgm) +{ + unsigned char b; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_close()\n", progname); + + /* + * Revert baud rate to what it used to be when we started. This + * appears to make AVR Studio happier when it is about to access the + * ICE later on. + */ + if ((serdev->flags & SERDEV_FL_CANSETSPEED) && PDATA(pgm)->initial_baudrate != pgm->baudrate) { + if ((b = jtagmkI_get_baud(PDATA(pgm)->initial_baudrate)) == 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_close(): unsupported baudrate %d\n", + progname, PDATA(pgm)->initial_baudrate); + } else { + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_close(): " + "trying to set baudrate to %d\n", + progname, PDATA(pgm)->initial_baudrate); + if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) { + serial_setspeed(&pgm->fd, pgm->baudrate); + } + } + } + + if (pgm->fd.ifd != -1) { + serial_close(&pgm->fd); + } + + pgm->fd.ifd = -1; +} + + +static int jtagmkI_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + int block_size, send_size, tries; + unsigned int maxaddr = addr + n_bytes; + unsigned char cmd[6], *datacmd; + unsigned char resp[2]; + int is_flash = 0; + long otimeout = serial_recv_timeout; +#define MAXTRIES 3 + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_paged_write(.., %s, %d, %d)\n", + progname, m->desc, page_size, n_bytes); + + if (jtagmkI_program_enable(pgm) < 0) + return -1; + + if (page_size == 0) page_size = 256; + + if (page_size > 256) { + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_write(): page size %d too large\n", + progname, page_size); + return -1; + } + + if ((datacmd = malloc(page_size + 1)) == NULL) { + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_write(): Out of memory\n", + progname); + return -1; + } + + cmd[0] = CMD_WRITE_MEM; + if (strcmp(m->desc, "flash") == 0) { + cmd[1] = MTYPE_FLASH_PAGE; + PDATA(pgm)->flash_pageaddr = (unsigned long)-1L; + page_size = PDATA(pgm)->flash_pagesize; + is_flash = 1; + } else if (strcmp(m->desc, "eeprom") == 0) { + cmd[1] = MTYPE_EEPROM_PAGE; + PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; + page_size = PDATA(pgm)->eeprom_pagesize; + } + datacmd[0] = CMD_DATA; + + serial_recv_timeout = 1000; + for (; addr < maxaddr; addr += page_size) { + tries = 0; + again: + + if (tries != 0 && jtagmkI_resync(pgm, 2000, 0) < 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_write(): sync loss, retries exhausted\n", + progname); + return -1; + } + + if (n_bytes < page_size) + block_size = n_bytes; + else + block_size = page_size; + avrdude_message(MSG_DEBUG, "%s: jtagmkI_paged_write(): " + "block_size at addr %d is %d\n", + progname, addr, block_size); + + /* We always write full pages. */ + send_size = page_size; + if (is_flash) { + cmd[2] = send_size / 2 - 1; + u32_to_b3(cmd + 3, addr / 2); + } else { + cmd[2] = send_size - 1; + u32_to_b3(cmd + 3, addr); + } + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_paged_write(): " + "Sending write memory command: ", + progname); + + /* First part, send the write command. */ + jtagmkI_send(pgm, cmd, 6); + if (jtagmkI_recv(pgm, resp, 1) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_write(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + if (tries++ < MAXTRIES) + goto again; + serial_recv_timeout = otimeout; + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + /* + * The JTAG ICE will refuse to write anything but a full page, at + * least for the flash ROM. If a partial page has been requested, + * set the remainder to 0xff. (Maybe we should rather read back + * the existing contents instead before? Doesn't matter much, as + * bits cannot be written to 1 anyway.) + */ + memset(datacmd + 1, 0xff, page_size); + memcpy(datacmd + 1, m->buf + addr, block_size); + + /* Second, send the data command. */ + jtagmkI_send(pgm, datacmd, send_size + 1); + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[1] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_write(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + if (tries++ < MAXTRIES) + goto again; + serial_recv_timeout = otimeout; + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + } + + free(datacmd); + serial_recv_timeout = otimeout; + +#undef MAXTRIES + return n_bytes; +} + +static int jtagmkI_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + int block_size, read_size, is_flash = 0, tries; + unsigned int maxaddr = addr + n_bytes; + unsigned char cmd[6], resp[256 * 2 + 3]; + long otimeout = serial_recv_timeout; +#define MAXTRIES 3 + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_paged_load(.., %s, %d, %d)\n", + progname, m->desc, page_size, n_bytes); + + if (jtagmkI_program_enable(pgm) < 0) + return -1; + + page_size = m->readsize; + + cmd[0] = CMD_READ_MEM; + if (strcmp(m->desc, "flash") == 0) { + cmd[1] = MTYPE_FLASH_PAGE; + is_flash = 1; + } else if (strcmp(m->desc, "eeprom") == 0) { + cmd[1] = MTYPE_EEPROM_PAGE; + } + + if (page_size > (is_flash? 512: 256)) { + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_load(): page size %d too large\n", + progname, page_size); + return -1; + } + + serial_recv_timeout = 1000; + for (; addr < maxaddr; addr += page_size) { + tries = 0; + again: + if (tries != 0 && jtagmkI_resync(pgm, 2000, 0) < 0) { + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_load(): sync loss, retries exhausted\n", + progname); + return -1; + } + + if (n_bytes < page_size) + block_size = n_bytes; + else + block_size = page_size; + avrdude_message(MSG_DEBUG, "%s: jtagmkI_paged_load(): " + "block_size at addr %d is %d\n", + progname, addr, block_size); + + if (is_flash) { + read_size = 2 * ((block_size + 1) / 2); /* round up */ + cmd[2] = read_size / 2 - 1; + u32_to_b3(cmd + 3, addr / 2); + } else { + read_size = page_size; + cmd[2] = page_size - 1; + u32_to_b3(cmd + 3, addr); + } + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_paged_load(): Sending read memory command: ", + progname); + + jtagmkI_send(pgm, cmd, 6); + if (jtagmkI_recv(pgm, resp, read_size + 3) < 0) + return -1; + + if (resp[read_size + 3 - 1] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_paged_load(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[read_size + 3 - 1]); + if (tries++ < MAXTRIES) + goto again; + + serial_recv_timeout = otimeout; + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + memcpy(m->buf + addr, resp + 1, block_size); + } + serial_recv_timeout = otimeout; + +#undef MAXTRIES + return n_bytes; +} + +static int jtagmkI_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char * value) +{ + unsigned char cmd[6]; + unsigned char resp[256 * 2 + 3], *cache_ptr = NULL; + unsigned long paddr = 0UL, *paddr_ptr = NULL; + unsigned int pagesize = 0; + int respsize = 3 + 1; + int is_flash = 0; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_read_byte(.., %s, 0x%lx, ...)\n", + progname, mem->desc, addr); + + if (jtagmkI_program_enable(pgm) < 0) + return -1; + + cmd[0] = CMD_READ_MEM; + + if (strcmp(mem->desc, "flash") == 0) { + cmd[1] = MTYPE_FLASH_PAGE; + pagesize = mem->page_size; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &PDATA(pgm)->flash_pageaddr; + cache_ptr = PDATA(pgm)->flash_pagecache; + is_flash = 1; + } else if (strcmp(mem->desc, "eeprom") == 0) { + cmd[1] = MTYPE_EEPROM_PAGE; + pagesize = mem->page_size; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &PDATA(pgm)->eeprom_pageaddr; + cache_ptr = PDATA(pgm)->eeprom_pagecache; + } else if (strcmp(mem->desc, "lfuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 0; + } else if (strcmp(mem->desc, "hfuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 1; + } else if (strcmp(mem->desc, "efuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 2; + } else if (strcmp(mem->desc, "lock") == 0) { + cmd[1] = MTYPE_LOCK_BITS; + } else if (strcmp(mem->desc, "calibration") == 0) { + cmd[1] = MTYPE_OSCCAL_BYTE; + } else if (strcmp(mem->desc, "signature") == 0) { + cmd[1] = MTYPE_SIGN_JTAG; + } + + /* + * To improve the read speed, we used paged reads for flash and + * EEPROM, and cache the results in a page cache. + * + * Page cache validation is based on "{flash,eeprom}_pageaddr" + * (holding the base address of the most recent cache fill + * operation). This variable is set to (unsigned long)-1L when the + * cache needs to be invalidated. + */ + if (pagesize && paddr == *paddr_ptr) { + *value = cache_ptr[addr & (pagesize - 1)]; + return 0; + } + + if (pagesize) { + if (is_flash) { + cmd[2] = pagesize / 2 - 1; + u32_to_b3(cmd + 3, paddr / 2); + } else { + cmd[2] = pagesize - 1; + u32_to_b3(cmd + 3, paddr); + } + respsize = 3 + pagesize; + } else { + if (cmd[1] == MTYPE_FUSE_BITS) { + /* + * The mkI ICE has a bug where it doesn't read efuse correctly + * when reading it as a single byte @offset 2, while reading all + * fuses at once does work. + */ + cmd[2] = 3 - 1; + u32_to_b3(cmd + 3, 0); + respsize = 3 + 3; + } else { + cmd[2] = 1 - 1; + u32_to_b3(cmd + 3, addr); + } + } + + jtagmkI_send(pgm, cmd, 6); + if (jtagmkI_recv(pgm, resp, respsize) < 0) + return -1; + + if (resp[respsize - 1] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_read_byte(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[respsize - 1]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + if (pagesize) { + *paddr_ptr = paddr; + memcpy(cache_ptr, resp + 1, pagesize); + *value = cache_ptr[addr & (pagesize - 1)]; + } else if (cmd[1] == MTYPE_FUSE_BITS) { + /* extract the desired fuse */ + *value = resp[1 + addr]; + } else + *value = resp[1]; + + return 0; +} + +static int jtagmkI_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data) +{ + unsigned char cmd[6], datacmd[1 * 2 + 1]; + unsigned char resp[1], writedata; + int len, need_progmode = 1; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_write_byte(.., %s, 0x%lx, ...)\n", + progname, mem->desc, addr); + + writedata = data; + cmd[0] = CMD_WRITE_MEM; + if (strcmp(mem->desc, "flash") == 0) { + cmd[1] = MTYPE_SPM; + need_progmode = 0; + PDATA(pgm)->flash_pageaddr = (unsigned long)-1L; + } else if (strcmp(mem->desc, "eeprom") == 0) { + cmd[1] = MTYPE_EEPROM; + need_progmode = 0; + PDATA(pgm)->eeprom_pageaddr = (unsigned long)-1L; + } else if (strcmp(mem->desc, "lfuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 0; + } else if (strcmp(mem->desc, "hfuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 1; + } else if (strcmp(mem->desc, "efuse") == 0) { + cmd[1] = MTYPE_FUSE_BITS; + addr = 2; + } else if (strcmp(mem->desc, "lock") == 0) { + cmd[1] = MTYPE_LOCK_BITS; + } else if (strcmp(mem->desc, "calibration") == 0) { + cmd[1] = MTYPE_OSCCAL_BYTE; + } else if (strcmp(mem->desc, "signature") == 0) { + cmd[1] = MTYPE_SIGN_JTAG; + } + + if (need_progmode) { + if (jtagmkI_program_enable(pgm) < 0) + return -1; + } else { + if (jtagmkI_program_disable(pgm) < 0) + return -1; + } + + cmd[2] = 1 - 1; + if (cmd[1] == MTYPE_SPM) { + /* + * Flash is word-addressed, but we cannot handle flash anyway + * here, as it needs to be written one page at a time... + */ + u32_to_b3(cmd + 3, addr / 2); + } else { + u32_to_b3(cmd + 3, addr); + } + /* First part, send the write command. */ + jtagmkI_send(pgm, cmd, 6); + if (jtagmkI_recv(pgm, resp, 1) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_write_byte(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + /* Now, send the data buffer. */ + datacmd[0] = CMD_DATA; + if (cmd[1] == MTYPE_SPM) { + len = 3; + if ((addr & 1) != 0) { + datacmd[1] = 0; + datacmd[2] = writedata; + } else { + datacmd[1] = writedata; + datacmd[2] = 0; + } + } else { + len = 2; + datacmd[1] = writedata; + } + jtagmkI_send(pgm, datacmd, len); + if (jtagmkI_recv(pgm, resp, 1) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_write_byte(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + return 0; +} + + +/* + * Set the JTAG clock. The actual frequency is quite a bit of + * guesswork, based on the values claimed by AVR Studio. Inside the + * JTAG ICE, the value is the delay count of a delay loop between the + * JTAG clock edges. A count of 0 bypasses the delay loop. + * + * As the STK500 expresses it as a period length (and we actualy do + * program a period length as well), we rather call it by that name. + */ +static int jtagmkI_set_sck_period(PROGRAMMER * pgm, double v) +{ + unsigned char dur; + + v = 1 / v; /* convert to frequency */ + if (v >= 1e6) + dur = JTAG_BITRATE_1_MHz; + else if (v >= 499e3) + dur = JTAG_BITRATE_500_kHz; + else if (v >= 249e3) + dur = JTAG_BITRATE_250_kHz; + else + dur = JTAG_BITRATE_125_kHz; + + return jtagmkI_setparm(pgm, PARM_CLOCK, dur); +} + + +/* + * Read an emulator parameter. The result is exactly one byte, + * multi-byte parameters get two different parameter names for + * their components. + */ +static int jtagmkI_getparm(PROGRAMMER * pgm, unsigned char parm, + unsigned char * value) +{ + unsigned char buf[2], resp[3]; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_getparm()\n", progname); + + buf[0] = CMD_GET_PARAM; + buf[1] = parm; + if (verbose >= 2) + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_getparm(): " + "Sending get parameter command (parm 0x%02x): ", + progname, parm); + jtagmkI_send(pgm, buf, 2); + + if (jtagmkI_recv(pgm, resp, 3) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_getparm(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else if (resp[2] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_getparm(): " + "unknown parameter 0x%02x\n", + progname, parm); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK, value 0x%02x\n", resp[1]); + } + + *value = resp[1]; + + return 0; +} + +/* + * Write an emulator parameter. + */ +static int jtagmkI_setparm(PROGRAMMER * pgm, unsigned char parm, + unsigned char value) +{ + unsigned char buf[3], resp[2]; + + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_setparm()\n", progname); + + buf[0] = CMD_SET_PARAM; + buf[1] = parm; + buf[2] = value; + avrdude_message(MSG_NOTICE2, "%s: jtagmkI_setparm(): " + "Sending set parameter command (parm 0x%02x): ", + progname, parm); + jtagmkI_send(pgm, buf, 3); + if (jtagmkI_recv(pgm, resp, 2) < 0) + return -1; + if (resp[0] != RESP_OK) { + if (verbose >= 2) + putc('\n', stderr); + avrdude_message(MSG_INFO, "%s: jtagmkI_setparm(): " + "timeout/error communicating with programmer (resp %c)\n", + progname, resp[0]); + return -1; + } else { + if (verbose == 2) + avrdude_message(MSG_NOTICE2, "OK\n"); + } + + return 0; +} + + +static void jtagmkI_display(PROGRAMMER * pgm, const char * p) +{ + + unsigned char hw, fw; + + if (jtagmkI_getparm(pgm, PARM_HW_VERSION, &hw) < 0 || + jtagmkI_getparm(pgm, PARM_SW_VERSION, &fw) < 0) + return; + + avrdude_message(MSG_INFO, "%sICE hardware version: 0x%02x\n", p, hw); + avrdude_message(MSG_INFO, "%sICE firmware version: 0x%02x\n", p, fw); + + jtagmkI_print_parms1(pgm, p); + + return; +} + + +static void jtagmkI_print_parms1(PROGRAMMER * pgm, const char * p) +{ + unsigned char vtarget, jtag_clock; + const char *clkstr; + double clk; + + if (jtagmkI_getparm(pgm, PARM_OCD_VTARGET, &vtarget) < 0 || + jtagmkI_getparm(pgm, PARM_CLOCK, &jtag_clock) < 0) + return; + + switch ((unsigned)jtag_clock) { + case JTAG_BITRATE_1_MHz: + clkstr = "1 MHz"; + clk = 1e6; + break; + + case JTAG_BITRATE_500_kHz: + clkstr = "500 kHz"; + clk = 500e3; + break; + + case JTAG_BITRATE_250_kHz: + clkstr = "250 kHz"; + clk = 250e3; + break; + + case JTAG_BITRATE_125_kHz: + clkstr = "125 kHz"; + clk = 125e3; + break; + + default: + clkstr = "???"; + clk = 1e6; + } + + avrdude_message(MSG_INFO, "%sVtarget : %.1f V\n", p, + 6.25 * (unsigned)vtarget / 255.0); + avrdude_message(MSG_INFO, "%sJTAG clock : %s (%.1f us)\n", p, clkstr, + 1.0e6 / clk); + + return; +} + + +static void jtagmkI_print_parms(PROGRAMMER * pgm) +{ + jtagmkI_print_parms1(pgm, ""); +} + +const char jtagmkI_desc[] = "Atmel JTAG ICE mkI"; + +void jtagmkI_initpgm(PROGRAMMER * pgm) +{ + strcpy(pgm->type, "JTAGMKI"); + + /* + * mandatory functions + */ + pgm->initialize = jtagmkI_initialize; + pgm->display = jtagmkI_display; + pgm->enable = jtagmkI_enable; + pgm->disable = jtagmkI_disable; + pgm->program_enable = jtagmkI_program_enable_dummy; + pgm->chip_erase = jtagmkI_chip_erase; + pgm->open = jtagmkI_open; + pgm->close = jtagmkI_close; + pgm->read_byte = jtagmkI_read_byte; + pgm->write_byte = jtagmkI_write_byte; + + /* + * optional functions + */ + pgm->paged_write = jtagmkI_paged_write; + pgm->paged_load = jtagmkI_paged_load; + pgm->print_parms = jtagmkI_print_parms; + pgm->set_sck_period = jtagmkI_set_sck_period; + pgm->setup = jtagmkI_setup; + pgm->teardown = jtagmkI_teardown; + pgm->page_size = 256; +} |