From 3a4b48b583a98a8b9b12e725b669e4097eea24ef Mon Sep 17 00:00:00 2001
From: Joerg Wunsch <j@uriah.heep.sax.de>
Date: Fri, 13 Apr 2012 15:25:41 +0000
Subject: [PATCH] bug #28744: Can't load bootloader to xmega128a1 (part 2, fix
 for firmware >= V7.x) * jtagmkII.c: Add firmware-version dependent handling
 of Xmega parameters. V7.x firmware expects the NVM offsets being specified
 through the Xmega parameters command, but left out as part of the memory
 address itself. * jtagmkII_private.h: Add CMND_SET_XMEGA_PARAMS, and struct
 xmega_device_desc. * config_gram.y: Add mcu_base keyword. * avrpart.h:
 (Dito.) * lexer.l: (Dito.) * avrdude.conf.in (.xmega): add mcu_base, and data
 memory segment.

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1078 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 ChangeLog          |  13 +++++
 NEWS               |   1 +
 avrdude.conf.in    |   6 +++
 avrpart.h          |   1 +
 config_gram.y      |   7 +++
 jtagmkII.c         | 129 ++++++++++++++++++++++++++++++++++++++++++---
 jtagmkII_private.h |  22 ++++++++
 lexer.l            |   1 +
 8 files changed, 174 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 685fbe0e..37fac2da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2012-04-13  Joerg Wunsch <j.gnu@uriah.heep.sax.de>
+
+	bug #28744: Can't load bootloader to xmega128a1 (part 2, fix for
+	firmware >= V7.x)
+	* jtagmkII.c: Add firmware-version dependent handling of Xmega parameters.
+	V7.x firmware expects the NVM offsets being specified through the Xmega
+	parameters command, but left out as part of the memory address itself.
+	* jtagmkII_private.h: Add CMND_SET_XMEGA_PARAMS, and struct xmega_device_desc.
+	* config_gram.y: Add mcu_base keyword.
+	* avrpart.h: (Dito.)
+	* lexer.l: (Dito.)
+	* avrdude.conf.in (.xmega): add mcu_base, and data memory segment.
+
 2012-03-30  Joerg Wunsch <j.gnu@uriah.heep.sax.de>
 
 	bug #28744: Can't load bootloader to xmega128a1 (part 1, fix for
diff --git a/NEWS b/NEWS
index 053c094b..818a3664 100644
--- a/NEWS
+++ b/NEWS
@@ -57,6 +57,7 @@ Current:
       - bug #34027: avrdude AT90S1200 Problem
       - bug #30451: Accessing some Xmega memory sections gives not
         supported error
+      - bug #28744: Can't load bootloader to xmega128a1
 
   * Keep track of input file contents
 
diff --git a/avrdude.conf.in b/avrdude.conf.in
index e43d007b..7a6c99de 100644
--- a/avrdude.conf.in
+++ b/avrdude.conf.in
@@ -12286,6 +12286,7 @@ part
     has_jtag	= yes;
     has_pdi	= yes;
     nvm_base	= 0x01c0;
+    mcu_base	= 0x0090;
 
     memory "prodsig"
         size		= 0x200;
@@ -12335,6 +12336,11 @@ part
         size		= 1;
         offset		= 0x8f0027;
     ;
+
+    memory "data"
+        # SRAM, only used to supply the offset
+        offset		= 0x1000000;
+    ;
 ;
 
 
diff --git a/avrpart.h b/avrpart.h
index 90a6f267..d097aac2 100644
--- a/avrpart.h
+++ b/avrpart.h
@@ -156,6 +156,7 @@ typedef struct avrpart {
   unsigned char rampz;              /* JTAG ICE mkII XML file parameter */
   unsigned char spmcr;              /* JTAG ICE mkII XML file parameter */
   unsigned short eecr;              /* JTAC ICE mkII XML file parameter */
+  unsigned int mcu_base;            /* Base address of MCU control block in ATxmega devices */
   unsigned int nvm_base;            /* Base address of NVM controller in ATxmega devices */
 
   OPCODE      * op[AVR_OP_MAX];     /* opcodes */
diff --git a/config_gram.y b/config_gram.y
index 1c1c79bb..d4fa3511 100644
--- a/config_gram.y
+++ b/config_gram.y
@@ -93,6 +93,7 @@ static int pin_name;
 %token K_IO
 %token K_LOADPAGE
 %token K_MAX_WRITE_DELAY
+%token K_MCU_BASE
 %token K_MIN_WRITE_DELAY
 %token K_MISO
 %token K_MOSI
@@ -1079,6 +1080,12 @@ part_parm :
       free_token($3);
     } |
 
