openFPGALoader/src/ftdispi.cpp

292 lines
6.2 KiB
C++
Raw Normal View History

2021-06-26 15:24:07 +02:00
// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
2019-09-26 18:29:20 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <ftdi.h>
#include <unistd.h>
#include <string.h>
2020-10-30 08:24:45 +01:00
#include "board.hpp"
2019-09-26 18:29:20 +02:00
#include "ftdipp_mpsse.hpp"
#include "ftdispi.hpp"
/*
* SCLK -> ADBUS0
* MOSI -> ADBUS1
* MISO -> ADBUS2
* CS -> ADBUS3
*/
/* GGM: Faut aussi definir l'etat des broches par defaut */
/* necessaire en mode0 et 1, ainsi qu'entre 2 et 3
*/
/* Rappel :
* Mode0 : clk idle low, ecriture avant le premier front
* ie lecture sur le premier front (montant)
* Mode1 : clk idle low, ecriture sur le premier front (montant)
* lecture sur le second front (descendant)
* Mode2 : clk idle high, ecriture avant le premier front
* lecture sur le premier front (descendant)
* Mode3 : clk idle high, ecriture sur le premier front (descendant)
* lecture sur le second front (montant)
*/
void FtdiSpi::setMode(uint8_t mode)
{
switch (mode) {
case 0:
2020-10-30 08:24:45 +01:00
_clk_idle = 0;
2019-09-26 18:29:20 +02:00
_wr_mode = MPSSE_WRITE_NEG;
_rd_mode = 0;
break;
case 1:
2020-10-30 08:24:45 +01:00
_clk_idle = 0;
2019-09-26 18:29:20 +02:00
_wr_mode = 0;
_rd_mode = MPSSE_READ_NEG;
break;
case 2:
2020-10-30 08:24:45 +01:00
_clk_idle = _clk;
2019-09-26 18:29:20 +02:00
_wr_mode = 0; //POS
_rd_mode = MPSSE_READ_NEG;
break;
case 3:
2020-10-30 08:24:45 +01:00
_clk_idle = _clk;
2019-09-26 18:29:20 +02:00
_wr_mode = MPSSE_WRITE_NEG;
_rd_mode = 0;
break;
}
2020-10-28 21:26:33 +01:00
/* for clk pin in idle state */
2020-10-30 08:24:45 +01:00
if (_clk_idle)
gpio_set(_clk);
2020-10-28 21:26:33 +01:00
else
gpio_clear(_clk);
2019-09-26 18:29:20 +02:00
}
static FTDIpp_MPSSE::mpsse_bit_config bit_conf =
{0x403, 0x6010, INTERFACE_B, 0x08, 0x0B, 0x08, 0x0B};
2019-09-26 18:29:20 +02:00
2019-11-21 08:52:20 +01:00
FtdiSpi::FtdiSpi(int vid, int pid, unsigned char interface, uint32_t clkHZ,
bool verbose):
2020-09-29 14:16:30 +02:00
FTDIpp_MPSSE(bit_conf, "", "", clkHZ, verbose)
2019-09-26 18:29:20 +02:00
{
2020-12-13 00:48:45 +01:00
(void)pid;
(void)vid;
(void)interface;
2020-10-06 08:36:31 +02:00
setMode(0);
2019-09-26 18:29:20 +02:00
setCSmode(SPI_CS_AUTO);
setEndianness(SPI_MSB_FIRST);
init(1, 0x00, BITMODE_MPSSE);
2019-09-26 18:29:20 +02:00
}
2020-08-23 17:17:51 +02:00
2020-10-30 08:24:45 +01:00
FtdiSpi::FtdiSpi(const FTDIpp_MPSSE::mpsse_bit_config &conf,
spi_pins_conf_t spi_config,
uint32_t clkHZ, bool verbose):
FTDIpp_MPSSE(conf, "", "", clkHZ, verbose),
_cs_bits(1 << 3), _clk(1 << 0), _holdn(0), _wpn(0)
2020-08-23 17:17:51 +02:00
{
2020-10-30 08:24:45 +01:00
if (spi_config.cs_pin)
_cs_bits = spi_config.cs_pin;
if (spi_config.sck_pin)
_clk = spi_config.sck_pin;
if (spi_config.holdn_pin)
_holdn = spi_config.holdn_pin;
if (spi_config.wpn_pin)
_holdn = spi_config.wpn_pin;
2020-10-30 08:24:45 +01:00
/* clk is fixed by MPSSE engine
* but CS, holdn, wpn are free -> update bits direction
2020-10-30 08:24:45 +01:00
*/
gpio_set_output(_cs_bits | _holdn | _wpn);
gpio_set(_cs_bits | _holdn | _wpn);
2020-10-30 08:24:45 +01:00
2020-10-06 08:36:31 +02:00
setMode(0);
2020-08-23 17:17:51 +02:00
setCSmode(SPI_CS_AUTO);
setEndianness(SPI_MSB_FIRST);
init(1, 0x00, BITMODE_MPSSE);
2020-08-23 17:17:51 +02:00
}
2019-09-26 18:29:20 +02:00
FtdiSpi::~FtdiSpi()
{
}
2020-10-28 21:26:33 +01:00
/* send two consecutive cs configuration */
bool FtdiSpi::confCs(char stat)
2019-09-26 18:29:20 +02:00
{
2020-10-28 21:26:33 +01:00
bool ret;
if (stat == 0) {
ret = gpio_clear(_cs_bits);
ret |= gpio_clear(_cs_bits);
2020-10-28 21:26:33 +01:00
} else {
ret = gpio_set(_cs_bits);
ret |= gpio_set(_cs_bits);
2019-09-26 18:29:20 +02:00
}
2020-10-28 21:26:33 +01:00
if (!ret)
printf("Error: CS update\n");
2019-09-26 18:29:20 +02:00
return ret;
}
2020-10-28 21:26:33 +01:00
bool FtdiSpi::setCs()
2019-09-26 18:29:20 +02:00
{
2020-10-30 08:24:45 +01:00
_cs = _cs_bits;
2020-10-28 21:26:33 +01:00
return confCs(_cs);
2019-09-26 18:29:20 +02:00
}
2020-10-28 21:26:33 +01:00
bool FtdiSpi::clearCs()
2019-09-26 18:29:20 +02:00
{
_cs = 0x00;
2020-10-28 21:26:33 +01:00
return confCs(_cs);
2019-09-26 18:29:20 +02:00
}
int FtdiSpi::ft2232_spi_wr_then_rd(
const uint8_t *tx_data, uint32_t tx_len,
uint8_t *rx_data, uint32_t rx_len)
{
setCSmode(SPI_CS_MANUAL);
clearCs();
uint32_t ret = ft2232_spi_wr_and_rd(tx_len, tx_data, NULL);
if (ret != 0) {
printf("%s : write error %d %d\n", __func__, ret, tx_len);
} else {
ret = ft2232_spi_wr_and_rd(rx_len, NULL, rx_data);
if (ret != 0) {
printf("%s : read error\n", __func__);
}
}
setCs();
setCSmode(SPI_CS_AUTO);
return ret;
}
/* Returns 0 upon success, a negative number upon errors. */
int FtdiSpi::ft2232_spi_wr_and_rd(//struct ftdi_spi *spi,
uint32_t writecnt,
const uint8_t * writearr, uint8_t * readarr)
{
2021-06-26 08:06:26 +02:00
uint32_t max_xfer = (readarr) ? _buffer_size : 4096;
uint8_t buf[max_xfer];
2020-10-28 21:26:33 +01:00
int i = 0;
2019-09-26 18:29:20 +02:00
int ret = 0;
uint8_t *rx_ptr = readarr;
uint8_t *tx_ptr = (uint8_t *)writearr;
2020-08-23 17:17:51 +02:00
uint32_t len = writecnt;
uint32_t xfer;
2019-09-26 18:29:20 +02:00
if (_cs_mode == SPI_CS_AUTO) {
2020-10-28 21:26:33 +01:00
clearCs();
2019-09-26 18:29:20 +02:00
}
2020-08-23 17:17:51 +02:00
mpsse_write();
2019-09-26 18:29:20 +02:00
/*
* Minimize USB transfers by packing as many commands as possible
* together. If we're not expecting to read, we can assert CS#, write,
* and deassert CS# all in one shot. If reading, we do three separate
* operations.
*/
while (len > 0) {
2021-06-26 08:06:26 +02:00
xfer = (len > max_xfer) ? max_xfer : len;
2019-09-26 18:29:20 +02:00
2020-10-28 21:26:33 +01:00
buf[i++] = ((readarr) ? (MPSSE_DO_READ | _rd_mode) : 0) |
((writearr) ? (MPSSE_DO_WRITE | _wr_mode) : 0);
2019-09-26 18:29:20 +02:00
buf[i++] = (xfer - 1) & 0xff;
buf[i++] = ((xfer - 1) >> 8) & 0xff;
if (writearr) {
memcpy(buf + i, tx_ptr, xfer);
tx_ptr += xfer;
i += xfer;
}
ret = mpsse_store(buf, i);
if (ret)
2020-10-28 21:26:33 +01:00
printf("send_buf failed before read: %i %s\n", ret, ftdi_get_error_string(_ftdi));
2019-09-26 18:29:20 +02:00
i = 0;
if (readarr) {
//if (ret == 0) {
ret = mpsse_read(rx_ptr, xfer);
2020-10-28 21:26:33 +01:00
if ((uint32_t)ret != xfer)
2019-09-26 18:29:20 +02:00
printf("get_buf failed: %i\n", ret);
//}
rx_ptr += xfer;
2020-08-23 17:17:51 +02:00
} else {
ret = mpsse_write();
2020-10-28 21:26:33 +01:00
if ((uint32_t)ret != xfer+3)
2020-08-23 17:17:51 +02:00
printf("error %d %d\n", ret, i);
2019-09-26 18:29:20 +02:00
}
len -= xfer;
}
if (_cs_mode == SPI_CS_AUTO) {
2020-10-28 21:26:33 +01:00
if (!setCs())
printf("send_buf failed at write %d\n", ret);
2019-09-26 18:29:20 +02:00
}
2020-10-28 21:26:33 +01:00
return 0;
2019-09-26 18:29:20 +02:00
}
2020-08-23 17:17:51 +02:00
/* method spiInterface::spi_put */
int FtdiSpi::spi_put(uint8_t cmd, uint8_t *tx, uint8_t *rx, uint32_t len)
{
uint32_t xfer_len = len + 1;
uint8_t jtx[xfer_len];
uint8_t jrx[xfer_len];
jtx[0] = cmd;
if (tx != NULL)
memcpy(jtx+1, tx, len);
/* send first alreay stored cmd,
* in the same time store each byte
* to next
*/
ft2232_spi_wr_and_rd(xfer_len, jtx, (rx != NULL)?jrx:NULL);
if (rx != NULL)
memcpy(rx, jrx+1, len);
return 0;
}
/* method spiInterface::spi_put */
int FtdiSpi::spi_put(uint8_t *tx, uint8_t *rx, uint32_t len)
{
return ft2232_spi_wr_and_rd(len, tx, rx);
}
/* method spiInterface::spi_wait
*/
int FtdiSpi::spi_wait(uint8_t cmd, uint8_t mask, uint8_t cond,
uint32_t timeout, bool verbose)
{
uint8_t rx;
uint32_t count = 0;
setCSmode(SPI_CS_MANUAL);
clearCs();
ft2232_spi_wr_and_rd(1, &cmd, NULL);
do {
ft2232_spi_wr_and_rd(1, NULL, &rx);
count ++;
if (count == timeout) {
printf("timeout: %2x %d\n", rx, count);
break;
}
if (verbose) {
printf("%02x %02x %02x %02x\n", rx, mask, cond, count);
}
} while((rx & mask) != cond);
setCs();
setCSmode(SPI_CS_AUTO);
if (count == timeout) {
printf("%x\n", rx);
std::cout << "wait: Error" << std::endl;
return -ETIME;
} else
return 0;
}