From 008ff9bb978e375293912bb7324e98b9635efa62 Mon Sep 17 00:00:00 2001
From: Hannes Weisbach <hannes_weisbach@gmx.net>
Date: Thu, 2 May 2013 11:06:15 +0000
Subject: [PATCH] Adds initial avrftdi TPI support.

Device identification is possible tested under OS X 10.6.8 with an
FT4232H and ATtiny10.

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1145 81a1dc3b-b13d-400b-aceb-764788c761c2
---
 Makefile.am       |   2 +
 avrftdi.c         | 176 +++++++++++++++--------
 avrftdi_private.h |  63 +++++++++
 avrftdi_tpi.c     | 352 ++++++++++++++++++++++++++++++++++++++++++++++
 avrftdi_tpi.h     |  14 ++
 5 files changed, 550 insertions(+), 57 deletions(-)
 create mode 100644 avrftdi_private.h
 create mode 100644 avrftdi_tpi.c
 create mode 100644 avrftdi_tpi.h

diff --git a/Makefile.am b/Makefile.am
index b94318ed..4d3a3e21 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -96,6 +96,8 @@ libavrdude_a_SOURCES = \
 	avrdude.h \
 	avrftdi.c \
 	avrftdi.h \
+	avrftdi_tpi.c \
+	avrftdi_tpi.h \
 	avrpart.c \
 	avrpart.h \
 	bitbang.c \
diff --git a/avrftdi.c b/avrftdi.c
index 670e8dc4..effc18dc 100644
--- a/avrftdi.c
+++ b/avrftdi.c
@@ -38,8 +38,8 @@
 #include "pgm.h"
 #include "avrftdi.h"
 #include "avrpart.h"
-#include "tpi.h"
-#include "usbasp.h"
+#include "avrftdi_tpi.h"
+#include "avrftdi_private.h"
 
 #ifdef HAVE_LIBUSB_1_0
 #ifdef HAVE_LIBFTDI1
@@ -64,26 +64,6 @@ enum { ERR, WARN, INFO, DEBUG, TRACE };
 
 #define FTDI_DEFAULT_MASK ( (1 << (FTDI_SCK - 1)) | (1 << (FTDI_MOSI - 1)) )
 
