diff options
author | Ben Hutchings <ben.hutchings@codethink.co.uk> | 2016-04-12 07:58:14 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-04-13 03:12:41 -0400 |
commit | 1ff7760ff66b98ef244bf0e5e2bd5310651205ad (patch) | |
tree | fd7a3c5af84df055ec34c5de017819d20f6b0b38 | |
parent | ea1b60fb085839a9544cb3a0069992991beabb7f (diff) |
spi: spi-ti-qspi: Handle truncated frames properly
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>
Cc: stable@vger.kernel.org
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 32 |
1 files changed, 20 insertions, 12 deletions
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 0ee4139dec48..443f664534e1 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c | |||
@@ -236,16 +236,16 @@ static inline int ti_qspi_poll_wc(struct ti_qspi *qspi) | |||
236 | return -ETIMEDOUT; | 236 | return -ETIMEDOUT; |
237 | } | 237 | } |
238 | 238 | ||
239 | static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) | 239 | static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
240 | int count) | ||
240 | { | 241 | { |
241 | int wlen, count, xfer_len; | 242 | int wlen, xfer_len; |
242 | unsigned int cmd; | 243 | unsigned int cmd; |
243 | const u8 *txbuf; | 244 | const u8 *txbuf; |
244 | u32 data; | 245 | u32 data; |
245 | 246 | ||
246 | txbuf = t->tx_buf; | 247 | txbuf = t->tx_buf; |
247 | cmd = qspi->cmd | QSPI_WR_SNGL; | 248 | cmd = qspi->cmd | QSPI_WR_SNGL; |
248 | count = t->len; | ||
249 | wlen = t->bits_per_word >> 3; /* in bytes */ | 249 | wlen = t->bits_per_word >> 3; /* in bytes */ |
250 | xfer_len = wlen; | 250 | xfer_len = wlen; |
251 | 251 | ||
@@ -305,9 +305,10 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) | |||
305 | return 0; | 305 | return 0; |
306 | } | 306 | } |
307 | 307 | ||
308 | static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) | 308 | static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
309 | int count) | ||
309 | { | 310 | { |
310 | int wlen, count; | 311 | int wlen; |
311 | unsigned int cmd; | 312 | unsigned int cmd; |
312 | u8 *rxbuf; | 313 | u8 *rxbuf; |
313 | 314 | ||
@@ -324,7 +325,6 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) | |||
324 | cmd |= QSPI_RD_SNGL; | 325 | cmd |= QSPI_RD_SNGL; |
325 | break; | 326 | break; |
326 | } | 327 | } |
327 | count = t->len; | ||
328 | wlen = t->bits_per_word >> 3; /* in bytes */ | 328 | wlen = t->bits_per_word >> 3; /* in bytes */ |
329 | 329 | ||
330 | while (count) { | 330 | while (count) { |
@@ -355,12 +355,13 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) | |||
355 | return 0; | 355 | return 0; |
356 | } | 356 | } |
357 | 357 | ||
358 | static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t) | 358 | static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t, |
359 | int count) | ||
359 | { | 360 | { |
360 | int ret; | 361 | int ret; |
361 | 362 | ||
362 | if (t->tx_buf) { | 363 | if (t->tx_buf) { |
363 | ret = qspi_write_msg(qspi, t); | 364 | ret = qspi_write_msg(qspi, t, count); |
364 | if (ret) { | 365 | if (ret) { |
365 | dev_dbg(qspi->dev, "Error while writing\n"); | 366 | dev_dbg(qspi->dev, "Error while writing\n"); |
366 | return ret; | 367 | return ret; |
@@ -368,7 +369,7 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t) | |||
368 | } | 369 | } |
369 | 370 | ||
370 | if (t->rx_buf) { | 371 | if (t->rx_buf) { |
371 | ret = qspi_read_msg(qspi, t); | 372 | ret = qspi_read_msg(qspi, t, count); |
372 | if (ret) { | 373 | if (ret) { |
373 | dev_dbg(qspi->dev, "Error while reading\n"); | 374 | dev_dbg(qspi->dev, "Error while reading\n"); |
374 | return ret; | 375 | return ret; |
@@ -451,7 +452,8 @@ static int ti_qspi_start_transfer_one(struct spi_master *master, | |||
451 | struct spi_device *spi = m->spi; | 452 | struct spi_device *spi = m->spi; |
452 | struct spi_transfer *t; | 453 | struct spi_transfer *t; |
453 | int status = 0, ret; | 454 | int status = 0, ret; |
454 | unsigned int frame_len_words; | 455 | unsigned int frame_len_words, transfer_len_words; |
456 | int wlen; | ||
455 | 457 | ||
456 | /* setup device control reg */ | 458 | /* setup device control reg */ |
457 | qspi->dc = 0; | 459 | qspi->dc = 0; |
@@ -484,14 +486,20 @@ static int ti_qspi_start_transfer_one(struct spi_master *master, | |||
484 | qspi->cmd = ((qspi->cmd & ~QSPI_WLEN_MASK) | | 486 | qspi->cmd = ((qspi->cmd & ~QSPI_WLEN_MASK) | |
485 | QSPI_WLEN(t->bits_per_word)); | 487 | QSPI_WLEN(t->bits_per_word)); |
486 | 488 | ||
487 | ret = qspi_transfer_msg(qspi, t); | 489 | wlen = t->bits_per_word >> 3; |
490 | transfer_len_words = min(t->len / wlen, frame_len_words); | ||
491 | |||
492 | ret = qspi_transfer_msg(qspi, t, transfer_len_words * wlen); | ||
488 | if (ret) { | 493 | if (ret) { |
489 | dev_dbg(qspi->dev, "transfer message failed\n"); | 494 | dev_dbg(qspi->dev, "transfer message failed\n"); |
490 | mutex_unlock(&qspi->list_lock); | 495 | mutex_unlock(&qspi->list_lock); |
491 | return -EINVAL; | 496 | return -EINVAL; |
492 | } | 497 | } |
493 | 498 | ||
494 | m->actual_length += t->len; | 499 | m->actual_length += transfer_len_words * wlen; |
500 | frame_len_words -= transfer_len_words; | ||
501 | if (frame_len_words == 0) | ||
502 | break; | ||
495 | } | 503 | } |
496 | 504 | ||
497 | mutex_unlock(&qspi->list_lock); | 505 | mutex_unlock(&qspi->list_lock); |