+  K_MCU_BASE TKN_EQUAL TKN_NUMBER
+    {
+      current_part->mcu_base = $3->value.number;
+      free_token($3);
+    } |
+
   K_NVM_BASE TKN_EQUAL TKN_NUMBER
     {
       current_part->nvm_base = $3->value.number;
diff --git a/jtagmkII.c b/jtagmkII.c
index 66ff1ca9..b3ee7cd2 100644
--- a/jtagmkII.c
+++ b/jtagmkII.c
@@ -82,6 +82,9 @@ struct pdata
 
   /* Start address of Xmega boot area */
   unsigned long boot_start;
+
+  /* Major firmware version (needed for Xmega programming) */
+  unsigned int fwver;
 };
 
 #define PDATA(pgm) ((struct pdata *)(pgm->cookie))
@@ -143,6 +146,7 @@ static int jtagmkII_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
                                 unsigned int page_size,
                                 unsigned int addr, unsigned int n_bytes);
 static unsigned char jtagmkII_memtype(PROGRAMMER * pgm, AVRPART * p, unsigned long addr);
+static unsigned int jtagmkII_memaddr(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr);
 
 // AVR32
 #define ERROR_SAB 0xFFFFFFFF
@@ -724,6 +728,7 @@ int jtagmkII_getsync(PROGRAMMER * pgm, int mode) {
     if (status > 0) {
       if ((c = resp[0]) == RSP_SIGN_ON) {
 	fwver = ((unsigned)resp[8] << 8) | (unsigned)resp[7];
+        PDATA(pgm)->fwver = fwver;
 	hwver = (unsigned)resp[9];
 	memcpy(PDATA(pgm)->serno, resp + 10, 6);
 	if (verbose >= 1 && status > 17) {
@@ -1035,6 +1040,83 @@ static void jtagmkII_set_devdescr(PROGRAMMER * pgm, AVRPART * p)
   }
 }
 
+static void jtagmkII_set_xmega_params(PROGRAMMER * pgm, AVRPART * p)
+{
+  int status;
+  unsigned char *resp, c;
+  LNODEID ln;
+  AVRMEM * m;
+  struct {
+    unsigned char cmd;
+    struct xmega_device_desc dd;
+  } sendbuf;
+
+  memset(&sendbuf, 0, sizeof sendbuf);
+  sendbuf.cmd = CMND_SET_XMEGA_PARAMS;
+  u16_to_b2(sendbuf.dd.whatever, 0x0002);
+  sendbuf.dd.datalen = 47;
+  u16_to_b2(sendbuf.dd.nvm_base_addr, p->nvm_base);
+  u16_to_b2(sendbuf.dd.mcu_base_addr, p->mcu_base);
+
+  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.flash_page_size, m->page_size * 2);
+    } else if (strcmp(m->desc, "eeprom") == 0) {
+      sendbuf.dd.eeprom_page_size = m->page_size;
+      u16_to_b2(sendbuf.dd.eeprom_size, m->size);
+      u32_to_b4(sendbuf.dd.nvm_eeprom_offset, m->offset);
+    } else if (strcmp(m->desc, "application") == 0) {
+      u32_to_b4(sendbuf.dd.app_size, m->size);
+      u32_to_b4(sendbuf.dd.nvm_app_offset, m->offset);
+    } else if (strcmp(m->desc, "boot") == 0) {
+      u16_to_b2(sendbuf.dd.boot_size, m->size);
+      u32_to_b4(sendbuf.dd.nvm_boot_offset, m->offset);
+    } else if (strcmp(m->desc, "fuse0") == 0) {
+      u32_to_b4(sendbuf.dd.nvm_fuse_offset, m->offset);
+    } else if (strcmp(m->desc, "lock") == 0) {
+      u32_to_b4(sendbuf.dd.nvm_lock_offset, m->offset);
+    } else if (strcmp(m->desc, "usersig") == 0) {
+      u32_to_b4(sendbuf.dd.nvm_user_sig_offset, m->offset);
+    } else if (strcmp(m->desc, "prodsig") == 0) {
+      u32_to_b4(sendbuf.dd.nvm_prod_sig_offset, m->offset);
+    } else if (strcmp(m->desc, "data") == 0) {
+      u32_to_b4(sendbuf.dd.nvm_data_offset, m->offset);
+    }
+  }
+
+  if (verbose >= 2)
+    fprintf(stderr, "%s: jtagmkII_set_xmega_params(): "
+	    "Sending set Xmega params command: ",
+	    progname);
+  jtagmkII_send(pgm, (unsigned char *)&sendbuf, sizeof sendbuf);
+
+  status = jtagmkII_recv(pgm, &resp);
+  if (status <= 0) {
+    if (verbose >= 2)
+      putc('\n', stderr);
+    fprintf(stderr,
+	    "%s: jtagmkII_set_xmega_params(): "
+	    "timeout/error communicating with programmer (status %d)\n",
+	    progname, status);
+    return;
+  }
+  if (verbose >= 3) {
+    putc('\n', stderr);
+    jtagmkII_prmsg(pgm, resp, status);
+  } else if (verbose == 2)
+    fprintf(stderr, "0x%02x (%d bytes msg)\n", resp[0], status);
+  c = resp[0];
+  free(resp);
+  if (c != RSP_OK) {
+    fprintf(stderr,
+	    "%s: jtagmkII_set_xmega_params(): "
+	    "bad response to set device descriptor command: %s\n",
+	    progname, jtagmkII_get_rc(c));
+  }
+}
+
 /*
  * Reset the target.
  */