-#define E(x, ftdi)                                                  \
-  do {                                                              \
-    if ((x)) {                                                      \
-      log_err("%s: %s (%d) %s",                                     \
-          #x, strerror(errno), errno, ftdi_get_error_string(ftdi)); \
-      return -1;                                                    \
-    }                                                               \
-  } while(0)
-
-#define E_VOID(x, ftdi)                                             \
-  do {                                                              \
-    if ((x)) {                                                      \
-      log_err("%s: %s (%d) %s",                                     \
-          #x, strerror(errno), errno, ftdi_get_error_string(ftdi)); \
-    }                                                               \
-  } while(0)
-
-#define to_pdata(pgm) \
-	((avrftdi_t *)((pgm)->cookie))
-
 /* This is for running the code without having a FTDI-device.
  * The generated code is useless! For debugging purposes only.
  * This should never be defined, unless you know what you are
@@ -92,22 +72,6 @@ enum { ERR, WARN, INFO, DEBUG, TRACE };
  */
 //#define DRYRUN
 
-typedef struct avrftdi_s {
-	/* pointer to struct maintained by libftdi to identify the device */
-	struct ftdi_context* ftdic; 
-	/* bitmask of values for pins. bit 0 represents pin 0 ([A|B]DBUS0) */
-	uint16_t pin_value;
-	/* bitmask of pin direction. a '1' make a pin an output.
-	 * bit 0 corresponds to pin 0. */
-	uint16_t pin_direction;
-	/* don't know. not useful. someone put it in. */
-	uint16_t led_mask;
-	/* total number of pins supported by a programmer. varies with FTDI chips */
-	int pin_limit;
-	/* internal RX buffer of the device. needed for INOUT transfers */
-	int rx_buffer_size;
-} avrftdi_t;
-
 static int write_flush(avrftdi_t *);
 
 /*
@@ -419,12 +383,12 @@ static int set_pin(PROGRAMMER * pgm, int pinfunc, int value)
 
 	pin = pgm->pinno[pinfunc] & PIN_MASK;
 	inverted = pgm->pinno[pinfunc] & PIN_INVERSE;
-
+	
 	pin_mask = 1 << (pin - 1);
-
+	
 	/* make value 0 or 1 and invert, if necessary */
 	value = (inverted) ? !value : !!value;
-
+	
 	if (!pin) {
 		/* this error message is really annoying, maybe use a ratelimit? */
 	/*
@@ -586,6 +550,71 @@ static int avrftdi_transmit(avrftdi_t* pdata, unsigned char mode, unsigned char
 	return written;
 }
 
+/* this function tries to sync up with the FTDI. See FTDI application note AN_129.
+ * AN_135 uses 0xab as bad command and enables/disables loopback around synchronisation.
+ * This may fail if data is left in the buffer (i.e. avrdude aborted with ctrl-c)
+ * or the device is in an illegal state (i.e. a previous program).
+ * If the FTDI is out of sync, the buffers are cleared ("purged") and the
+ * sync is re-tried.
+ * if it still fails, we return an error code. higher level code may than abort.
+ * the device may be reset by unplugging the device and plugging it back in.
+ * resetting the device did not always help for me.
+ */
+static int ftdi_sync(avrftdi_t* pdata)
+{
+	unsigned char illegal_cmd[] = {0xaa};
+	unsigned char reply[2];
+	unsigned int i, n;
+	unsigned int retry = 0;
+	unsigned char latency;
+
+	ftdi_get_latency_timer(pdata->ftdic, &latency);
+	fprintf(stderr, "Latency: %d\n", latency);
+
+	do{
+		n = ftdi_read_data(pdata->ftdic, reply, 1);
+	} while(n > 0);
+retry:
+	/* send command "0xaa", which is an illegal command. */
+	E(ftdi_write_data(pdata->ftdic, illegal_cmd, sizeof(illegal_cmd)) != sizeof(illegal_cmd), pdata->ftdic);
+	
+	i = 0;
+	do {
+#ifndef DRYRUN
+		n = ftdi_read_data(pdata->ftdic, &reply[i], sizeof(reply) - i);
+		E(n < 0, pdata->ftdic);
+		//fprintf(stderr, "%s\n", ftdi_get_error_string(pdata->ftdic));
+#else
+		n = sizeof(reply) - i;
+#endif
+		i += n;
+	} while (i < sizeof(reply));
+
+	/* 0xfa is return code for illegal command - we expect that, since we issued an
+	 * illegal command (0xaa)
+	 * the next byte will be the illegal command, the FTDI is complaining about.
+	 */
+	if(reply[0] == 0xfa && reply[1] == illegal_cmd[0])
+	{
+		/* if the FTDI is complaining about the right thing, everything is fine */
+		fprintf(stderr, "FTDI is in sync.\n");
+		return 0;
+	}
+		else
+	{
+		fprintf(stderr, "FTDI out of sync. Received 0x%02x 0x%02x\n", reply[0], reply[1]);
+		if(retry < 4)
+		{
+			fprintf(stderr, "Trying to re-sync by purging buffers. Attempt\n");
+			E(ftdi_usb_purge_buffers(pdata->ftdic), pdata->ftdic);
+			retry++;
+			goto retry;
+		} else
+			fprintf(stderr, "Aborting. Try resetting or unplugging the device.\n");
+	}
+	return -1;
+}
+
 static int write_flush(avrftdi_t* pdata)
 {
 	unsigned char buf[6];
@@ -615,23 +644,36 @@ static int write_flush(avrftdi_t* pdata)
 	 *
 	 * Add.: purge does NOT flush. It clears. Also, it is unkown, when the purge
 	 * command actually arrives at the chip.
-	 * Use read-pin-status command as sync.
+	 * Use read pin status command as sync.
 	 */
 #ifndef DRYRUN
 	//E(ftdi_usb_purge_buffers(pdata->ftdic), pdata->ftdic);
 
 	unsigned char cmd[] = { GET_BITS_LOW, SEND_IMMEDIATE };
 	unsigned int n;
+	int retries = 0;
+	int num = 0;
 	E(ftdi_write_data(pdata->ftdic, cmd, sizeof(cmd)) != sizeof(cmd), pdata->ftdic);
 	do
 	{
 		n = ftdi_read_data(pdata->ftdic, cmd, 1);
+		if(n > 0)
+		{
+			avrftdi_print(0, "Low byte lines: 0x%02x\n", cmd[0]);
+			num += n;
+		}
+		if(!n)
+		{
+			retries++;
+		}
 		E(n < 0, pdata->ftdic);
-	} while(n < 1);
+	} while(retries < 1/*n < 1*/);
 	
+	avrftdi_print(0, "Read %d extra bytes\n", num-1);
 #endif
 
 	return 0;
+
 }
 
 
@@ -698,12 +740,21 @@ static int avrftdi_open(PROGRAMMER * pgm, char *port)
 	}
 	
 	ftdi_set_latency_timer(pdata->ftdic, 1);
+	//ftdi_write_data_set_chunksize(pdata->ftdic, 16);
+	//ftdi_read_data_set_chunksize(pdata->ftdic, 16);
 
 	/* set SPI mode */
 	E(ftdi_set_bitmode(pdata->ftdic, 0, BITMODE_RESET) < 0, pdata->ftdic);
 	E(ftdi_set_bitmode(pdata->ftdic, pdata->pin_direction & 0xff, BITMODE_MPSSE) < 0, pdata->ftdic);
 	E(ftdi_usb_purge_buffers(pdata->ftdic), pdata->ftdic);
 
+/*
+	ret = ftdi_sync(pdata);
+	if(ret < 0)
+		return ret;
+*/
+	write_flush(pdata);
+
 	if (pgm->baudrate) {
 		set_frequency(pdata, pgm->baudrate);
 	} else if(pgm->bitclock) {
@@ -781,7 +832,6 @@ static int avrftdi_open(PROGRAMMER * pgm, char *port)
 	if (add_pin(pgm, PIN_AVR_MOSI)) return -1;
 	if (add_pin(pgm, PIN_AVR_RESET)) return -1;
 
-
 	/* gather the rest of the pins */
 	if (add_pins(pgm, PPI_AVR_VCC)) return -1;
 	if (add_pins(pgm, PPI_AVR_BUFF)) return -1;
@@ -830,21 +880,29 @@ static void avrftdi_close(PROGRAMMER * pgm)
 
 static int avrftdi_initialize(PROGRAMMER * pgm, AVRPART * p)
 {
-	set_pin(pgm, PIN_AVR_RESET, OFF);
-	set_pins(pgm, PPI_AVR_BUFF, OFF);
-	set_pin(pgm, PIN_AVR_SCK, OFF);
-	/*use speed optimization with CAUTION*/
-	usleep(20 * 1000);
+	if(p->flags & AVRPART_HAS_TPI)
+	{
+		/* see avrftdi_tpi.c */
+		avrftdi_tpi_initialize(pgm, p);
+	}
+	else
+	{
+		set_pin(pgm, PIN_AVR_RESET, OFF);
+		set_pins(pgm, PPI_AVR_BUFF, OFF);
+		set_pin(pgm, PIN_AVR_SCK, OFF);
+		/*use speed optimization with CAUTION*/
+		usleep(20 * 1000);
 
-	/* giving rst-pulse of at least 2 avr-clock-cycles, for
-	 * security (2us @ 1MHz) */
-	set_pin(pgm, PIN_AVR_RESET, ON);
-	usleep(20 * 1000);
+		/* giving rst-pulse of at least 2 avr-clock-cycles, for
+		 * security (2us @ 1MHz) */
+		set_pin(pgm, PIN_AVR_RESET, ON);
+		usleep(20 * 1000);
 
-	/*setting rst back to 0 */
-	set_pin(pgm, PIN_AVR_RESET, OFF);
-	/*wait at least 20ms bevor issuing spi commands to avr */
-	usleep(20 * 1000);
+		/*setting rst back to 0 */
+		set_pin(pgm, PIN_AVR_RESET, OFF);
+		/*wait at least 20ms bevor issuing spi commands to avr */
+		usleep(20 * 1000);
+	}
 
 	return pgm->program_enable(pgm, p);
 }
@@ -965,6 +1023,7 @@ static int avrftdi_eeprom_write(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m,
 		avr_set_addr(m->op[AVR_OP_WRITE], &cmd[3], add);
 		avr_set_input(m->op[AVR_OP_WRITE], &cmd[3], *data++);
 
+		//avrftdi_transmit(to_pdata(pgm), MPSSE_DO_WRITE, cmd, cmd, 4);
 		E(ftdi_write_data(to_pdata(pgm)->ftdic, cmd, sizeof(cmd)) != sizeof(cmd),
 				to_pdata(pgm)->ftdic);
 
@@ -1237,11 +1296,14 @@ avrftdi_setup(PROGRAMMER * pgm)
 	pdata->pin_value = 0;
 	pdata->pin_direction = 0;
 	pdata->led_mask = 0;
+	pdata->guard_bits = 128 + 2;
+	pdata->set_pin = &set_pin;
 }
 
 static void
 avrftdi_teardown(PROGRAMMER * pgm)
 {
+	fprintf(stderr, "\n%s: Unintializing programmer.\n", progname);
 	avrftdi_t* pdata = to_pdata(pgm);
 
 	if(pdata) {
diff --git a/avrftdi_private.h b/avrftdi_private.h
new file mode 100644
index 00000000..a620747e
--- /dev/null
+++ b/avrftdi_private.h
@@ -0,0 +1,63 @@
+#pragma once
+#include "ac_cfg.h"
+
+#include <stdint.h>
+
+#ifdef HAVE_LIBFTDI1
+#  include <libftdi1/ftdi.h>
+#else
+#  error "libftdi1 required for avrftdi."
+#endif
+
+#include "pgm.h"
+
+#define E(x, ftdi)                                                  \
+	do {                                                              \
+		if ((x))                                                        \
+		{                                                               \
+			fprintf(stderr, "%s:%d %s() %s: %s (%d)\n\t%s\n",             \
+					__FILE__, __LINE__, __FUNCTION__,                         \
+					#x, strerror(errno), errno, ftdi_get_error_string(ftdi)); \
+			return -1;                                                    \
+		}                                                               \
+	} while(0)
+
+#define E_VOID(x, ftdi)                                             \
+	do {                                                              \
+		if ((x))                                                        \
+		{                                                               \
+			fprintf(stderr, "%s:%d %s() %s: %s (%d)\n\t%s\n",             \
+					__FILE__, __LINE__, __FUNCTION__,                         \
+	 			 #x, strerror(errno), errno, ftdi_get_error_string(ftdi));  \
+		}                                                               \
+	} while(0)
+
+
+#define to_pdata(pgm) \
+	((avrftdi_t *)((pgm)->cookie))
+
+typedef struct avrftdi_s {
+	/* pointer to struct maintained by libftdi to identify the device */
+	struct ftdi_context* ftdic; 
+	/* bitmask of values for pins. bit 0 represents pin 0 ([A|B]DBUS0) */
+	uint16_t pin_value;
+	/* bitmask of pin direction. a '1' make a pin an output.
+	 * bit 0 corresponds to pin 0. */
+	uint16_t pin_direction;
+	/* don't know. not useful. someone put it in. */
+	uint16_t led_mask;
+	/* total number of pins supported by a programmer. varies with FTDI chips */
+	int pin_limit;
+	/* internal RX buffer of the device. needed for INOUT transfers */
+	int rx_buffer_size;
+	/* number of guard bits for TPI. should be moved to struct PROGRAMMER */
+	int guard_bits;
+	/* function pointer to the set_pin function, so that we do not have to drag
+	 * it into global scope. it's a hack, but i think it's slightly better than
+	 * the alternative.
+	 */
+	int (*set_pin)(PROGRAMMER *, int, int);
+} avrftdi_t;
+
+void avrftdi_print(int level, const char * fmt, ...);
+
diff --git a/avrftdi_tpi.c b/avrftdi_tpi.c
new file mode 100644
index 00000000..91ebebdf
--- /dev/null
+++ b/avrftdi_tpi.c
@@ -0,0 +1,352 @@
+#include "ac_cfg.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pgm.h"
+#include "avrpart.h"
+#include "pindefs.h"
+#include "tpi.h"
+#include "usbasp.h"
+
+#include "avrftdi_tpi.h"
+#include "avrftdi_private.h"
+
+#ifdef HAVE_LIBUSB_1_0
+#ifdef HAVE_LIBFTDI1
+
+#include <libusb-1.0/libusb.h>
+#include <libftdi1/ftdi.h>
+
+static void
+avrftdi_debug_frame(uint16_t frame)
+{
+	static char bit_name[] = "IDLES01234567PSS";
+	//static char bit_name[] = "SSP76543210SELDI";
+	char line0[34], line1[34], line2[34];
+	int bit, pos;
+
+	for(bit = 0; bit < 16; bit++)
+	{
+		pos = 16 - bit - 1;
+		if(frame & (1 << pos))
+		{
+			line0[2*pos]  = '_';
+			line0[2*pos+1] = ' ';
+			
+			line2[2*pos]  = ' ';
+			line2[2*pos+1] = ' ';
+		}
+		else
+		{
+			line0[2*pos]  = ' ';
+			line0[2*pos+1] = ' ';
+			
+			line2[2*pos]  = '-';
+			line2[2*pos+1] = ' ';
+		}
+			
+		line1[2*pos]  = bit_name[pos];
+		line1[2*pos+1] = ' ';
+			
+	}
+
+	line0[32] = 0;
+	line1[32] = 0;
+	line2[32] = 0;
+
+	avrftdi_print(0, "%s\n", line0);
+	avrftdi_print(0, "%s\n", line1);
+	//avrftdi_print(0, "%s\n", line2);
+}
+
+int
+avrftdi_tpi_initialize(PROGRAMMER * pgm, AVRPART * p)
+{
+	int ret;
+
+	avrftdi_t* pdata = to_pdata(pgm);
+	unsigned char buf[] = { MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB, 0x01, 0x00, 0xff, 0xff };
+
+	avrftdi_print(0, "Using TPI interface\n");
+
+	pgm->program_enable = avrftdi_tpi_program_enable;
+	pgm->cmd_tpi = avrftdi_cmd_tpi;
+	pgm->chip_erase = avrftdi_tpi_chip_erase;
+	//pgm->read_byte = avrftdi_tpi_read_byte;
+	//pgm->write_byte = avrftdi_tpi_write_byte;
+	
+	avrftdi_print(0, "Setting /Reset pin low\n");
+	pdata->set_pin(pgm, PIN_AVR_RESET, OFF);
+	pdata->set_pin(pgm, PIN_AVR_SCK, OFF);
+	pdata->set_pin(pgm, PIN_AVR_MOSI, ON);
+	usleep(20 * 1000);
+
+	pdata->set_pin(pgm, PIN_AVR_RESET, ON);
+	/* worst case 128ms */
+	usleep(2 * 128 * 1000);
+
+	/*setting rst back to 0 */
+	pdata->set_pin(pgm, PIN_AVR_RESET, OFF);
+	/*wait at least 20ms bevor issuing spi commands to avr */
+	usleep(20 * 1000);
+	
+	avrftdi_print(0, "Sending 16 init clock cycles ... ");
+	ret = ftdi_write_data(pdata->ftdic, buf, sizeof(buf));
+	avrftdi_print(0, "Done.\n");
+
+	return ret;
+}
+
+#define TPI_BIT_PAR 0x2000
+
+static uint16_t
+tpi_byte2frame(uint8_t byte)
+{
+	uint16_t frame = 0xc00f;
+	int parity = __builtin_popcount(byte) & 1;
+
+	frame |= ((byte << 5) & 0x1fe0);
+
+	if(parity)
+		frame |= TPI_BIT_PAR;
+	
+	return frame;
+}
+
+static int
+tpi_frame2byte(uint16_t frame, uint8_t * byte)
+{
+	/* drop partity + 2 stop bits */
+	*byte = (frame >> 1) & 0xff;
+
+	int parity = __builtin_popcount(*byte) & 1;
+	int parity_rcvd = (frame & 0x200) ? 1 : 0;
+
+	avrftdi_print(1, "Recevied frame with payload 0x%02x and parity %d.\n", *byte, parity);
+	
+	return parity != parity_rcvd;
+}
+
+#define TPI_FRAME_SIZE 2
+
+static int
+avrftdi_tpi_break(PROGRAMMER * pgm)
+{
+	unsigned char buffer[] = { MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB, 1, 0, 0x80, 0x01 };
+	E(ftdi_write_data(to_pdata(pgm)->ftdic, buffer, sizeof(buffer)) != sizeof(buffer), to_pdata(pgm)->ftdic);
+
+	return 0;
+}
+
+static int
+avrftdi_tpi_write_byte(PROGRAMMER * pgm, unsigned char byte)
+{
+	uint16_t frame;
+
+	struct ftdi_context* ftdic = to_pdata(pgm)->ftdic;
+
+	unsigned char buffer[] = { MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB, 1, 0, 0, 0 };
+
+	frame = tpi_byte2frame(byte);
+	
+	buffer[3] = frame & 0xff;
+	buffer[4] = frame >> 8;
+	
+	avrftdi_print(1, "TPI frame: 0x%02x 0x%02x, data byte 0x%02x\n",
+			buffer[6], buffer[7], byte);
+	avrftdi_print(2, "FTDI raw data: 0x%02x 0x%02x 0x%02x  0x%02x 0x%02x\n",
+			buffer[0], buffer[1], buffer[2], buffer[3], buffer[4] /*, buffer[5], buffer[6], buffer[7]*/);
+
+	avrftdi_debug_frame(frame);
+	
+	E(ftdi_write_data(ftdic, buffer, sizeof(buffer)) != sizeof(buffer), ftdic);
+
+	return 0;
+}
+
+static int
+avrftdi_tpi_read_byte(PROGRAMMER * pgm, unsigned char * byte)
+{
+	uint16_t frame;
+	
+	int guard_bits = to_pdata(pgm)->guard_bits;
+	int bytes = ((guard_bits + 7) / 8) + 2;
+	int i = 0, n = 0;
+
+	/* worst case size:
+	 * - 128 guard bits
+	 * - 2 default idle bits
+	 * - 12 frame bits
+	 * = 142 bits
+	 */
+	unsigned char buffer[bytes];
+
+	if(bytes > sizeof(buffer))
+		avrftdi_print(0, "Requested more bytes (%d) than available buffer space (%d)\n", bytes, sizeof(buffer));
+
+	avrftdi_print(1, "Guard bit size (incl. default idle bits) is %d\n", guard_bits);
+	avrftdi_print(1, "Reading %d bytes for guard bits + frame\n", bytes);
+
+	/* set it high, so the PDI won't detect we're driving the line */
+	to_pdata(pgm)->set_pin(pgm, PIN_AVR_MOSI, ON);
+
+	buffer[0] = MPSSE_DO_READ | MPSSE_WRITE_NEG | MPSSE_LSB;
+	buffer[1] = (bytes-1) & 0xff;
+	buffer[2] = ((bytes-1) >> 8) & 0xff;
+	buffer[3] = SEND_IMMEDIATE;
+
+	avrftdi_print(3, "Read request: 0x%02x 0x%02x 0x%02x 0x%02x\n",
+			buffer[0], buffer[1], buffer[2], buffer[3]);
+
+	ftdi_write_data(to_pdata(pgm)->ftdic, buffer, 4);
+
+	memset(buffer, 0, sizeof(buffer));
+
+	i = 0;
+	do {
+		n = ftdi_read_data(to_pdata(pgm)->ftdic, &buffer[i], bytes - i);
+		E(n < 0, to_pdata(pgm)->ftdic);
+		i += n;
+	} while(i < bytes);
+
+	/* dismiss at least (guard_bits / 8) bytes */
+	i = guard_bits / 8;
+	frame = buffer[i] | (buffer[i+1] << 8);
+
+	/* now shift the rest of guard bits out */
+	i = guard_bits - (i*8);
+	frame >>= i;
+
+	avrftdi_print(1, "Received frame 0x%04x (LSB first)\n", frame);
+
+	return tpi_frame2byte(frame, byte);
+}
+
+int
+avrftdi_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p)
+{
+	int retry;
+	int err;
+	int i;
+	unsigned char byte = 0;
+
+	avrftdi_print(0, "TPI program enable\n");
+
+	//TODO determine guard time:
+	//-disable output possible -> guard time
+	//-else: minimum guard time
+	/* set guard time */
+	//avrftdi_tpi_write_byte(pgm, TPI_OP_SSTCS(TPIPCR));
+	//avrftdi_tpi_write_byte(pgm, TPIPCR_GT_2b);
+
+	/* send SKEY */
+  avrftdi_tpi_write_byte(pgm, TPI_CMD_SKEY);
+	for(i = sizeof(tpi_skey) - 1; i >= 0; --i)
+  	avrftdi_tpi_write_byte(pgm, tpi_skey[i]);
+
+	/* check if device is ready */
+  for(retry = 0; retry < 10; retry++)
+  {
+		avrftdi_print(0, "Reading Identification register\n");
+    avrftdi_tpi_write_byte(pgm, TPI_OP_SLDCS(TPIIR));
+    err = avrftdi_tpi_read_byte(pgm, &byte);
+		if(err || byte != 0x80)
+		{
+			avrftdi_print(0, "Error. Sending break.\n");
+			avrftdi_tpi_break(pgm);
+			avrftdi_tpi_break(pgm);
+			continue;
+		}
+
+    avrftdi_print(0, "Reading Status register\n");
+		avrftdi_tpi_write_byte(pgm, TPI_OP_SLDCS(TPISR));
+    err = avrftdi_tpi_read_byte(pgm, &byte);
+		if(err || !(byte & TPISR_NVMEN))
+		{
+			avrftdi_print(0, "Error. Sending break.\n");
+			avrftdi_tpi_break(pgm);
+			avrftdi_tpi_break(pgm);
+			continue;
+		}
+		
+		return 0;
+  }
+
+	avrftdi_print(0, "error connecting to target\n");
+	return -1;
+}
+
+static int
+avrftdi_tpi_nvm_waitbusy(PROGRAMMER * pgm)
+{
+	unsigned char byte;
+	int err;
+	int retry;
+
+	for(retry = 50; retry > 0; retry--)
+	{
+		avrftdi_tpi_write_byte(pgm, TPI_OP_SIN(NVMCSR));
+    err = avrftdi_tpi_read_byte(pgm, &byte);		
+		if(err || (byte & NVMCSR_BSY))
+			continue;
+		return 0;
+	}
+
+	return -1;
+}
+
+int
+avrftdi_cmd_tpi(PROGRAMMER * pgm, unsigned char cmd[], int cmd_len,
+		unsigned char res[], int res_len)
+{
+	int i, err = 0;
+
+	for(i = 0; i < cmd_len; i++)
+	{
+		err = avrftdi_tpi_write_byte(pgm, cmd[i]);
+		if(err)
+			return err;
+	}
+
+	for(i = 0; i < res_len; i++)
+	{
+		err = avrftdi_tpi_read_byte(pgm, &res[i]);
+		if(err)
+			return err;
+	}
+
+	return 0;
+}
+
+int
+avrftdi_tpi_chip_erase(PROGRAMMER * pgm, AVRPART * p)
+{
+  /* Set PR to flash */
+  avrftdi_tpi_write_byte(pgm, TPI_OP_SSTPR(0));
+  avrftdi_tpi_write_byte(pgm, 0x01);
+  avrftdi_tpi_write_byte(pgm, TPI_OP_SSTPR(1));
+  avrftdi_tpi_write_byte(pgm, 0x40);
+  /* select ERASE */
+  avrftdi_tpi_write_byte(pgm, TPI_OP_SOUT(NVMCMD));
+  avrftdi_tpi_write_byte(pgm, NVMCMD_CHIP_ERASE);
+  /* dummy write */
+  avrftdi_tpi_write_byte(pgm, TPI_OP_SST_INC);
+  avrftdi_tpi_write_byte(pgm, 0x00);
+  avrftdi_tpi_nvm_waitbusy(pgm);
+  
+  usleep(p->chip_erase_delay);
+  pgm->initialize(pgm, p);
+
+  return 0;
+}
+
+#else /*HAVE_LIBFTDI*/
+#endif  /* HAVE_LIBFTDI */
+
+#else /*HAVE_LIBUSB*/
+
+#endif /*HAVE_LIBUSB*/
+
diff --git a/avrftdi_tpi.h b/avrftdi_tpi.h
new file mode 100644
index 00000000..d38fa887
--- /dev/null
+++ b/avrftdi_tpi.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "pgm.h"
+#include "avrpart.h"
+
+//int avrftdi_tpi_write_byte(PROGRAMMER * pgm, unsigned char byte);
+//int avrftdi_tpi_read_byte(PROGRAMMER * pgm, unsigned char * byte);
+int avrftdi_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p);
+int avrftdi_tpi_chip_erase(PROGRAMMER * pgm, AVRPART * p);
+int avrftdi_cmd_tpi(PROGRAMMER * pgm, unsigned char cmd[], int cmd_len,
+		unsigned char res[], int res_len);
+int avrftdi_tpi_initialize(PROGRAMMER * pgm, AVRPART * p);
+
+