diff -uNrp avrdude-5.10/avr.c avrdude-5.10-gm/avr.c
--- avrdude-5.10/avr.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/avr.c	2011-08-18 22:45:29.000000000 +0800
@@ -36,17 +36,60 @@
 #include "ppi.h"
 #include "safemode.h"
 #include "update.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;
+  int rc = 0;
+
+  cmd = TPI_CMD_SIN | TPI_SIO_ADDR(TPI_IOREG_NVMCSR);
+  rc = pgm->cmd_tpi(pgm, &cmd, 1, &res, 1);
+  return (rc & TPI_IOREG_NVMCSR_NVMBSY);
+}
+
+/* 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) {
@@ -60,6 +103,27 @@ int avr_read_byte_default(PROGRAMMER * p
   pgm->pgm_led(pgm, ON);
   pgm->err_led(pgm, OFF);
 
+  if (p->flags & AVRPART_HAS_TPI) {
+    if (pgm->cmd_tpi == NULL) {
+      fprintf(stderr, "%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
    */
@@ -151,6 +215,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART *
   unsigned char    rbyte;
   unsigned long    i;
   unsigned char  * buf;
+  unsigned char    cmd[4];
   AVRMEM * mem;
   int rc;
 
@@ -171,6 +236,33 @@ int avr_read(PROGRAMMER * pgm, AVRPART *
    */
   memset(buf, 0xff, size);
 
+  /* supports "paged load" thru post-increment */
+  if ((p->flags & AVRPART_HAS_TPI) && mem->page_size != 0) {
+    if (pgm->cmd_tpi == NULL) {
+      fprintf(stderr, "%s: Error: %s programmer does not support TPI\n",
+          progname, pgm->type);
+      return -1;
+    }
+
+    while (avr_tpi_poll_nvmbsy(pgm));
+
+    /* setup for read (NOOP) */
+    avr_tpi_setup_rw(pgm, mem, 0, TPI_NVMCMD_NO_OPERATION);
+
+    /* load bytes */
+    for (i = 0; i < size; i++) {
+      cmd[0] = TPI_CMD_SLD_PI;
+      rc = pgm->cmd_tpi(pgm, cmd, 1, &buf[i], 1);
+      if (rc == -1) {
+        fprintf(stderr, "avr_read(): error reading address 0x%04lx\n", i);
+        return -1;
+      }
+
+      report_progress(i, size, NULL);
+    }
+    return avr_mem_hiaddr(mem);
+  }
+
   if (pgm->paged_load != NULL && mem->page_size != 0) {
     /*
      * the programmer supports a paged mode read, perhaps more
@@ -303,6 +395,52 @@ int avr_write_byte_default(PROGRAMMER * 
     return -1;
   }
 
+  if (p->flags & AVRPART_HAS_TPI) {
+    if (pgm->cmd_tpi == NULL) {
+      fprintf(stderr, "%s: Error: %s programmer does not support TPI\n",
+          progname, pgm->type);
+      return -1;
+    }
+
+    if (strcmp(mem->desc, "flash") == 0) {
+      fprintf(stderr, "Writing a byte to flash is not supported for %s\n", p->desc);
+      return -1;
+    } else if ((mem->offset + addr) & 1) {
+      fprintf(stderr, "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) {
     /* 
      * check to see if the write is necessary by reading the existing
@@ -540,6 +678,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART 
   long             i;
   unsigned char    data;
   int              werror;
+  unsigned char    cmd[4];
   AVRMEM         * m;
 
   m = avr_locate_mem(p, memtype);
@@ -566,6 +705,40 @@ int avr_write(PROGRAMMER * pgm, AVRPART 
             progbuf, wsize);
   }
 
+
+  if ((p->flags & AVRPART_HAS_TPI) && m->page_size != 0) {
+    if (pgm->cmd_tpi == NULL) {
+      fprintf(stderr,
+          "%s: Error: %s programmer does not support TPI\n",
+          progname, pgm->type);
+      return -1;
+    }
+
+    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 (i = 0; i < wsize; 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];
+      rc = pgm->cmd_tpi(pgm, cmd, 2, NULL, 0);
+
+      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, perhaps more
diff -uNrp avrdude-5.10/avr.h avrdude-5.10-gm/avr.h
--- avrdude-5.10/avr.h	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/avr.h	2011-08-18 22:45:29.000000000 +0800
@@ -37,6 +37,7 @@ extern FP_UpdateProgress update_progress
 extern "C" {
 #endif
 
+int avr_tpi_poll_nvmbsy(PROGRAMMER *pgm);
 int avr_read_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
 			  unsigned long addr, unsigned char * value);
 
diff -uNrp avrdude-5.10/bitbang.c avrdude-5.10-gm/bitbang.c
--- avrdude-5.10/bitbang.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/bitbang.c	2011-08-18 23:34:39.000000000 +0800
@@ -39,6 +39,7 @@
 #include "pgm.h"
 #include "par.h"
 #include "serbb.h"
+#include "tpi.h"
 
 static int delay_decrement;
 
@@ -212,6 +213,93 @@ static unsigned char bitbang_txrx(PROGRA
   return rbyte;
 }
 
+static int bitbang_tpi_clk(PROGRAMMER * pgm) 
+{
+  unsigned char r = 0;
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 1);
+
+  r = pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]);
+
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0);
+
+  return r;
+}
+
+void bitbang_tpi_tx(PROGRAMMER * pgm, unsigned char byte) 
+{
+  int i;
+  unsigned char b, parity;
+
+  /* start bit */
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 0);
+  bitbang_tpi_clk(pgm);
+
+  parity = 0;
+  for (i = 0; i <= 7; i++) {
+    b = (byte >> i) & 0x01;
+    parity ^= b;
+
+    /* set the data input line as desired */
+    pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], b);
+    bitbang_tpi_clk(pgm);
+  }
+  
+  /* parity bit */
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], parity);
+  bitbang_tpi_clk(pgm);
+
+  /* 2 stop bits */
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1);
+  bitbang_tpi_clk(pgm);
+  bitbang_tpi_clk(pgm);
+}
+
+int bitbang_tpi_rx(PROGRAMMER * pgm) 
+{
+  int i;
+  unsigned char b, rbyte, parity;
+
+  /* make sure pin is on for "pullup" */
+  pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1);
+
+  /* wait for start bit (up to 10 bits) */
+  b = 1;
+  for (i = 0; i < 10; i++) {
+    b = bitbang_tpi_clk(pgm);
+    if (b == 0)
+      break;
+  }
+  if (b != 0) {
+    fprintf(stderr, "bitbang_tpi_rx: start bit not received correctly\n");
+    return -1;
+  }
+
+  rbyte = 0;
+  parity = 0;
+  for (i=0; i<=7; i++) {
+    b = bitbang_tpi_clk(pgm);
+    parity ^= b;
+
+    rbyte |= b << i;
+  }
+
+  /* parity bit */
+  if (bitbang_tpi_clk(pgm) != parity) {
+    fprintf(stderr, "bitbang_tpi_rx: parity bit is wrong\n");
+    return -1;
+  }
+
+  /* 2 stop bits */
+  b = 1;
+  b &= bitbang_tpi_clk(pgm);
+  b &= bitbang_tpi_clk(pgm);
+  if (b != 1) {
+    fprintf(stderr, "bitbang_tpi_rx: stop bits not received correctly\n");
+    return -1;
+  }
+  
+  return rbyte;
+}
 
 int bitbang_rdy_led(PROGRAMMER * pgm, int value)
 {
@@ -267,6 +355,44 @@ int bitbang_cmd(PROGRAMMER * pgm, unsign
   return 0;
 }
 
+int bitbang_cmd_tpi(PROGRAMMER * pgm, unsigned char cmd[], 
+                       int cmd_len, unsigned char res[], int res_len) 
+{
+  int i, r;
+
+  pgm->pgm_led(pgm, ON);
+
+  for (i=0; i<cmd_len; i++) {
+    bitbang_tpi_tx(pgm, cmd[i]);
+  }
+
+  r = 0;
+  for (i=0; i<res_len; i++) {
+    r = bitbang_tpi_rx(pgm);
+    if (r == -1)
+      break;
+    res[i] = r;
+  }
+
+  if(verbose >= 2)
+  {
+    fprintf(stderr, "bitbang_cmd_tpi(): [ ");
+    for(i = 0; i < cmd_len; i++)
+      fprintf(stderr, "%02X ", cmd[i]);
+    fprintf(stderr, "] [ ");
+    for(i = 0; i < res_len; i++)
+    {
+      fprintf(stderr, "%02X ", res[i]);
+    }
+    fprintf(stderr, "]\n");
+  }
+
+  pgm->pgm_led(pgm, OFF);
+  if (r == -1)
+    return -1;
+  return 0;
+}
+
 /*
  * transmit bytes via SPI and return the results; 'cmd' and
  * 'res' must point to data buffers
@@ -308,6 +434,39 @@ int bitbang_chip_erase(PROGRAMMER * pgm,
 {
   unsigned char cmd[4];
   unsigned char res[4];
+  AVRMEM *mem;
+
+  if (p->flags & AVRPART_HAS_TPI) {
+    pgm->pgm_led(pgm, ON);
+
+    while (avr_tpi_poll_nvmbsy(pgm));
+
+    /* NVMCMD <- CHIP_ERASE */
+    bitbang_tpi_tx(pgm, TPI_CMD_SOUT | TPI_SIO_ADDR(TPI_IOREG_NVMCMD));
+    bitbang_tpi_tx(pgm, TPI_NVMCMD_CHIP_ERASE); /* CHIP_ERASE */
+
+    /* Set Pointer Register */
+    mem = avr_locate_mem(p, "flash");
+    if (mem == NULL) {
+      fprintf(stderr, "No flash memory to erase for part %s\n",
+          p->desc);
+      return -1;
+    }
+    bitbang_tpi_tx(pgm, TPI_CMD_SSTPR | 0);
+    bitbang_tpi_tx(pgm, (mem->offset & 0xFF) | 1);  /* high byte */
+    bitbang_tpi_tx(pgm, TPI_CMD_SSTPR | 1);
+    bitbang_tpi_tx(pgm, (mem->offset >> 8) & 0xFF);
+
+    /* write dummy value to start erase */
+    bitbang_tpi_tx(pgm, TPI_CMD_SST);
+    bitbang_tpi_tx(pgm, 0xFF);
+
+    while (avr_tpi_poll_nvmbsy(pgm));
+
+    pgm->pgm_led(pgm, OFF);
+
+    return 0;
+  }
 
   if (p->op[AVR_OP_CHIP_ERASE] == NULL) {
     fprintf(stderr, "chip erase instruction not defined for part \"%s\"\n",
@@ -336,6 +495,19 @@ int bitbang_program_enable(PROGRAMMER * 
 {
   unsigned char cmd[4];
   unsigned char res[4];
+  int i;
+
+  if (p->flags & AVRPART_HAS_TPI) {
+    /* enable NVM programming */
+    bitbang_tpi_tx(pgm, TPI_CMD_SKEY);
+    for (i = sizeof(tpi_skey) - 1; i >= 0; i--)
+      bitbang_tpi_tx(pgm, tpi_skey[i]);
+
+    /* check NVMEN bit */
+    bitbang_tpi_tx(pgm, TPI_CMD_SLDCS | TPI_REG_TPISR);
+    i = bitbang_tpi_rx(pgm);
+    return (i != -1 && (i & TPI_REG_TPISR_NVMEN)) ? 0 : -2;
+  }
 
   if (p->op[AVR_OP_PGM_ENABLE] == NULL) {
     fprintf(stderr, "program enable instruction not defined for part \"%s\"\n",
@@ -360,17 +532,68 @@ int bitbang_initialize(PROGRAMMER * pgm,
 {
   int rc;
   int tries;
+  int i;
 
   bitbang_calibrate_delay();
 
   pgm->powerup(pgm);
   usleep(20000);
 
+  /* TPIDATA is a single line, so MISO & MOSI should be connected */
+  if (p->flags & AVRPART_HAS_TPI) {
+    /* make sure cmd_tpi() is defined */
+    if (pgm->cmd_tpi == NULL) {
+      fprintf(stderr, "%s: Error: %s programmer does not support TPI\n",
+          progname, pgm->type);
+      return -1;
+    }
+
+	/* bring RESET high first */
+    pgm->setpin(pgm, pgm->pinno[PIN_AVR_RESET], 1);
+	usleep(1000);
+
+    if (verbose >= 2)
+      fprintf(stderr, "doing MOSI-MISO link check\n");
+
+    pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 0);
+    if (pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]) != 0) {
+      fprintf(stderr, "MOSI->MISO 0 failed\n");
+      return -1;
+    }
+    pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1);
+    if (pgm->getpin(pgm, pgm->pinno[PIN_AVR_MISO]) != 1) {
+      fprintf(stderr, "MOSI->MISO 1 failed\n");
+      return -1;
+    }
+
+    if (verbose >= 2)
+      fprintf(stderr, "MOSI-MISO link present\n");
+  }
+
   pgm->setpin(pgm, pgm->pinno[PIN_AVR_SCK], 0);
   pgm->setpin(pgm, pgm->pinno[PIN_AVR_RESET], 0);
   usleep(20000);
 
-  pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_RESET]);
+  if (p->flags & AVRPART_HAS_TPI) {
+    /* keep TPIDATA high for 16 clock cycles */
+    pgm->setpin(pgm, pgm->pinno[PIN_AVR_MOSI], 1);
+    for (i = 0; i < 16; i++)
+      pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_SCK]);
+
+    /* remove extra guard timing bits */
+    bitbang_tpi_tx(pgm, TPI_CMD_SSTCS | TPI_REG_TPIPCR);
+    bitbang_tpi_tx(pgm, 0x7);
+
+    /* read TPI ident reg */
+    bitbang_tpi_tx(pgm, TPI_CMD_SLDCS | TPI_REG_TPIIR);
+    rc = bitbang_tpi_rx(pgm);
+    if (rc != 0x80) {
+      fprintf(stderr, "TPIIR not correct\n");
+      return -1;
+    }
+  } else {
+    pgm->highpulsepin(pgm, pgm->pinno[PIN_AVR_RESET]);
+  }
 
   usleep(20000); /* 20 ms XXX should be a per-chip parameter */
 