@@ -1287,7 +1369,10 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p)
   /*
    * Must set the device descriptor before entering programming mode.
    */
-  jtagmkII_set_devdescr(pgm, p);
+  if (PDATA(pgm)->fwver >= 0x700 && (p->flags & AVRPART_HAS_PDI) != 0)
+    jtagmkII_set_xmega_params(pgm, p);
+  else
+    jtagmkII_set_devdescr(pgm, p);
 
   PDATA(pgm)->boot_start = ULONG_MAX;
   /*
@@ -1850,7 +1935,7 @@ static int jtagmkII_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
       cmd[1] = jtagmkII_memtype(pgm, p, addr);
 
     u32_to_b4(cmd + 2, page_size);
-    u32_to_b4(cmd + 6, addr+m->offset );
+    u32_to_b4(cmd + 6, jtagmkII_memaddr(pgm, p, m, addr));
 
     /*
      * The JTAG ICE will refuse to write anything but a full page, at
@@ -1924,7 +2009,7 @@ static int jtagmkII_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
   unsigned int maxaddr = addr + n_bytes;
   unsigned char cmd[10];
   unsigned char *resp;
-  int status, tries;
+  int status, tries, dynamic_memtype = 0;
   long otimeout = serial_recv_timeout;
 
   if (verbose >= 2)
@@ -1937,8 +2022,12 @@ static int jtagmkII_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
   page_size = m->readsize;
 
   cmd[0] = CMND_READ_MEMORY;
-  cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_FLASH : MTYPE_FLASH_PAGE;
-  if (strcmp(m->desc, "eeprom") == 0) {
+  cmd[1] = jtagmkII_memtype(pgm, p, addr);
+  if (strcmp(m->desc, "flash") == 0) {
+    if (p->flags & AVRPART_HAS_PDI)
+      /* dynamically decide between flash/boot memtype */
+      dynamic_memtype = 1;
+  } else if (strcmp(m->desc, "eeprom") == 0) {
     cmd[1] = ( p->flags & AVRPART_HAS_PDI ) ? MTYPE_EEPROM : MTYPE_EEPROM_PAGE;
     if (pgm->flag & PGM_FL_IS_DW)
       return -1;
@@ -1958,8 +2047,11 @@ static int jtagmkII_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
 	      "block_size at addr %d is %d\n",
 	      progname, addr, block_size);
 
+    if (dynamic_memtype)
+      cmd[1] = jtagmkII_memtype(pgm, p, addr);
+
     u32_to_b4(cmd + 2, block_size);
-    u32_to_b4(cmd + 6, addr+m->offset );
+    u32_to_b4(cmd + 6, jtagmkII_memaddr(pgm, p, m, addr));
 
     tries = 0;
 
