aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorNed Forrester <nforrester@whoi.edu>2008-09-13 05:33:17 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-09-13 17:41:51 -0400
commit8423597d676615f3dd2d9ab36f59f147086b90b8 (patch)
tree3a93559a27bf1bb616ab74d3edcc992916645f44 /drivers/spi
parentaa77d96ba94326db4f50d2aa36602824dd03286a (diff)
pxa2xx_spi: chipselect bugfixes
Fixes several chipselect bugs in the pxa2xx_spi driver. These bugs are in all versions of this driver and prevent using it with chips like m25p16 flash. 1. The spi_transfer.cs_change flag is handled too early: before spi_transfer.delay_usecs applies, thus making the delay ineffective at holding chip select. 2. spi_transfer.delay_usecs is ignored on the last transfer of a message (likewise not holding chipselect long enough). 3. If spi_transfer.cs_change is set on the last transfer, the chip select is always disabled, instead of the intended meaning: optionally holding chip select enabled for the next message. Those first three bugs were fixed with a relocation of delays and chip select de-assertions. 4. If a message has the cs_change flag set on the last transfer, and had the chip select stayed enabled as requested (see 3, above), it would not have been disabled if the next message is for a different chip. Fixed by dropping chip select regardless of cs_change at end of a message, if there is no next message or if the next message is for a different chip. This patch should apply to all kernels back to and including 2.6.20; it was test patched against 2.6.20. An additional patch would be required for older kernels, but those versions are very buggy anyway. Signed-off-by: Ned Forrester <nforrester@whoi.edu> Cc: Vernon Sauder <vernoninhand@gmail.com> Cc: Eric Miao <eric.y.miao@gmail.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Cc: <stable@kernel.org> [2.6.25.x, 2.6.26.x] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/pxa2xx_spi.c59
1 files changed, 48 insertions, 11 deletions
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index 34c7c9875681..a7b9070e1b46 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -144,7 +144,6 @@ struct driver_data {
144 size_t tx_map_len; 144 size_t tx_map_len;
145 u8 n_bytes; 145 u8 n_bytes;
146 u32 dma_width; 146 u32 dma_width;
147 int cs_change;
148 int (*write)(struct driver_data *drv_data); 147 int (*write)(struct driver_data *drv_data);
149 int (*read)(struct driver_data *drv_data); 148 int (*read)(struct driver_data *drv_data);
150 irqreturn_t (*transfer_handler)(struct driver_data *drv_data); 149 irqreturn_t (*transfer_handler)(struct driver_data *drv_data);
@@ -406,8 +405,45 @@ static void giveback(struct driver_data *drv_data)
406 struct spi_transfer, 405 struct spi_transfer,
407 transfer_list); 406 transfer_list);
408 407
408 /* Delay if requested before any change in chip select */
409 if (last_transfer->delay_usecs)
410 udelay(last_transfer->delay_usecs);
411
412 /* Drop chip select UNLESS cs_change is true or we are returning
413 * a message with an error, or next message is for another chip
414 */
409 if (!last_transfer->cs_change) 415 if (!last_transfer->cs_change)
410 drv_data->cs_control(PXA2XX_CS_DEASSERT); 416 drv_data->cs_control(PXA2XX_CS_DEASSERT);
417 else {
418 struct spi_message *next_msg;
419
420 /* Holding of cs was hinted, but we need to make sure
421 * the next message is for the same chip. Don't waste
422 * time with the following tests unless this was hinted.
423 *
424 * We cannot postpone this until pump_messages, because
425 * after calling msg->complete (below) the driver that
426 * sent the current message could be unloaded, which
427 * could invalidate the cs_control() callback...
428 */
429
430 /* get a pointer to the next message, if any */
431 spin_lock_irqsave(&drv_data->lock, flags);
432 if (list_empty(&drv_data->queue))
433 next_msg = NULL;
434 else
435 next_msg = list_entry(drv_data->queue.next,
436 struct spi_message, queue);
437 spin_unlock_irqrestore(&drv_data->lock, flags);
438
439 /* see if the next and current messages point
440 * to the same chip
441 */
442 if (next_msg && next_msg->spi != msg->spi)
443 next_msg = NULL;
444 if (!next_msg || msg->state == ERROR_STATE)
445 drv_data->cs_control(PXA2XX_CS_DEASSERT);
446 }
411 447
412 msg->state = NULL; 448 msg->state = NULL;
413 if (msg->complete) 449 if (msg->complete)
@@ -490,10 +526,9 @@ static void dma_transfer_complete(struct driver_data *drv_data)
490 msg->actual_length += drv_data->len - 526 msg->actual_length += drv_data->len -
491 (drv_data->rx_end - drv_data->rx); 527 (drv_data->rx_end - drv_data->rx);
492 528
493 /* Release chip select if requested, transfer delays are 529 /* Transfer delays and chip select release are
494 * handled in pump_transfers */ 530 * handled in pump_transfers or giveback
495 if (drv_data->cs_change) 531 */
496 drv_data->cs_control(PXA2XX_CS_DEASSERT);
497 532
498 /* Move to next transfer */ 533 /* Move to next transfer */
499 msg->state = next_transfer(drv_data); 534 msg->state = next_transfer(drv_data);
@@ -602,10 +637,9 @@ static void int_transfer_complete(struct driver_data *drv_data)
602 drv_data->cur_msg->actual_length += drv_data->len - 637 drv_data->cur_msg->actual_length += drv_data->len -
603 (drv_data->rx_end - drv_data->rx); 638 (drv_data->rx_end - drv_data->rx);
604 639
605 /* Release chip select if requested, transfer delays are 640 /* Transfer delays and chip select release are
606 * handled in pump_transfers */ 641 * handled in pump_transfers or giveback
607 if (drv_data->cs_change) 642 */
608 drv_data->cs_control(PXA2XX_CS_DEASSERT);
609 643
610 /* Move to next transfer */ 644 /* Move to next transfer */
611 drv_data->cur_msg->state = next_transfer(drv_data); 645 drv_data->cur_msg->state = next_transfer(drv_data);
@@ -840,13 +874,17 @@ static void pump_transfers(unsigned long data)
840 return; 874 return;
841 } 875 }
842 876
843 /* Delay if requested at end of transfer*/ 877 /* Delay if requested at end of transfer before CS change */
844 if (message->state == RUNNING_STATE) { 878 if (message->state == RUNNING_STATE) {
845 previous = list_entry(transfer->transfer_list.prev, 879 previous = list_entry(transfer->transfer_list.prev,
846 struct spi_transfer, 880 struct spi_transfer,
847 transfer_list); 881 transfer_list);
848 if (previous->delay_usecs) 882 if (previous->delay_usecs)
849 udelay(previous->delay_usecs); 883 udelay(previous->delay_usecs);
884
885 /* Drop chip select only if cs_change is requested */
886 if (previous->cs_change)
887 drv_data->cs_control(PXA2XX_CS_DEASSERT);
850 } 888 }
851 889
852 /* Check transfer length */ 890 /* Check transfer length */
@@ -878,7 +916,6 @@ static void pump_transfers(unsigned long data)
878 drv_data->len = transfer->len & DCMD_LENGTH; 916 drv_data->len = transfer->len & DCMD_LENGTH;
879 drv_data->write = drv_data->tx ? chip->write : null_writer; 917 drv_data->write = drv_data->tx ? chip->write : null_writer;
880 drv_data->read = drv_data->rx ? chip->read : null_reader; 918 drv_data->read = drv_data->rx ? chip->read : null_reader;
881 drv_data->cs_change = transfer->cs_change;
882 919
883 /* Change speed and bit per word on a per transfer */ 920 /* Change speed and bit per word on a per transfer */
884 cr0 = chip->cr0; 921 cr0 = chip->cr0;