added TPI support for USBasp (patch #7440)

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@961 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
Thomas Fischl 2011-05-28 07:35:40 +00:00
parent fc13ef96a1
commit ddfbdc0b87
4 changed files with 476 additions and 39 deletions

View File

@ -1,3 +1,9 @@
2011-05-28 Thomas Fischl <tfischl@gmx.de>
Based on patch #7440 commited by Slawomir Fraś:
* usbasp.c: added TPI support for USBasp
* usbasp.h: (Ditto.)
2011-05-11 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
* avrdude.conf.in: Add support for ATmega168P.

View File

@ -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;
;
;

436
usbasp.c
View File

@ -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;
}

View File

@ -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;