@@ -2544,6 +2636,31 @@ static unsigned char jtagmkII_memtype(PROGRAMMER * pgm, AVRPART * p, unsigned lo
   }
 }
 
+static unsigned int jtagmkII_memaddr(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr)
+{
+  /*
+   * Xmega devices handled by V7+ firmware don't want to be told their
+   * m->offset within the write memory command.
+   */
+  if (PDATA(pgm)->fwver >= 0x700 && (p->flags & AVRPART_HAS_PDI) != 0) {
+    if (addr >= PDATA(pgm)->boot_start)
+      /*
+       * all memories but "flash" are smaller than boot_start anyway, so
+       * no need for an extra check we are operating on "flash"
+       */
+      return addr - PDATA(pgm)->boot_start;
+    else
+      /* normal flash, or anything else */
+      return addr;
+  }
+  /*
+   * Old firmware, or non-Xmega device.  Non-Xmega (and non-AVR32)
+   * devices always have an m->offset of 0, so we don't have to
+   * distinguish them here.
+   */
+  return addr + m->offset;
+}
+
 
 #ifdef __OBJC__
 #pragma mark -
diff --git a/jtagmkII_private.h b/jtagmkII_private.h
index cb442c91..4e3272d6 100644
--- a/jtagmkII_private.h
+++ b/jtagmkII_private.h
@@ -111,6 +111,7 @@
 #define CMND_WRITE_MEMORY32        0x2D
 #define CMND_ISP_PACKET            0x2F
 #define CMND_XMEGA_ERASE           0x34
+#define CMND_SET_XMEGA_PARAMS      0x36  // undocumented in AVR067
 
 
 /* ICE responses */
@@ -355,6 +356,27 @@ struct device_descriptor
   /* new as of early 2005, firmware 4.x */
   unsigned char EECRAddress[2]; /* EECR memory-mapped IO address */
 };
+
+/* New Xmega device descriptor, for firmware version 7 and above */
+struct xmega_device_desc {
+    unsigned char whatever[2];		// cannot guess; must be 0x0002
+    unsigned char datalen;		// length of the following data, = 47
+    unsigned char nvm_app_offset[4];	// NVM offset for application flash
+    unsigned char nvm_boot_offset[4];	// NVM offset for boot flash
+    unsigned char nvm_eeprom_offset[4]; // NVM offset for EEPROM
+    unsigned char nvm_fuse_offset[4];	// NVM offset for fuses
+    unsigned char nvm_lock_offset[4];	// NVM offset for lock bits
+    unsigned char nvm_user_sig_offset[4]; // NVM offset for user signature row
+    unsigned char nvm_prod_sig_offset[4]; // NVM offset for production sign. row
+    unsigned char nvm_data_offset[4];	// NVM offset for data memory (SRAM + IO)
+    unsigned char app_size[4];		// size of application flash
+    unsigned char boot_size[2];		// size of boot flash
+    unsigned char flash_page_size[2];	// flash page size
+    unsigned char eeprom_size[2];	// size of EEPROM
+    unsigned char eeprom_page_size;	// EEPROM page size
+    unsigned char nvm_base_addr[2];	// IO space base address of NVM controller
+    unsigned char mcu_base_addr[2];	// IO space base address of MCU control
+};
 #endif /* JTAGMKII_PRIVATE_EXPORTED */
 
 /* return code from jtagmkII_getsync() to indicate a "graceful"
diff --git a/lexer.l b/lexer.l
index 7e55026f..255ae941 100644
--- a/lexer.l
+++ b/lexer.l
@@ -167,6 +167,7 @@ load_ext_addr    { yylval=new_token(K_LOAD_EXT_ADDR); return K_LOAD_EXT_ADDR; }
 loadpage_hi      { yylval=new_token(K_LOADPAGE_HI); return K_LOADPAGE_HI; }
 loadpage_lo      { yylval=new_token(K_LOADPAGE_LO); return K_LOADPAGE_LO; }
 max_write_delay  { yylval=NULL; return K_MAX_WRITE_DELAY; }
+mcu_base         { yylval=NULL; return K_MCU_BASE; }
 memory           { yylval=NULL; return K_MEMORY; }
 min_write_delay  { yylval=NULL; return K_MIN_WRITE_DELAY; }
 miso             { yylval=NULL; return K_MISO; }