aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-pl022.c
diff options
context:
space:
mode:
authorVirupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>2011-11-10 02:13:24 -0500
committerLinus Walleij <linus.walleij@linaro.org>2011-12-01 11:16:08 -0500
commit8b8d719161c386668161daf29b2632f094e326c8 (patch)
treef7c182a6b6e2cf08455af11915b37b92f6c766c7 /drivers/spi/spi-pl022.c
parent53e4acea0e819a6a8513e10a0773f2259ede0481 (diff)
spi/pl022: make the chip deselect handling thread safe
There is a possibility that the pump_message and giveback run in parallel on a SMP system. Both the pump_message and giveback threads work on the same SPI message queue. Results will be in correct if the pump_message gets to work on the queue first. when the pump_message works with the queue, it reads the head of the queue and removes it from the queue. pump_message activates the chip select depending on this message read. This leads to giveback working on the modified queue or a emptied queue. If the queue is empty or if the next message on the queue (which is not the actual next message, as the pump message has removed the next message from the queue) is not for the same SPI device as that Of the previous one, giveback will de-activate the chip select activated by pump_message(), which is wrong. To solve this problem pump_message is made not to run and access the queue until the giveback is done handling the queue. I.e. by making the cur_msg NULL after the giveback has read the queue. Also a state variable has been introduced to keep track of when the CS for next message is already activated and avoid to double-activate it. Reviewed-by: Viresh Kumar <viresh.kumar@st.com> Signed-off-by: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/spi/spi-pl022.c')
-rw-r--r--drivers/spi/spi-pl022.c72
1 files changed, 37 insertions, 35 deletions
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 13988a3024bb..1cff8ad470d1 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -340,6 +340,10 @@ struct vendor_data {
340 * @cur_msg: Pointer to current spi_message being processed 340 * @cur_msg: Pointer to current spi_message being processed
341 * @cur_transfer: Pointer to current spi_transfer 341 * @cur_transfer: Pointer to current spi_transfer
342 * @cur_chip: pointer to current clients chip(assigned from controller_state) 342 * @cur_chip: pointer to current clients chip(assigned from controller_state)
343 * @next_msg_cs_active: the next message in the queue has been examined
344 * and it was found that it uses the same chip select as the previous
345 * message, so we left it active after the previous transfer, and it's
346 * active already.
343 * @tx: current position in TX buffer to be read 347 * @tx: current position in TX buffer to be read
344 * @tx_end: end position in TX buffer to be read 348 * @tx_end: end position in TX buffer to be read
345 * @rx: current position in RX buffer to be written 349 * @rx: current position in RX buffer to be written
@@ -373,6 +377,7 @@ struct pl022 {
373 struct spi_message *cur_msg; 377 struct spi_message *cur_msg;
374 struct spi_transfer *cur_transfer; 378 struct spi_transfer *cur_transfer;
375 struct chip_data *cur_chip; 379 struct chip_data *cur_chip;
380 bool next_msg_cs_active;
376 void *tx; 381 void *tx;
377 void *tx_end; 382 void *tx_end;
378 void *rx; 383 void *rx;
@@ -445,23 +450,9 @@ static void giveback(struct pl022 *pl022)
445 struct spi_transfer *last_transfer; 450 struct spi_transfer *last_transfer;
446 unsigned long flags; 451 unsigned long flags;
447 struct spi_message *msg; 452 struct spi_message *msg;
448 void (*curr_cs_control) (u32 command); 453 pl022->next_msg_cs_active = false;
449 454
450 /* 455 last_transfer = list_entry(pl022->cur_msg->transfers.prev,
451 * This local reference to the chip select function
452 * is needed because we set curr_chip to NULL
453 * as a step toward termininating the message.
454 */
455 curr_cs_control = pl022->cur_chip->cs_control;
456 spin_lock_irqsave(&pl022->queue_lock, flags);
457 msg = pl022->cur_msg;
458 pl022->cur_msg = NULL;
459 pl022->cur_transfer = NULL;
460 pl022->cur_chip = NULL;
461 queue_work(pl022->workqueue, &pl022->pump_messages);
462 spin_unlock_irqrestore(&pl022->queue_lock, flags);
463
464 last_transfer = list_entry(msg->transfers.prev,
465 struct spi_transfer, 456 struct spi_transfer,
466 transfer_list); 457 transfer_list);
467 458
@@ -473,18 +464,13 @@ static void giveback(struct pl022 *pl022)
473 */ 464 */
474 udelay(last_transfer->delay_usecs); 465 udelay(last_transfer->delay_usecs);
475 466
476 /* 467 if (!last_transfer->cs_change) {
477 * Drop chip select UNLESS cs_change is true or we are returning
478 * a message with an error, or next message is for another chip
479 */
480 if (!last_transfer->cs_change)
481 curr_cs_control(SSP_CHIP_DESELECT);
482 else {
483 struct spi_message *next_msg; 468 struct spi_message *next_msg;
484 469
485 /* Holding of cs was hinted, but we need to make sure 470 /*
486 * the next message is for the same chip. Don't waste 471 * cs_change was not set. We can keep the chip select
487 * time with the following tests unless this was hinted. 472 * enabled if there is message in the queue and it is
473 * for the same spi device.
488 * 474 *
489 * We cannot postpone this until pump_messages, because 475 * We cannot postpone this until pump_messages, because
490 * after calling msg->complete (below) the driver that 476 * after calling msg->complete (below) the driver that
@@ -501,14 +487,26 @@ static void giveback(struct pl022 *pl022)
501 struct spi_message, queue); 487 struct spi_message, queue);
502 spin_unlock_irqrestore(&pl022->queue_lock, flags); 488 spin_unlock_irqrestore(&pl022->queue_lock, flags);
503 489
504 /* see if the next and current messages point 490 /*
505 * to the same chip 491 * see if the next and current messages point
492 * to the same spi device.
506 */ 493 */
507 if (next_msg && next_msg->spi != msg->spi) 494 if (next_msg && next_msg->spi != pl022->cur_msg->spi)
508 next_msg = NULL; 495 next_msg = NULL;
509 if (!next_msg || msg->state == STATE_ERROR) 496 if (!next_msg || pl022->cur_msg->state == STATE_ERROR)
510 curr_cs_control(SSP_CHIP_DESELECT); 497 pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
498 else
499 pl022->next_msg_cs_active = true;
511 } 500 }
501
502 spin_lock_irqsave(&pl022->queue_lock, flags);
503 msg = pl022->cur_msg;
504 pl022->cur_msg = NULL;
505 pl022->cur_transfer = NULL;
506 pl022->cur_chip = NULL;
507 queue_work(pl022->workqueue, &pl022->pump_messages);
508 spin_unlock_irqrestore(&pl022->queue_lock, flags);
509
512 msg->state = NULL; 510 msg->state = NULL;
513 if (msg->complete) 511 if (msg->complete)
514 msg->complete(msg->context); 512 msg->complete(msg->context);
@@ -1350,7 +1348,7 @@ static void pump_transfers(unsigned long data)
1350 */ 1348 */
1351 udelay(previous->delay_usecs); 1349 udelay(previous->delay_usecs);
1352 1350
1353 /* Drop chip select only if cs_change is requested */ 1351 /* Reselect chip select only if cs_change was requested */
1354 if (previous->cs_change) 1352 if (previous->cs_change)
1355 pl022->cur_chip->cs_control(SSP_CHIP_SELECT); 1353 pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
1356 } else { 1354 } else {
@@ -1389,8 +1387,10 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022)
1389 */ 1387 */
1390 u32 irqflags = ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM; 1388 u32 irqflags = ENABLE_ALL_INTERRUPTS & ~SSP_IMSC_MASK_RXIM;
1391 1389
1392 /* Enable target chip */ 1390 /* Enable target chip, if not already active */
1393 pl022->cur_chip->cs_control(SSP_CHIP_SELECT); 1391 if (!pl022->next_msg_cs_active)
1392 pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
1393
1394 if (set_up_next_transfer(pl022, pl022->cur_transfer)) { 1394 if (set_up_next_transfer(pl022, pl022->cur_transfer)) {
1395 /* Error path */ 1395 /* Error path */
1396 pl022->cur_msg->state = STATE_ERROR; 1396 pl022->cur_msg->state = STATE_ERROR;
@@ -1445,7 +1445,8 @@ static void do_polling_transfer(struct pl022 *pl022)
1445 } else { 1445 } else {
1446 /* STATE_START */ 1446 /* STATE_START */
1447 message->state = STATE_RUNNING; 1447 message->state = STATE_RUNNING;
1448 pl022->cur_chip->cs_control(SSP_CHIP_SELECT); 1448 if (!pl022->next_msg_cs_active)
1449 pl022->cur_chip->cs_control(SSP_CHIP_SELECT);
1449 } 1450 }
1450 1451
1451 /* Configuration Changing Per Transfer */ 1452 /* Configuration Changing Per Transfer */
@@ -1604,6 +1605,7 @@ static int start_queue(struct pl022 *pl022)
1604 pl022->cur_msg = NULL; 1605 pl022->cur_msg = NULL;
1605 pl022->cur_transfer = NULL; 1606 pl022->cur_transfer = NULL;
1606 pl022->cur_chip = NULL; 1607 pl022->cur_chip = NULL;
1608 pl022->next_msg_cs_active = false;
1607 spin_unlock_irqrestore(&pl022->queue_lock, flags); 1609 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1608 1610
1609 queue_work(pl022->workqueue, &pl022->pump_messages); 1611 queue_work(pl022->workqueue, &pl022->pump_messages);