| From 1ff7760ff66b98ef244bf0e5e2bd5310651205ad Mon Sep 17 00:00:00 2001 |
| From: Ben Hutchings <ben.hutchings@codethink.co.uk> |
| Date: Tue, 12 Apr 2016 12:58:14 +0100 |
| Subject: spi: spi-ti-qspi: Handle truncated frames properly |
| |
| From: Ben Hutchings <ben.hutchings@codethink.co.uk> |
| |
| commit 1ff7760ff66b98ef244bf0e5e2bd5310651205ad upstream. |
| |
| We clamp frame_len_words to a maximum of 4096, but do not actually |
| limit the number of words written or read through the DATA registers |
| or the length added to spi_message::actual_length. This results in |
| silent data corruption for commands longer than this maximum. |
| |
| Recalculate the length of each transfer, taking frame_len_words into |
| account. Use this length in qspi_{read,write}_msg(), and to increment |
| spi_message::actual_length. |
| |
| Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/spi/spi-ti-qspi.c | 32 ++++++++++++++++++++------------ |
| 1 file changed, 20 insertions(+), 12 deletions(-) |
| |
| --- a/drivers/spi/spi-ti-qspi.c |
| +++ b/drivers/spi/spi-ti-qspi.c |
| @@ -225,16 +225,16 @@ static inline int ti_qspi_poll_wc(struct |
| return -ETIMEDOUT; |
| } |
| |
| -static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) |
| +static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
| + int count) |
| { |
| - int wlen, count, xfer_len; |
| + int wlen, xfer_len; |
| unsigned int cmd; |
| const u8 *txbuf; |
| u32 data; |
| |
| txbuf = t->tx_buf; |
| cmd = qspi->cmd | QSPI_WR_SNGL; |
| - count = t->len; |
| wlen = t->bits_per_word >> 3; /* in bytes */ |
| xfer_len = wlen; |
| |
| @@ -294,9 +294,10 @@ static int qspi_write_msg(struct ti_qspi |
| return 0; |
| } |
| |
| -static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) |
| +static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
| + int count) |
| { |
| - int wlen, count; |
| + int wlen; |
| unsigned int cmd; |
| u8 *rxbuf; |
| |
| @@ -313,7 +314,6 @@ static int qspi_read_msg(struct ti_qspi |
| cmd |= QSPI_RD_SNGL; |
| break; |
| } |
| - count = t->len; |
| wlen = t->bits_per_word >> 3; /* in bytes */ |
| |
| while (count) { |
| @@ -344,12 +344,13 @@ static int qspi_read_msg(struct ti_qspi |
| return 0; |
| } |
| |
| -static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t) |
| +static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
| + int count) |
| { |
| int ret; |
| |
| if (t->tx_buf) { |
| - ret = qspi_write_msg(qspi, t); |
| + ret = qspi_write_msg(qspi, t, count); |
| if (ret) { |
| dev_dbg(qspi->dev, "Error while writing\n"); |
| return ret; |
| @@ -357,7 +358,7 @@ static int qspi_transfer_msg(struct ti_q |
| } |
| |
| if (t->rx_buf) { |
| - ret = qspi_read_msg(qspi, t); |
| + ret = qspi_read_msg(qspi, t, count); |
| if (ret) { |
| dev_dbg(qspi->dev, "Error while reading\n"); |
| return ret; |
| @@ -374,7 +375,8 @@ static int ti_qspi_start_transfer_one(st |
| struct spi_device *spi = m->spi; |
| struct spi_transfer *t; |
| int status = 0, ret; |
| - unsigned int frame_len_words; |
| + unsigned int frame_len_words, transfer_len_words; |
| + int wlen; |
| |
| /* setup device control reg */ |
| qspi->dc = 0; |
| @@ -404,14 +406,20 @@ static int ti_qspi_start_transfer_one(st |
| qspi->cmd = ((qspi->cmd & ~QSPI_WLEN_MASK) | |
| QSPI_WLEN(t->bits_per_word)); |
| |
| - ret = qspi_transfer_msg(qspi, t); |
| + wlen = t->bits_per_word >> 3; |
| + transfer_len_words = min(t->len / wlen, frame_len_words); |
| + |
| + ret = qspi_transfer_msg(qspi, t, transfer_len_words * wlen); |
| if (ret) { |
| dev_dbg(qspi->dev, "transfer message failed\n"); |
| mutex_unlock(&qspi->list_lock); |
| return -EINVAL; |
| } |
| |
| - m->actual_length += t->len; |
| + m->actual_length += transfer_len_words * wlen; |
| + frame_len_words -= transfer_len_words; |
| + if (frame_len_words == 0) |
| + break; |
| } |
| |
| mutex_unlock(&qspi->list_lock); |