diff -uNrp avrdude-5.10/bitbang.h avrdude-5.10-gm/bitbang.h
--- avrdude-5.10/bitbang.h	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/bitbang.h	2011-08-18 22:45:29.000000000 +0800
@@ -39,6 +39,8 @@ int  bitbang_pgm_led        (PROGRAMMER 
 int  bitbang_vfy_led        (PROGRAMMER * pgm, int value);
 int  bitbang_cmd            (PROGRAMMER * pgm, unsigned char cmd[4],
                                 unsigned char res[4]);
+int  bitbang_cmd_tpi        (PROGRAMMER * pgm, unsigned char cmd[], 
+                                int cmd_len, unsigned char res[], int res_len);
 int  bitbang_spi            (PROGRAMMER * pgm, unsigned char cmd[],
                                 unsigned char res[], int count);
 int  bitbang_chip_erase     (PROGRAMMER * pgm, AVRPART * p);
diff -uNrp avrdude-5.10/doc/avrdude.texi avrdude-5.10-gm/doc/avrdude.texi
--- avrdude-5.10/doc/avrdude.texi	2010-01-19 18:39:08.000000000 +0800
+++ avrdude-5.10-gm/doc/avrdude.texi	2011-08-19 00:33:51.000000000 +0800
@@ -2387,6 +2387,44 @@ Solution: Use the following pin mapping:
 @end multitable
 
 @item
+Problem: I want to program an ATtiny4/5/9/10 device using a serial/parallel 
+bitbang programmer.  How to connect the pins?
+
+Solution: Since TPI has only 1 pin for bi-directional data transfer, both 
+@var{MISO} and @var{MOSI} pins should be connected to the @var{TPIDATA} pin 
+on the ATtiny device.
+However, a 1K resistor should be placed between the @var{MOSI} and @var{TPIDATA}.
+The @var{MISO} pin connects to @var{TPIDATA} directly.
+The @var{SCK} pin is connected to @var{TPICLK}.
+
+In addition, the @var{Vcc}, @var{/RESET} and @var{GND} pins should 
+be connected to their respective ports on the ATtiny device.
+
+@item
+Problem: How can I use a FTDI FT232R USB-to-Serial device for bitbang programming?
+
+Solution: When connecting the FT232 directly to the pins of the target Atmel device, 
+the polarity of the pins defined in the @code{programmer} definition should be 
+inverted by prefixing a tilde. For example, the @var{dasa} programmer would 
+look like this when connected via a FT232R device (notice the tildes in 
+front of pins 7, 4, 3 and 8):
+
+@example
+programmer
+  id    = "dasa_ftdi";
+  desc  = "serial port banging, reset=rts sck=dtr mosi=txd miso=cts";
+  type  = serbb;
+  reset = ~7;
+  sck   = ~4;
+  mosi  = ~3;
+  miso  = ~8;
+;
+@end example
+
+Note that this uses the FT232 device as a normal serial port, not using the 
+FTDI drivers in the special bitbang mode.
+
+@item
 Problem: My ATtiny4/5/9/10 reads out fine, but any attempt to program
 it (through TPI) fails.  Instead, the memory retains the old contents.
 
diff -uNrp avrdude-5.10/par.c avrdude-5.10-gm/par.c
--- avrdude-5.10/par.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/par.c	2011-08-18 22:45:29.000000000 +0800
@@ -416,6 +416,7 @@ void par_initpgm(PROGRAMMER * pgm)
   pgm->program_enable = bitbang_program_enable;
   pgm->chip_erase     = bitbang_chip_erase;
   pgm->cmd            = bitbang_cmd;
+  pgm->cmd_tpi        = bitbang_tpi;
   pgm->spi            = bitbang_spi;
   pgm->open           = par_open;
   pgm->close          = par_close;
diff -uNrp avrdude-5.10/pgm.c avrdude-5.10-gm/pgm.c
--- avrdude-5.10/pgm.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/pgm.c	2011-08-18 22:45:29.000000000 +0800
@@ -118,6 +118,7 @@ PROGRAMMER * pgm_new(void)
    * assigned before they are called
    */
   pgm->cmd            = NULL;
+  pgm->cmd_tpi        = NULL;
   pgm->spi            = NULL;
   pgm->paged_write    = NULL;
   pgm->paged_load     = NULL;
diff -uNrp avrdude-5.10/pgm.h avrdude-5.10-gm/pgm.h
--- avrdude-5.10/pgm.h	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/pgm.h	2011-08-18 22:45:29.000000000 +0800
@@ -78,6 +78,8 @@ typedef struct programmer_t {
   int  (*chip_erase)     (struct programmer_t * pgm, AVRPART * p);
   int  (*cmd)            (struct programmer_t * pgm, unsigned char cmd[4], 
                           unsigned char res[4]);
+  int  (*cmd_tpi)        (struct programmer_t * pgm, unsigned char cmd[], 
+                          int cmd_len, unsigned char res[], int res_len);
   int  (*spi)            (struct programmer_t * pgm, unsigned char cmd[], 
                           unsigned char res[], int count);
   int  (*open)           (struct programmer_t * pgm, char * port);
diff -uNrp avrdude-5.10/serbb_posix.c avrdude-5.10-gm/serbb_posix.c
--- avrdude-5.10/serbb_posix.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/serbb_posix.c	2011-08-18 22:45:29.000000000 +0800
@@ -300,6 +300,7 @@ void serbb_initpgm(PROGRAMMER *pgm)
   pgm->program_enable = bitbang_program_enable;
   pgm->chip_erase     = bitbang_chip_erase;
   pgm->cmd            = bitbang_cmd;
+  pgm->cmd_tpi        = bitbang_cmd_tpi;
   pgm->open           = serbb_open;
   pgm->close          = serbb_close;
   pgm->setpin         = serbb_setpin;
diff -uNrp avrdude-5.10/serbb_win32.c avrdude-5.10-gm/serbb_win32.c
--- avrdude-5.10/serbb_win32.c	2010-01-19 18:39:11.000000000 +0800
+++ avrdude-5.10-gm/serbb_win32.c	2011-08-18 22:45:29.000000000 +0800
@@ -361,6 +361,7 @@ void serbb_initpgm(PROGRAMMER *pgm)
   pgm->program_enable = bitbang_program_enable;
   pgm->chip_erase     = bitbang_chip_erase;
   pgm->cmd            = bitbang_cmd;
+  pgm->cmd_tpi        = bitbang_cmd_tpi;
   pgm->open           = serbb_open;
   pgm->close          = serbb_close;
   pgm->setpin         = serbb_setpin;
diff -uNrp avrdude-5.10/tpi.h avrdude-5.10-gm/tpi.h
--- avrdude-5.10/tpi.h	1970-01-01 07:30:00.000000000 +0730
+++ avrdude-5.10-gm/tpi.h	2011-08-18 22:46:56.000000000 +0800
@@ -0,0 +1,70 @@
+/*
+ * avrdude - A Downloader/Uploader for AVR device programmers
+ * Copyright (C) 2010  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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef tpi_h
+#define tpi_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const unsigned char tpi_skey[] = { 0x12, 0x89, 0xAB, 0x45, 0xCD, 0xD8, 0x88, 0xFF };
+
+/* registers */
+#define TPI_REG_TPIIR	0x0F
+#define TPI_REG_TPIPCR	0x02
+#define TPI_REG_TPISR	0x00
+
+#define TPI_REG_TPISR_NVMEN		(1 << 1)
+
+/* TPI commands */
+#define TPI_CMD_SLD		0x20
+#define TPI_CMD_SLD_PI	0x24
+#define TPI_CMD_SIN		0x10
+#define TPI_CMD_SOUT	0x90
+#define TPI_CMD_SSTCS	0xC0
+#define TPI_CMD_SST		0x60
+#define TPI_CMD_SST_PI	0x64
+
+#define TPI_CMD_SLDCS	0x80
+#define TPI_CMD_SSTPR	0x68
+#define TPI_CMD_SKEY	0xE0
+
+/* for TPI_CMD_SIN & TPI_CMD_SOUT */
+#define TPI_SIO_ADDR(x) ((x & 0x30) << 1 | (x & 0x0F)) 
+
+/* ATtiny4/5/9/10 I/O registers */
+#define TPI_IOREG_NVMCSR		0x32
+#define TPI_IOREG_NVMCMD		0x33
+
+/* bit for NVMCSR */
+#define TPI_IOREG_NVMCSR_NVMBSY	(1 << 7)
+
+/* NVM commands */
+#define TPI_NVMCMD_NO_OPERATION		0x00
+#define TPI_NVMCMD_CHIP_ERASE		0x10
+#define TPI_NVMCMD_SECTION_ERASE	0x14
+#define TPI_NVMCMD_WORD_WRITE		0x1D
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
