Skip to content

Commit

Permalink
raw/ifpga/base: fix SPI transaction
Browse files Browse the repository at this point in the history
[ upstream commit eacdd03 ]

When EOP is detected, 2 more bytes should be received
(may be a SPI_PACKET_ESC before last valid byte) then
rx should be finished.

Fixes: 96ebfcf ("raw/ifpga/base: add SPI and MAX10 device driver")

Signed-off-by: Tianfei Zhang <tianfei.zhang@intel.com>
Acked-by: Rosen Xu <rosen.xu@intel.com>
  • Loading branch information
Figo-zhang authored and kevintraynor committed Feb 21, 2022
1 parent 4599a61 commit 81597d6
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 91 deletions.
12 changes: 12 additions & 0 deletions drivers/raw/ifpga/base/opae_spi.c
Expand Up @@ -239,6 +239,18 @@ int spi_command(struct altera_spi_device *dev, unsigned int chip_select,
return 0;
}

int spi_write(struct altera_spi_device *dev, unsigned int chip_select,
unsigned int wlen, void *wdata)
{
return spi_command(dev, chip_select, wlen, wdata, 0, NULL);
}

int spi_read(struct altera_spi_device *dev, unsigned int chip_select,
unsigned int rlen, void *rdata)
{
return spi_command(dev, chip_select, 0, NULL, rlen, rdata);
}

struct altera_spi_device *altera_spi_alloc(void *base, int type)
{
struct altera_spi_device *spi_dev =
Expand Down
4 changes: 4 additions & 0 deletions drivers/raw/ifpga/base/opae_spi.h
Expand Up @@ -117,6 +117,10 @@ struct spi_tran_header {
u32 addr;
};

int spi_read(struct altera_spi_device *dev, unsigned int chip_select,
unsigned int rlen, void *rdata);
int spi_write(struct altera_spi_device *dev, unsigned int chip_select,
unsigned int wlen, void *wdata);
int spi_command(struct altera_spi_device *dev, unsigned int chip_select,
unsigned int wlen, void *wdata, unsigned int rlen, void *rdata);
void spi_cs_deactivate(struct altera_spi_device *dev);
Expand Down
215 changes: 124 additions & 91 deletions drivers/raw/ifpga/base/opae_spi_transaction.c
Expand Up @@ -40,7 +40,7 @@ static void print_buffer(const char *string, void *buffer, int len)
printf("%s print buffer, len=%d\n", string, len);

for (i = 0; i < len; i++)
printf("%x ", *(p+i));
printf("%02x ", *(p+i));
printf("\n");
}
#else
Expand Down Expand Up @@ -72,43 +72,6 @@ static void reorder_phy_data(u8 bits_per_word,
}
}

enum {
SPI_FOUND_SOP,
SPI_FOUND_EOP,
SPI_NOT_FOUND,
};

static int resp_find_sop_eop(unsigned char *resp, unsigned int len,
int flags)
{
int ret = SPI_NOT_FOUND;

unsigned char *b = resp;

/* find SOP */
if (flags != SPI_FOUND_SOP) {
while (b < resp + len && *b != SPI_PACKET_SOP)
b++;

if (*b != SPI_PACKET_SOP)
goto done;

ret = SPI_FOUND_SOP;
}

/* find EOP */
while (b < resp + len && *b != SPI_PACKET_EOP)
b++;

if (*b != SPI_PACKET_EOP)
goto done;

ret = SPI_FOUND_EOP;

done:
return ret;
}

static void phy_tx_pad(unsigned char *phy_buf, unsigned int phy_buf_len,
unsigned int *aligned_len)
{
Expand Down Expand Up @@ -137,6 +100,104 @@ static void phy_tx_pad(unsigned char *phy_buf, unsigned int phy_buf_len,
*p++ = SPI_BYTE_IDLE;
}

#define RX_ALL_IDLE_DATA (SPI_BYTE_IDLE << 24 | SPI_BYTE_IDLE << 16 | \
SPI_BYTE_IDLE << 8 | SPI_BYTE_IDLE)

static bool all_idle_data(u8 *rxbuf)
{
return *(u32 *)rxbuf == RX_ALL_IDLE_DATA;
}

static unsigned char *find_eop(u8 *rxbuf, u32 BPW)
{
return memchr(rxbuf, SPI_PACKET_EOP, BPW);
}

