From fc589638df6c76e0478e2f1eae6891e43585d389 Mon Sep 17 00:00:00 2001 From: fischl Date: Sat, 28 May 2011 07:35:40 +0000 Subject: [PATCH] added TPI support for USBasp (patch #7440) git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@961 81a1dc3b-b13d-400b-aceb-764788c761c2 --- ChangeLog | 6 + avrdude.conf.in | 16 ++ usbasp.c | 436 +++++++++++++++++++++++++++++++++++++++++++----- usbasp.h | 57 +++++++ 4 files changed, 476 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index bbc84fd2..102f03e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2011-05-28 Thomas Fischl + + Based on patch #7440 commited by Slawomir Fraƛ: + * usbasp.c: added TPI support for USBasp + * usbasp.h: (Ditto.) + 2011-05-11 Joerg Wunsch * avrdude.conf.in: Add support for ATmega168P. diff --git a/avrdude.conf.in b/avrdude.conf.in index 67034604..56071860 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -16641,22 +16641,26 @@ part memory "signature" size = 3; offset = 0x3fc0; + page_size = 16; ; memory "fuse" size = 1; offset = 0x3f40; + page_size = 16; blocksize = 4; ; memory "calibration" size = 1; offset = 0x3f80; + page_size = 16; ; memory "lockbits" size = 1; offset = 0x3f00; + page_size = 16; ; ; @@ -16681,22 +16685,26 @@ part memory "signature" size = 3; offset = 0x3fc0; + page_size = 16; ; memory "fuse" size = 1; offset = 0x3f40; + page_size = 16; blocksize = 4; ; memory "calibration" size = 1; offset = 0x3f80; + page_size = 16; ; memory "lockbits" size = 1; offset = 0x3f00; + page_size = 16; ; ; @@ -16721,22 +16729,26 @@ part memory "signature" size = 3; offset = 0x3fc0; + page_size = 16; ; memory "fuse" size = 1; offset = 0x3f40; + page_size = 16; blocksize = 4; ; memory "calibration" size = 1; offset = 0x3f80; + page_size = 16; ; memory "lockbits" size = 1; offset = 0x3f00; + page_size = 16; ; ; @@ -16761,22 +16773,26 @@ part memory "signature" size = 3; offset = 0x3fc0; + page_size = 16; ; memory "fuse" size = 1; offset = 0x3f40; + page_size = 16; blocksize = 4; ; memory "calibration" size = 1; offset = 0x3f80; + page_size = 16; ; memory "lockbits" size = 1; offset = 0x3f00; + page_size = 16; ; ; diff --git a/usbasp.c b/usbasp.c index c8ae501f..d2695bdb 100644 --- a/usbasp.c +++ b/usbasp.c @@ -106,11 +106,56 @@ struct pdata #else usb_dev_handle *usbhandle; #endif - int sckfreq_hz; + int sckfreq_hz; + unsigned int capabilities; + int use_tpi; }; #define PDATA(pgm) ((struct pdata *)(pgm->cookie)) +#define IMPORT_PDATA(pgm) struct pdata *pdata = PDATA(pgm) + + +/* Prototypes */ +// interface - management +static void usbasp_setup(PROGRAMMER * pgm); +static void usbasp_teardown(PROGRAMMER * pgm); +// internal functions +static int usbasp_transmit(PROGRAMMER * pgm, unsigned char receive, unsigned char functionid, unsigned char send[4], unsigned char * buffer, int buffersize); +#ifdef USE_LIBUSB_1_0 +static int usbOpenDevice(libusb_device_handle **device, int vendor, char *vendorName, int product, char *productName); +#else +static int usbOpenDevice(usb_dev_handle **device, int vendor, char *vendorName, int product, char *productName); +#endif +// interface - prog. +static int usbasp_open(PROGRAMMER * pgm, char * port); +static void usbasp_close(PROGRAMMER * pgm); +// dummy functions +static void usbasp_disable(PROGRAMMER * pgm); +static void usbasp_enable(PROGRAMMER * pgm); +static void usbasp_display(PROGRAMMER * pgm, const char * p); +// universal functions +static int usbasp_initialize(PROGRAMMER * pgm, AVRPART * p); +// SPI specific functions +static int usbasp_spi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4]); +static int usbasp_spi_program_enable(PROGRAMMER * pgm, AVRPART * p); +static int usbasp_spi_chip_erase(PROGRAMMER * pgm, AVRPART * p); +static int usbasp_spi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes); +static int usbasp_spi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes); +static int usbasp_spi_set_sck_period(PROGRAMMER *pgm, double sckperiod); +// TPI specific functions +static void usbasp_tpi_send_byte(PROGRAMMER * pgm, uint8_t b); +static int usbasp_tpi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4]); +static int usbasp_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p); +static int usbasp_tpi_chip_erase(PROGRAMMER * pgm, AVRPART * p); +static int usbasp_tpi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes); +static int usbasp_tpi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes); +static int usbasp_tpi_set_sck_period(PROGRAMMER *pgm, double sckperiod); +static int usbasp_tpi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr, unsigned char * value); +static int usbasp_tpi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr, unsigned char data); + + +/* Interface - management */ static void usbasp_setup(PROGRAMMER * pgm) { if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) { @@ -127,7 +172,7 @@ static void usbasp_teardown(PROGRAMMER * pgm) free(pgm->cookie); } - +/* Internal functions */ /* * wrapper for usb_control_msg call */ @@ -333,6 +378,8 @@ static int didUsbInit = 0; } #endif + +/* Interface - prog. */ static int usbasp_open(PROGRAMMER * pgm, char * port) { #ifdef USE_LIBUSB_1_0 @@ -377,13 +424,18 @@ static int usbasp_open(PROGRAMMER * pgm, char * port) return 0; } - static void usbasp_close(PROGRAMMER * pgm) { if (PDATA(pgm)->usbhandle!=NULL) { unsigned char temp[4]; memset(temp, 0, sizeof(temp)); - usbasp_transmit(pgm, 1, USBASP_FUNC_DISCONNECT, temp, temp, sizeof(temp)); + + if (PDATA(pgm)->use_tpi) { + usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_DISCONNECT, temp, temp, sizeof(temp)); + } else { + usbasp_transmit(pgm, 1, USBASP_FUNC_DISCONNECT, temp, temp, sizeof(temp)); + } + #ifdef USE_LIBUSB_1_0 libusb_close(PDATA(pgm)->usbhandle); #else @@ -393,24 +445,7 @@ static void usbasp_close(PROGRAMMER * pgm) } -static int usbasp_initialize(PROGRAMMER * pgm, AVRPART * p) -{ - - unsigned char temp[4]; - memset(temp, 0, sizeof(temp)); - - /* set sck period */ - pgm->set_sck_period(pgm, pgm->bitclock); - - /* connect to target device */ - usbasp_transmit(pgm, 1, USBASP_FUNC_CONNECT, temp, temp, sizeof(temp)); - - /* wait, so device is ready to receive commands */ - usleep(100000); - - return pgm->program_enable(pgm, p); -} - +/* Dummy functions */ static void usbasp_disable(PROGRAMMER * pgm) { /* Do nothing. */ @@ -431,7 +466,75 @@ static void usbasp_display(PROGRAMMER * pgm, const char * p) } -static int usbasp_cmd(PROGRAMMER * pgm, unsigned char cmd[4], +/* Universal functions: for both SPI and TPI */ +static int usbasp_initialize(PROGRAMMER * pgm, AVRPART * p) +{ + int dly; + unsigned char temp[4]; + unsigned char res[4]; + IMPORT_PDATA(pgm); + + + /* get capabilities */ + memset(temp, 0, sizeof(temp)); + if(usbasp_transmit(pgm, 1, USBASP_FUNC_GETCAPABILITIES, temp, res, sizeof(res)) == 4) + pdata->capabilities = res[0] | ((unsigned int)res[1] << 8) | ((unsigned int)res[2] << 16) | ((unsigned int)res[3] << 24); + else + pdata->capabilities = 0; + + pdata->use_tpi = ((pdata->capabilities & USBASP_CAP_TPI) != 0 && (p->flags & AVRPART_HAS_TPI) != 0) ? 1 : 0; + + if(pdata->use_tpi) + { + /* calc tpiclk delay */ + dly = 1500000.0 * pgm->bitclock; + if(dly < 1) + dly = 1; + else if(dly > 2047) + dly = 2047; + temp[0] = dly; + temp[1] = dly >> 8; + + /* connect */ + usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_CONNECT, temp, res, sizeof(res)); + + /* change interface */ + pgm->program_enable = usbasp_tpi_program_enable; + pgm->chip_erase = usbasp_tpi_chip_erase; + pgm->cmd = usbasp_tpi_cmd; + pgm->read_byte = usbasp_tpi_read_byte; + pgm->write_byte = usbasp_tpi_write_byte; + pgm->paged_write = usbasp_tpi_paged_write; + pgm->paged_load = usbasp_tpi_paged_load; + pgm->set_sck_period = usbasp_tpi_set_sck_period; + } + else + { + /* set sck period */ + pgm->set_sck_period(pgm, pgm->bitclock); + + /* connect to target device */ + usbasp_transmit(pgm, 1, USBASP_FUNC_CONNECT, temp, res, sizeof(res)); + + /* change interface */ + pgm->program_enable = usbasp_spi_program_enable; + pgm->chip_erase = usbasp_spi_chip_erase; + pgm->cmd = usbasp_spi_cmd; + pgm->read_byte = avr_read_byte_default; + pgm->write_byte = avr_write_byte_default; + pgm->paged_write = usbasp_spi_paged_write; + pgm->paged_load = usbasp_spi_paged_load; + pgm->set_sck_period = usbasp_spi_set_sck_period; + } + + /* wait, so device is ready to receive commands */ + usleep(100000); + + return pgm->program_enable(pgm, p); +} + +/* SPI specific functions */ +static int usbasp_spi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4]) { int nbytes = @@ -446,8 +549,7 @@ static int usbasp_cmd(PROGRAMMER * pgm, unsigned char cmd[4], return 0; } - -static int usbasp_program_enable(PROGRAMMER * pgm, AVRPART * p) +static int usbasp_spi_program_enable(PROGRAMMER * pgm, AVRPART * p) { unsigned char res[4]; unsigned char cmd[4]; @@ -468,8 +570,7 @@ static int usbasp_program_enable(PROGRAMMER * pgm, AVRPART * p) return 0; } - -static int usbasp_chip_erase(PROGRAMMER * pgm, AVRPART * p) +static int usbasp_spi_chip_erase(PROGRAMMER * pgm, AVRPART * p) { unsigned char cmd[4]; unsigned char res[4]; @@ -490,8 +591,7 @@ static int usbasp_chip_erase(PROGRAMMER * pgm, AVRPART * p) return 0; } - -static int usbasp_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, +static int usbasp_spi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes) { int n; @@ -557,7 +657,7 @@ static int usbasp_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, return n_bytes; } -static int usbasp_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, +static int usbasp_spi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes) { int n; @@ -630,7 +730,6 @@ static int usbasp_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, return n_bytes; } - /* The list of SCK frequencies in Hz supported by USBasp */ static struct sckoptions_t usbaspSCKoptions[] = { { USBASP_ISP_SCK_1500, 1500000 }, @@ -647,12 +746,11 @@ static struct sckoptions_t usbaspSCKoptions[] = { { USBASP_ISP_SCK_0_5, 500 } }; - /* * Set sck period (in seconds) * Find next possible sck period and write it to the programmer. */ -static int usbasp_set_sck_period(PROGRAMMER *pgm, double sckperiod) +static int usbasp_spi_set_sck_period(PROGRAMMER *pgm, double sckperiod) { char clockoption = USBASP_ISP_SCK_AUTO; unsigned char res[4]; @@ -714,6 +812,266 @@ static int usbasp_set_sck_period(PROGRAMMER *pgm, double sckperiod) return 0; } +/* TPI specific functions */ +static void usbasp_tpi_send_byte(PROGRAMMER * pgm, uint8_t b) +{ + unsigned char temp[4]; + memset(temp, 0, sizeof(temp)); + + temp[0] = b; + + usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_RAWWRITE, temp, temp, sizeof(temp)); +} + + +static int usbasp_tpi_recv_byte(PROGRAMMER * pgm) +{ + unsigned char temp[4]; + memset(temp, 0, sizeof(temp)); + + if(usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_RAWREAD, temp, temp, sizeof(temp)) != 1) + { + fprintf(stderr, "%s: error: wrong responds size\n", progname); + return -1; + } + + return temp[0]; +} + + +static int usbasp_tpi_nvm_waitbusy(PROGRAMMER * pgm) +{ + int retry; + + + for(retry=50; retry>0; retry--) + { + usbasp_tpi_send_byte(pgm, TPI_OP_SIN(NVMCSR)); + if(usbasp_tpi_recv_byte(pgm) & NVMCSR_BSY) + continue; + return 0; + } + return -1; +} + +static int usbasp_tpi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res[4]) +{ + fprintf(stderr, "%s: error: spi_cmd used in TPI mode: not allowed\n", progname); + return -1; +} + +static int usbasp_tpi_program_enable(PROGRAMMER * pgm, AVRPART * p) +{ + int retry; + + + /* change guard time */ + usbasp_tpi_send_byte(pgm, TPI_OP_SSTCS(TPIPCR)); + usbasp_tpi_send_byte(pgm, TPIPCR_GT_2b); + + /* send SKEY */ + usbasp_tpi_send_byte(pgm, 0xE0); + usbasp_tpi_send_byte(pgm, 0xFF); + usbasp_tpi_send_byte(pgm, 0x88); + usbasp_tpi_send_byte(pgm, 0xD8); + usbasp_tpi_send_byte(pgm, 0xCD); + usbasp_tpi_send_byte(pgm, 0x45); + usbasp_tpi_send_byte(pgm, 0xAB); + usbasp_tpi_send_byte(pgm, 0x89); + usbasp_tpi_send_byte(pgm, 0x12); + + /* check if device is ready */ + for(retry=0; retry<10; retry++) + { + usbasp_tpi_send_byte(pgm, TPI_OP_SLDCS(TPIIR)); + if(usbasp_tpi_recv_byte(pgm) != 0x80) + continue; + usbasp_tpi_send_byte(pgm, TPI_OP_SLDCS(TPISR)); + if((usbasp_tpi_recv_byte(pgm) & TPISR_NVMEN) == 0) + continue; + break; + } + if(retry >= 10) + { + fprintf(stderr, "%s: error: programm enable: target doesn't answer.\n", progname); + return -1; + } + + return 0; +} + +static int usbasp_tpi_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + /* Set PR to flash */ + usbasp_tpi_send_byte(pgm, TPI_OP_SSTPR(0)); + usbasp_tpi_send_byte(pgm, 0x01); + usbasp_tpi_send_byte(pgm, TPI_OP_SSTPR(1)); + usbasp_tpi_send_byte(pgm, 0x40); + /* select ERASE */ + usbasp_tpi_send_byte(pgm, TPI_OP_SOUT(NVMCMD)); + usbasp_tpi_send_byte(pgm, NVMCMD_CHIP_ERASE); + /* dummy write */ + usbasp_tpi_send_byte(pgm, TPI_OP_SST_INC); + usbasp_tpi_send_byte(pgm, 0x00); + usbasp_tpi_nvm_waitbusy(pgm); + + usleep(p->chip_erase_delay); + pgm->initialize(pgm, p); + + return 0; +} + +static int usbasp_tpi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes) +{ + unsigned char cmd[4]; + unsigned char* dptr; + int readed, clen, n; + uint16_t pr; + + + dptr = m->buf; + pr = m->offset; + readed = 0; + + while(readed < n_bytes) + { + clen = n_bytes - readed; + if(clen > 32) + clen = 32; + + /* prepare READBLOCK cmd */ + cmd[0] = pr & 0xFF; + cmd[1] = pr >> 8; + cmd[2] = 0; + cmd[3] = 0; + n = usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_READBLOCK, cmd, dptr, clen); + if(n != clen) + { + fprintf(stderr, "%s: error: wrong reading bytes %x\n", progname, n); + return -3; + } + + readed += clen; + pr += clen; + dptr += clen; + + report_progress(readed, n_bytes, NULL); + } + + return n_bytes; +} + +static int usbasp_tpi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes) +{ + unsigned char cmd[4]; + unsigned char dummy[8]; + unsigned char* sptr; + int writed, clen, n; + uint16_t pr; + + + sptr = m->buf; + pr = m->offset; + writed = 0; + + /* Set PR to flash */ + usbasp_tpi_send_byte(pgm, TPI_OP_SSTPR(0)); + usbasp_tpi_send_byte(pgm, (pr & 0xFF) | 1 ); + usbasp_tpi_send_byte(pgm, TPI_OP_SSTPR(1)); + usbasp_tpi_send_byte(pgm, (pr >> 8) ); + /* select SECTIONERASE */ + usbasp_tpi_send_byte(pgm, TPI_OP_SOUT(NVMCMD)); + usbasp_tpi_send_byte(pgm, NVMCMD_SECTION_ERASE); + /* dummy write */ + usbasp_tpi_send_byte(pgm, TPI_OP_SST); + usbasp_tpi_send_byte(pgm, 0x00); + usbasp_tpi_nvm_waitbusy(pgm); + + usleep(p->chip_erase_delay); + + while(writed < n_bytes) + { + clen = n_bytes - writed; + if(clen > 32) + clen = 32; + + /* prepare WRITEBLOCK cmd */ + cmd[0] = pr & 0xFF; + cmd[1] = pr >> 8; + cmd[2] = 0; + cmd[3] = 0; + n = usbasp_transmit(pgm, 0, USBASP_FUNC_TPI_WRITEBLOCK, cmd, sptr, clen); + if(n != clen) + { + fprintf(stderr, "%s: error: wrong count at writing %x\n", progname, n); + return -3; + } + + writed += clen; + pr += clen; + sptr += clen; + + report_progress(writed, n_bytes, NULL); + } + + /* finishing write */ + while(( clen = (-pr) & (m->page_size-1) ) != 0) + { + if(clen > 8) + clen = 8; + + memset(dummy, 0xFF, clen); + + cmd[0] = pr & 0xFF; + cmd[1] = pr >> 8; + cmd[2] = 0; + cmd[3] = 0; + n = usbasp_transmit(pgm, 0, USBASP_FUNC_TPI_WRITEBLOCK, cmd, dummy, clen); + if(n != clen) + { + fprintf(stderr, "%s: error: wrong count at writing %x\n", progname, n); + return -3; + } + + pr += clen; + } + + return n_bytes; +} + +static int usbasp_tpi_set_sck_period(PROGRAMMER *pgm, double sckperiod) +{ + return 0; +} +static int usbasp_tpi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr, unsigned char * value) +{ + unsigned char cmd[4]; + int n; + uint16_t pr; + + + pr = m->offset + addr; + + /* READBLOCK */ + cmd[0] = pr & 0xFF; + cmd[1] = pr >> 8; + cmd[2] = 0; + cmd[3] = 0; + n = usbasp_transmit(pgm, 1, USBASP_FUNC_TPI_READBLOCK, cmd, value, 1); + if(n != 1) + { + fprintf(stderr, "%s: error: wrong reading bytes %x\n", progname, n); + return -3; + } + return 0; +} + +static int usbasp_tpi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned long addr, unsigned char data) +{ + fprintf(stderr, "%s: error: usbasp_write_byte in TPI mode: all writes have to be done at page level\n", progname); + return -1; +} + void usbasp_initpgm(PROGRAMMER * pgm) { @@ -727,9 +1085,9 @@ void usbasp_initpgm(PROGRAMMER * pgm) pgm->display = usbasp_display; pgm->enable = usbasp_enable; pgm->disable = usbasp_disable; - pgm->program_enable = usbasp_program_enable; - pgm->chip_erase = usbasp_chip_erase; - pgm->cmd = usbasp_cmd; + pgm->program_enable = usbasp_spi_program_enable; + pgm->chip_erase = usbasp_spi_chip_erase; + pgm->cmd = usbasp_spi_cmd; pgm->open = usbasp_open; pgm->close = usbasp_close; pgm->read_byte = avr_read_byte_default; @@ -739,11 +1097,11 @@ void usbasp_initpgm(PROGRAMMER * pgm) * optional functions */ - pgm->paged_write = usbasp_paged_write; - pgm->paged_load = usbasp_paged_load; + pgm->paged_write = usbasp_spi_paged_write; + pgm->paged_load = usbasp_spi_paged_load; pgm->setup = usbasp_setup; pgm->teardown = usbasp_teardown; - pgm->set_sck_period = usbasp_set_sck_period; + pgm->set_sck_period = usbasp_spi_set_sck_period; } diff --git a/usbasp.h b/usbasp.h index c9610acf..91b6e8a8 100644 --- a/usbasp.h +++ b/usbasp.h @@ -45,6 +45,16 @@ #define USBASP_FUNC_WRITEEEPROM 8 #define USBASP_FUNC_SETLONGADDRESS 9 #define USBASP_FUNC_SETISPSCK 10 +#define USBASP_FUNC_TPI_CONNECT 11 +#define USBASP_FUNC_TPI_DISCONNECT 12 +#define USBASP_FUNC_TPI_RAWREAD 13 +#define USBASP_FUNC_TPI_RAWWRITE 14 +#define USBASP_FUNC_TPI_READBLOCK 15 +#define USBASP_FUNC_TPI_WRITEBLOCK 16 +#define USBASP_FUNC_GETCAPABILITIES 127 + +/* USBASP capabilities */ +#define USBASP_CAP_TPI 0x01 /* Block mode flags */ #define USBASP_BLOCKFLAG_FIRST 1 @@ -69,6 +79,53 @@ #define USBASP_ISP_SCK_750 11 /* 750 kHz */ #define USBASP_ISP_SCK_1500 12 /* 1.5 MHz */ +/* TPI instructions */ +#define TPI_OP_SLD 0x20 +#define TPI_OP_SLD_INC 0x24 +#define TPI_OP_SST 0x60 +#define TPI_OP_SST_INC 0x64 +#define TPI_OP_SSTPR(a) (0x68 | (a)) +#define TPI_OP_SIN(a) (0x10 | (((a)<<1)&0x60) | ((a)&0x0F) ) +#define TPI_OP_SOUT(a) (0x90 | (((a)<<1)&0x60) | ((a)&0x0F) ) +#define TPI_OP_SLDCS(a) (0x80 | ((a)&0x0F) ) +#define TPI_OP_SSTCS(a) (0xC0 | ((a)&0x0F) ) +#define TPI_OP_SKEY 0xE0 + +/* TPI control/status registers */ +#define TPIIR 0xF +#define TPIPCR 0x2 +#define TPISR 0x0 + +// TPIPCR bits +#define TPIPCR_GT_2 0x04 +#define TPIPCR_GT_1 0x02 +#define TPIPCR_GT_0 0x01 +#define TPIPCR_GT_128b 0x00 +#define TPIPCR_GT_64b 0x01 +#define TPIPCR_GT_32b 0x02 +#define TPIPCR_GT_16b 0x03 +#define TPIPCR_GT_8b 0x04 +#define TPIPCR_GT_4b 0x05 +#define TPIPCR_GT_2b 0x06 +#define TPIPCR_GT_0b 0x07 + +// TPISR bits +#define TPISR_NVMEN 0x02 + +/* NVM registers */ +#define NVMCSR 0x32 +#define NVMCMD 0x33 + +// NVMCSR bits +#define NVMCSR_BSY 0x80 + +// NVMCMD values +#define NVMCMD_NOP 0x00 +#define NVMCMD_CHIP_ERASE 0x10 +#define NVMCMD_SECTION_ERASE 0x14 +#define NVMCMD_WORD_WRITE 0x1D + + typedef struct sckoptions_t { int id; double frequency;