From 3ff432faedee2ec0b93d163e4808f391d65f1ba2 Mon Sep 17 00:00:00 2001 From: John Klug Date: Fri, 18 Aug 2017 15:14:17 -0500 Subject: mts-fpga-loader --- src/mts_fpga_spi.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 src/mts_fpga_spi.c (limited to 'src/mts_fpga_spi.c') diff --git a/src/mts_fpga_spi.c b/src/mts_fpga_spi.c new file mode 100644 index 0000000..1d95ac1 --- /dev/null +++ b/src/mts_fpga_spi.c @@ -0,0 +1,513 @@ +#include "mts_error_codes.h" +#include "mts_fpga_spi.h" + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#if DEBUG_SPI == 1 +#define DEBUG_MSG(str) fprintf(stderr, str) +#define DEBUG_PRINTF(fmt, args...) \ + fprintf(stderr, "%s:%d: " fmt, __FUNCTION__, __LINE__, args) +#define CHECK_NULL(a) \ + if (a == NULL) { \ + fprintf(stderr, "%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, \ + __LINE__); \ + return MTAC_SPI_ERROR; \ + } +#else +#define DEBUG_MSG(str) +#define DEBUG_PRINTF(fmt, args...) +#define CHECK_NULL(a) \ + if (a == NULL) { \ + return MTAC_SPI_ERROR; \ + } +#endif + +/* -------------------------------------------------------------------------- */ +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define READ_ACCESS 0x00 +#define WRITE_ACCESS 0x80 +#define SPI_SPEED 8000000 +/* -------------------------------------------------------------------------- */ +/* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */ + +/* + SPI initialization and configuration +*/ +int mtac_spi_open(char *spidev, void **spi_target_ptr) { + int *spi_device = NULL; + int dev; + int a = 0, b = 0; + int i; + + /* check input variables */ + CHECK_NULL(spi_target_ptr); + + /* allocate memory for the device descriptor */ + spi_device = malloc(sizeof(int)); + if (spi_device == NULL) { + DEBUG_MSG("ERROR: MALLOC FAIL\n"); + return MTAC_SPI_OPEN_FAILURE; + } + + /* open SPI device */ + dev = open(spidev, O_RDWR); + if (dev < 0) { + DEBUG_PRINTF("ERROR: failed to open SPI device %s\n", spidev); + return MTAC_SPI_OPEN_FAILURE; + } + + /* setting SPI mode to 'mode 0' */ + i = SPI_MODE_3; + a = ioctl(dev, SPI_IOC_WR_MODE, &i); + b = ioctl(dev, SPI_IOC_RD_MODE, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET IN MODE 0\n"); + close(dev); + free(spi_device); + return MTAC_SPI_OPEN_FAILURE; + } + + /* setting SPI max clk (in Hz) */ + i = SPI_SPEED; + a = ioctl(dev, SPI_IOC_WR_MAX_SPEED_HZ, &i); + b = ioctl(dev, SPI_IOC_RD_MAX_SPEED_HZ, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MAX SPEED\n"); + close(dev); + free(spi_device); + return MTAC_SPI_OPEN_FAILURE; + } + + /* setting SPI to MSB first */ + i = 0; + a = ioctl(dev, SPI_IOC_WR_LSB_FIRST, &i); + b = ioctl(dev, SPI_IOC_RD_LSB_FIRST, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET MSB FIRST\n"); + close(dev); + free(spi_device); + return MTAC_SPI_OPEN_FAILURE; + } + + /* setting SPI to 8 bits per word */ + i = 0; + a = ioctl(dev, SPI_IOC_WR_BITS_PER_WORD, &i); + b = ioctl(dev, SPI_IOC_RD_BITS_PER_WORD, &i); + if ((a < 0) || (b < 0)) { + DEBUG_MSG("ERROR: SPI PORT FAIL TO SET 8 BITS-PER-WORD\n"); + close(dev); + return MTAC_SPI_OPEN_FAILURE; + } + + *spi_device = dev; + *spi_target_ptr = (void *)spi_device; + return MTAC_SUCCESS; +} + +/* + Check Chip ID +*/ +int mtac_id(char *spidev, void *spi_target) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int ret; + size_t command_size = 6; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = 0x90; + out_buf[1] = 0x00; + out_buf[2] = 0x00; + out_buf[3] = 0x00; + out_buf[4] = 0x00; + out_buf[5] = 0x00; + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 0; + k.bits_per_word = 8; + ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + mtac_ret = mtac_disconnect(); + + /* determine return code */ + if (ret != (int)k.len) { + DEBUG_MSG("Chip transfer failed\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } + else if (((int)in_buf[4] == 0xFF && (int)in_buf[5] == 0xFF) || ((int)in_buf[4] == 0 && (int)in_buf[5] == 0)) { + printf("Unable to communicate with flash\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } + else { + printf("Manufacturer ID: %x %x",in_buf[4], in_buf[5]); + mtac_ret = MTAC_SUCCESS; + } + return mtac_ret; +} + +/* + Chip erase instruction sets all memory within + the device to the erased state of all 1s (FFh) +*/ +int chip_erase(char *spidev, void *spi_target) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int ret; + size_t command_size = 1; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = CHIP_ERASE; + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 0; + k.bits_per_word = 8; + ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + sleep(5); + mtac_ret = mtac_disconnect(); + + /* determine return code */ + if (ret != (int)k.len) { + DEBUG_MSG("Chip Erase transfer failed\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } else { + DEBUG_MSG("Chip Erase transfer successful\n"); + mtac_erase_verify(spidev, spi_target); + mtac_ret = MTAC_SUCCESS; + } + return mtac_ret; +} + +/* + Verify that the chip erase was successful +*/ +int mtac_erase_verify(char *spidev, void *spi_target) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int a, ret; + uint16_t command_size = 256 + 5; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = 0x0B; + out_buf[1] = 0x00; + out_buf[2] = 0x00; + out_buf[3] = 0x00; + out_buf[4] = 0x00; + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 1; + k.bits_per_word = 8; + ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + + /* determine return code */ + if (ret != (int)k.len) { + DEBUG_MSG("Chip transfer failed\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } else { + DEBUG_MSG("Chip transfer successful\n"); + mtac_ret = MTAC_SUCCESS; + } + mtac_ret = mtac_disconnect(); + /* verify that the chip was erased */ + for (a = 5; a < command_size; a++) { + if (in_buf[a] != 0xFF) { + mtac_disconnect(); + mtac_ret = MTAC_CHIP_ERASE_FAILURE; + return mtac_ret; + } + } + return mtac_ret; +} + +/* + Write enable instruction sets the write enable latch + in the status register to 1. It needs to be set before + a page can be programmed or chip erased +*/ +int write_enable(char *spidev, void *spi_target) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int ret; + size_t command_size = 1; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = 0x06; + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 0; + k.bits_per_word = 8; + ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + mtac_ret = mtac_disconnect(); + + /* determine return code */ + if (ret != (int)k.len) { + DEBUG_MSG("Write Enable failed"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } else { + usleep(1000); + DEBUG_MSG("Write Enable successful"); + mtac_ret = MTAC_SUCCESS; + } + return mtac_ret; +} + +/* + Release Power-down instruction releases the device from + said state allowing device communication +*/ +int mtac_release(char *spidev, void *spi_target) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int a, ret; + size_t command_size = 5; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = CHIP_RELEASE; + out_buf[1] = 0; + out_buf[2] = 0; + out_buf[3] = 0; + out_buf[4] = 0; + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 1; + k.bits_per_word = 8; + ret = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + mtac_ret = mtac_disconnect(); + + /* determine return code */ + if (ret != (int)k.len) { + DEBUG_MSG("\nRelease failed\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } else { + usleep(1000); + DEBUG_MSG("\nRelease successful\n"); + mtac_ret = MTAC_SUCCESS; + } + return mtac_ret; +} + +/* + Page Program instruction allows writing upto 256 bytes (a page) of + data to be programmed at previously erased memory locations Write + enable must be issued first +*/ +int page_program(char *spidev, void *spi_target, uint8_t adr_lower, uint8_t adr_higher, + int data[256]) { + int mtac_ret; + int spi_device; + struct spi_ioc_transfer k; + int a, ret, h; + size_t command_size = 260; + char out_buf[command_size]; + char in_buf[command_size]; + + /* prepare frame to be sent */ + out_buf[0] = PAGE_PROGRAM; + out_buf[1] = adr_higher; + out_buf[2] = adr_lower; + out_buf[3] = 0x00; + for (h = 0; h < 256; h++) { + out_buf[h + 4] = data[h]; + } + + /* I/O transaction */ + mtac_ret = mtac_spi_open(spidev, &spi_target); + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 1; + k.bits_per_word = 8; + a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + printf("Writing Page %x%x to MTAC\r", adr_higher, adr_lower); + usleep(10000); + for (ret = 0; ret < command_size; ret++) { + DEBUG_PRINTF("%x ", out_buf[ret]); + } + mtac_ret = mtac_disconnect(); + + /* determine return code */ + if (a != (int)k.len) { + DEBUG_MSG("ERROR: SPI WRITE FAILURE\n"); + mtac_ret = MTAC_SPI_TRANSFER_FAILURE; + } else { + DEBUG_MSG("Note: SPI write success\n"); + mtac_ret = MTAC_SUCCESS; + } + return mtac_ret; +} + +/* Simple spi write to fpga*/ +int mtac_spi_w(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, + uint8_t address, uint8_t data) { + int spi_device; + uint8_t out_buf[3]; + uint8_t command_size; + struct spi_ioc_transfer k; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + + spi_device = *(int *)spi_target; /* spi_target must not be null beforehand */ + + /* prepare frame to be sent */ + if (spi_mux_mode == MTAC_SPI_MUX_MODE1) { + out_buf[0] = spi_mux_target; + out_buf[1] = WRITE_ACCESS | (address & 0x7F); + out_buf[2] = data; + command_size = 3; + } else { + out_buf[0] = WRITE_ACCESS | (address & 0x7F); + out_buf[1] = data; + command_size = 2; + } + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.len = command_size; + k.speed_hz = SPI_SPEED; + k.cs_change = 1; + k.bits_per_word = 8; + a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + + /* determine return code */ + if (a != (int)k.len) { + DEBUG_MSG("ERROR: SPI WRITE FAILURE\n"); + return MTAC_SPI_TRANSFER_FAILURE; + } else { + DEBUG_MSG("Note: SPI write success\n"); + return MTAC_SUCCESS; + } +} + +/* Simple spi read to fpga */ +int mtac_spi_r(void *spi_target, uint8_t spi_mux_mode, uint8_t spi_mux_target, + uint8_t address, uint8_t *data) { + int spi_device; + uint8_t out_buf[3]; + uint8_t in_buf[ARRAY_SIZE(out_buf)]; + uint8_t command_size; + struct spi_ioc_transfer k; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + if ((address & 0x80) != 0) { + DEBUG_MSG("WARNING: SPI address > 127\n"); + } + CHECK_NULL(data); + + spi_device = *(int *)spi_target; /* spi_target cannot be null beforehand */ + + /* prepare frame to be sent */ + if (spi_mux_mode == MTAC_SPI_MUX_MODE1) { + out_buf[0] = spi_mux_target; + out_buf[1] = READ_ACCESS | (address & 0x7F); + out_buf[2] = 0x00; + command_size = 3; + } else { + out_buf[0] = READ_ACCESS | (address & 0x7F); + out_buf[1] = 0x00; + command_size = 2; + } + + /* I/O transaction */ + memset(&k, 0, sizeof(k)); /* clear k */ + k.tx_buf = (unsigned long)out_buf; + k.rx_buf = (unsigned long)in_buf; + k.len = command_size; + k.cs_change = 1; + a = ioctl(spi_device, SPI_IOC_MESSAGE(1), &k); + + /* determine return code */ + if (a != (int)k.len) { + DEBUG_MSG("ERROR: SPI READ FAILURE\n"); + return MTAC_SPI_TRANSFER_FAILURE; + } else { + DEBUG_MSG("Note: SPI read success\n"); + *data = in_buf[command_size - 1]; + return MTAC_SUCCESS; + } +} + +/* + SPI release +*/ +int mtac_spi_close(void *spi_target) { + int spi_device; + int a; + + /* check input variables */ + CHECK_NULL(spi_target); + + /* close file & deallocate file descriptor */ + spi_device = *(int *)spi_target; /* check that spi_target is not null */ + a = close(spi_device); + free(spi_target); + + /* determine return code */ + if (a < 0) { + DEBUG_MSG("ERROR: SPI PORT FAILED TO CLOSE\n"); + return MTAC_SPI_CLOSE_FAILURE; + } else { + DEBUG_MSG("Note: SPI port closed\n"); + return MTAC_SUCCESS; + } +} \ No newline at end of file -- cgit v1.2.3