2013-05-02 11:06:15 +00:00
|
|
|
#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>
|
|
|
|
|
2013-05-02 11:06:45 +00:00
|
|
|
static void avrftdi_tpi_disable(PROGRAMMER *);
|
|
|
|
|
2013-05-02 11:06:15 +00:00
|
|
|
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;
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_debug("%s\n", line0);
|
|
|
|
log_debug("%s\n", line1);
|
|
|
|
//log_debug("%s\n", line2);
|
2013-05-02 11:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 };
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_info("Using TPI interface\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
pgm->program_enable = avrftdi_tpi_program_enable;
|
|
|
|
pgm->cmd_tpi = avrftdi_cmd_tpi;
|
|
|
|
pgm->chip_erase = avrftdi_tpi_chip_erase;
|
2013-05-02 11:06:45 +00:00
|
|
|
pgm->disable = avrftdi_tpi_disable;
|
|
|
|
|
|
|
|
pgm->paged_load = NULL;
|
|
|
|
pgm->paged_write = NULL;
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_info("Setting /Reset pin low\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
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);
|
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
log_info("Sending 16 init clock cycles ...\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
ret = ftdi_write_data(pdata->ftdic, buf, sizeof(buf));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
#define TPI_PARITY_MASK 0x2000
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
tpi_byte2frame(uint8_t byte)
|
|
|
|
{
|
|
|
|
uint16_t frame = 0xc00f;
|
|
|
|
int parity = __builtin_popcount(byte) & 1;
|
|
|
|
|
|
|
|
frame |= ((byte << 5) & 0x1fe0);
|
|
|
|
|
|
|
|
if(parity)
|
2013-05-02 11:06:52 +00:00
|
|
|
frame |= TPI_PARITY_MASK;
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
return frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
tpi_frame2byte(uint16_t frame, uint8_t * byte)
|
|
|
|
{
|
2013-05-02 11:06:52 +00:00
|
|
|
/* drop idle and start bit(s) */
|
|
|
|
*byte = (frame >> 5) & 0xff;
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
int parity = __builtin_popcount(*byte) & 1;
|
2013-05-02 11:06:52 +00:00
|
|
|
int parity_rcvd = (frame & TPI_PARITY_MASK) ? 1 : 0;
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
return parity != parity_rcvd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
avrftdi_tpi_break(PROGRAMMER * pgm)
|
|
|
|
{
|
2013-05-02 11:06:37 +00:00
|
|
|
unsigned char buffer[] = { MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_LSB, 1, 0, 0, 0 };
|
2013-05-02 11:06:15 +00:00
|
|
|
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;
|
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
log_trace("Byte %02x, frame: %04x, MPSSE: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
|
|
|
|
byte, frame, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
//avrftdi_debug_frame(frame);
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
E(ftdi_write_data(ftdic, buffer, sizeof(buffer)) != sizeof(buffer), ftdic);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
#define TPI_FRAME_SIZE 12
|
|
|
|
#define TPI_IDLE_BITS 2
|
|
|
|
|
2013-05-02 11:06:15 +00:00
|
|
|
static int
|
|
|
|
avrftdi_tpi_read_byte(PROGRAMMER * pgm, unsigned char * byte)
|
|
|
|
{
|
|
|
|
uint16_t frame;
|
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
/* use 2 guard bits, 2 default idle bits + 12 frame bits = 16 bits total */
|
|
|
|
const int bytes = 3;
|
|
|
|
int err, i = 0;
|
|
|
|
|
|
|
|
unsigned char buffer[4];
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
/* set it high, so the TPI won't detect we're driving the line */
|
2013-05-02 11:06:15 +00:00
|
|
|
to_pdata(pgm)->set_pin(pgm, PIN_AVR_MOSI, ON);
|
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
buffer[0] = MPSSE_DO_READ | MPSSE_LSB;
|
2013-05-02 11:06:15 +00:00
|
|
|
buffer[1] = (bytes-1) & 0xff;
|
|
|
|
buffer[2] = ((bytes-1) >> 8) & 0xff;
|
|
|
|
buffer[3] = SEND_IMMEDIATE;
|
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
log_trace("MPSSE: 0x%02x 0x%02x 0x%02x 0x%02x (Read frame)\n",
|
2013-05-02 11:06:15 +00:00
|
|
|
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 {
|
2013-05-02 11:06:52 +00:00
|
|
|
int err = ftdi_read_data(to_pdata(pgm)->ftdic, &buffer[i], bytes - i);
|
|
|
|
E(err < 0, to_pdata(pgm)->ftdic);
|
|
|
|
i += err;
|
2013-05-02 11:06:15 +00:00
|
|
|
} while(i < bytes);
|
|
|
|
|
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
log_trace("MPSSE: 0x%02x 0x%02x 0x%02x 0x%02x (Read frame)\n",
|
|
|
|
buffer[0], buffer[1], buffer[2], buffer[3]);
|
|
|
|
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
frame = buffer[0] | (buffer[1] << 8);
|
|
|
|
|
|
|
|
err = tpi_frame2byte(frame, byte);
|
|
|
|
log_trace("Frame: 0x%04x, byte: 0x%02x\n", frame, *byte);
|
|
|
|
|
|
|
|
//avrftdi_debug_frame(frame);
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:06:52 +00:00
|
|
|
return err;
|
2013-05-02 11:06:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
avrftdi_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p)
|
|
|
|
{
|
|
|
|
int retry;
|
|
|
|
int err;
|
|
|
|
int i;
|
|
|
|
unsigned char byte = 0;
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_info("TPI program enable\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
/* set guard time */
|
2013-05-02 11:06:52 +00:00
|
|
|
avrftdi_tpi_write_byte(pgm, TPI_OP_SSTCS(TPIPCR));
|
|
|
|
avrftdi_tpi_write_byte(pgm, TPIPCR_GT_2b);
|
2013-05-02 11:06:15 +00:00
|
|
|
|
|
|
|
/* 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++)
|
|
|
|
{
|
2013-05-02 11:06:22 +00:00
|
|
|
log_info("Reading Identification register\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
avrftdi_tpi_write_byte(pgm, TPI_OP_SLDCS(TPIIR));
|
|
|
|
err = avrftdi_tpi_read_byte(pgm, &byte);
|
2013-05-02 11:07:00 +00:00
|
|
|
if(err || byte != TPI_IDENT_CODE)
|
2013-05-02 11:06:15 +00:00
|
|
|
{
|
2013-05-02 11:07:00 +00:00
|
|
|
log_err("Error. Sending break.\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
avrftdi_tpi_break(pgm);
|
|
|
|
avrftdi_tpi_break(pgm);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_info("Reading Status register\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
avrftdi_tpi_write_byte(pgm, TPI_OP_SLDCS(TPISR));
|
|
|
|
err = avrftdi_tpi_read_byte(pgm, &byte);
|
|
|
|
if(err || !(byte & TPISR_NVMEN))
|
|
|
|
{
|
2013-05-02 11:07:00 +00:00
|
|
|
log_err("Error. Sending break.\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
avrftdi_tpi_break(pgm);
|
|
|
|
avrftdi_tpi_break(pgm);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-02 11:06:22 +00:00
|
|
|
log_err("Error connecting to target.\n");
|
2013-05-02 11:06:15 +00:00
|
|
|
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);
|
2013-05-02 11:07:00 +00:00
|
|
|
//TODO usleep on bsy?
|
2013-05-02 11:06:15 +00:00
|
|
|
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);
|
|
|
|
|
2013-05-02 11:06:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
avrftdi_tpi_disable(PROGRAMMER * pgm)
|
|
|
|
{
|
|
|
|
log_info("Leaving Programming mode.\n");
|
|
|
|
avrftdi_tpi_write_byte(pgm, TPI_OP_SSTCS(TPIPCR));
|
|
|
|
avrftdi_tpi_write_byte(pgm, 0);
|
2013-05-02 11:06:15 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
#else /* HAVE_LIBFTDI1 */
|
|
|
|
|
|
|
|
#endif /* HAVE_LIBFTDI1 */
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
#else /* HAVE_LIBUSB_1_0 */
|
2013-05-02 11:06:15 +00:00
|
|
|
|
2013-05-02 11:07:00 +00:00
|
|
|
#endif /* HAVE_LIBUSB_1_0 */
|
2013-05-02 11:06:15 +00:00
|
|
|
|