diff options
-rw-r--r-- | drivers/spi/spi-pl022.c | 72 |
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); |