static int do_spi_txrx(struct spi_transaction_dev *dev,
unsigned char *tx_buffer,
unsigned int tx_len, unsigned char *rx_buffer,
unsigned int rx_len,
unsigned int *actual_rx)
{
unsigned int rx_cnt = 0;
int ret = 0;
unsigned int BPW = 4;
bool eop_found = false;
unsigned char *eop;
unsigned char *ptr;
unsigned char *rxbuf = rx_buffer;
int add_byte = 0;
unsigned long ticks;
unsigned long timeout;

/* send command */
ret = spi_write(dev->dev, dev->chipselect, tx_len, tx_buffer);
if (ret)
return -EBUSY;

timeout = rte_get_timer_cycles() +
msecs_to_timer_cycles(2000);

/* read out data */
while (rx_cnt < rx_len) {
ret = spi_read(dev->dev, dev->chipselect, BPW, rxbuf);
if (ret)
return -EBUSY;

/* skip all of invalid data */
if (!eop_found && all_idle_data(rxbuf)) {
ticks = rte_get_timer_cycles();
if (!time_after(ticks, timeout)) {
continue;
} else {
dev_err(dev, "read spi data timeout\n");
return -ETIMEDOUT;
}
}

rx_cnt += BPW;
if (!eop_found) {
/* EOP is found, we read 2 more bytes and exit. */
eop = find_eop(rxbuf, BPW);
if (eop) {
if ((BPW + rxbuf - eop) > 2) {
/*
* check if the last 2 bytes are already
* received in current word.
*/
break;
} else if ((BPW + rxbuf - eop) == 2) {
/*
* skip if last byte is not SPI_BYTE_ESC
* or SPI_PACKET_ESC. this is the valid
* end of a response too.
*/
ptr = eop + 1;

if (*ptr != SPI_BYTE_ESC &&
*ptr != SPI_PACKET_ESC)
break;

add_byte = 1;
} else {
add_byte = 2;
}

rx_len = min(rx_len,
IFPGA_ALIGN(rx_cnt +
add_byte, BPW));
eop_found = true;
}
}
rxbuf += BPW;
}

*actual_rx = rx_cnt;
print_buffer("found valid data:", rx_buffer, rx_cnt);

return ret;
}

static int byte_to_core_convert(struct spi_transaction_dev *dev,
unsigned int send_len, unsigned char *send_data,
unsigned int resp_len, unsigned char *resp_data,
Expand All @@ -148,15 +209,9 @@ static int byte_to_core_convert(struct spi_transaction_dev *dev,
unsigned char *resp_packet = dev->buffer->bytes_resp;
unsigned char *p;
unsigned char current_byte;
unsigned char *tx_buffer;
unsigned int tx_len = 0;
unsigned char *rx_buffer;
unsigned int rx_len = 0;
int retry = 0;
int spi_flags;
unsigned long timeout = msecs_to_timer_cycles(1000);
unsigned long ticks;
unsigned int resp_max_len = 2 * resp_len;
unsigned int actual_rx;

print_buffer("before bytes:", send_data, send_len);

Expand Down Expand Up @@ -190,48 +245,15 @@ static int byte_to_core_convert(struct spi_transaction_dev *dev,

print_buffer("after order to spi:", send_packet, tx_len);

/* call spi */
tx_buffer = send_packet;
rx_buffer = resp_packet;
rx_len = resp_max_len;
spi_flags = SPI_NOT_FOUND;

read_again:
ret = spi_command(dev->dev, dev->chipselect, tx_len, tx_buffer,
rx_len, rx_buffer);
ret = do_spi_txrx(dev, send_packet, tx_len, resp_packet,
resp_max_len, &actual_rx);
if (ret)
return -EBUSY;

print_buffer("read from spi:", rx_buffer, rx_len);

/* look for SOP firstly*/
ret = resp_find_sop_eop(rx_buffer, rx_len - 1, spi_flags);
if (ret != SPI_FOUND_EOP) {
tx_buffer = NULL;
tx_len = 0;
ticks = rte_get_timer_cycles();
if (time_after(ticks, timeout) &&
retry++ > SPI_MAX_RETRY) {
dev_err(NULL, "Have retry %d, found invalid packet data\n",
retry);
return -EBUSY;
}

if (ret == SPI_FOUND_SOP) {
rx_buffer += rx_len;
resp_max_len += rx_len;
}

spi_flags = ret;
goto read_again;
}

print_buffer("found valid data:", resp_packet, resp_max_len);
return ret;

/* analyze response packet */
i = 0;
p = resp_data;
while (i < resp_max_len) {
while (i < actual_rx) {
current_byte = resp_packet[i];
switch (current_byte) {
case SPI_BYTE_IDLE:
Expand Down Expand Up @@ -337,9 +359,13 @@ static int packet_to_byte_conver(struct spi_transaction_dev *dev,
current_byte = resp_packet[i];

switch (current_byte) {
case SPI_PACKET_ESC:
case SPI_PACKET_CHANNEL:
case SPI_PACKET_SOP:
dev_err(dev, "error on get SOP after SOP\n");
return -EINVAL;
case SPI_PACKET_CHANNEL:
i += 2;
break;
case SPI_PACKET_ESC:
i++;
current_byte = resp_packet[i];
*p++ = xor_20(current_byte);
Expand All @@ -348,23 +374,30 @@ static int packet_to_byte_conver(struct spi_transaction_dev *dev,
case SPI_PACKET_EOP:
i++;
current_byte = resp_packet[i];
if (current_byte == SPI_PACKET_ESC ||
current_byte == SPI_PACKET_CHANNEL ||
current_byte == SPI_PACKET_SOP) {
switch (current_byte) {
case SPI_PACKET_ESC:
i++;
current_byte = resp_packet[i];
*p++ = xor_20(current_byte);
} else
break;
case SPI_PACKET_CHANNEL:
case SPI_PACKET_SOP:
case SPI_PACKET_EOP:
dev_err(dev, "error get SOP/EOP after EOP\n");
return -EINVAL;
default:
*p++ = current_byte;
i = valid_resp_len;
break;
break;
}
goto done;

default:
*p++ = current_byte;
i++;
}

}

done:
*valid = p - resp_buf;

print_buffer("after packet:", resp_buf, *valid);
Expand Down

0 comments on commit 81597d6

Please sign in to comment.