The tegra114 driver wasn't currently handling the cs_change functionality.
It is meant to invert normal behavior, and we were only using it to possibly
delay at the end of a transfer.

This patch modifies the logic so that the cs state will be toggled after
every individual transfer or NOT toggled at the end of the last transfer
if cs_change is set in that transfer struct.

Also, this builds in logic so that if a different device tries to start
a transfer while CS is active from a different device, it will abort the
previous transfer and start a new one for the new device.

This work was based on the spi-atmel driver.

Signed-off-by: Rhyland Klein <rkl...@nvidia.com>
---

RESEND WITH CORRECT PATCH ANNOTATION IN SUBJECT

 drivers/spi/spi-tegra114.c |   59 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 52 insertions(+), 7 deletions(-)

diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 145dd43..a9973de 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -182,6 +182,7 @@ struct tegra_spi_data {
        u32                                     cur_speed;
 
        struct spi_device                       *cur_spi;
+       struct spi_device                       *cs_control;
        unsigned                                cur_pos;
        unsigned                                cur_len;
        unsigned                                words_per_32bit;
@@ -717,7 +718,12 @@ static int tegra_spi_start_transfer_one(struct spi_device 
*spi,
                else if (req_mode == SPI_MODE_3)
                        command1 |= SPI_CONTROL_MODE_3;
 
-               tegra_spi_writel(tspi, command1, SPI_COMMAND1);
+               if (tspi->cs_control) {
+                       if (tspi->cs_control != spi)
+                               tegra_spi_writel(tspi, command1, SPI_COMMAND1);
+                       tspi->cs_control = NULL;
+               } else
+                       tegra_spi_writel(tspi, command1, SPI_COMMAND1);
 
                command1 |= SPI_CS_SW_HW;
                if (spi->mode & SPI_CS_HIGH)
@@ -732,6 +738,9 @@ static int tegra_spi_start_transfer_one(struct spi_device 
*spi,
                command1 |= SPI_BIT_LENGTH(bits_per_word - 1);
        }
 
+       if (t->len == 0 && !t->tx_buf && !t->rx_buf)
+               return 0;
+
        if (tspi->is_packed)
                command1 |= SPI_PACKED;
 
@@ -803,6 +812,17 @@ static int tegra_spi_setup(struct spi_device *spi)
        return 0;
 }
 
+static void tegra_spi_transfer_delay(int delay)
+{
+       if (!delay)
+               return;
+
+       if (delay >= 1000)
+               mdelay(delay / 1000);
+
+       udelay(delay % 1000);
+}
+
 static int tegra_spi_transfer_one_message(struct spi_master *master,
                        struct spi_message *msg)
 {
@@ -812,6 +832,7 @@ static int tegra_spi_transfer_one_message(struct spi_master 
*master,
        struct spi_transfer *xfer;
        struct spi_device *spi = msg->spi;
        int ret;
+       bool skip = false;
 
        msg->status = 0;
        msg->actual_length = 0;
@@ -819,13 +840,20 @@ static int tegra_spi_transfer_one_message(struct 
spi_master *master,
        single_xfer = list_is_singular(&msg->transfers);
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
                INIT_COMPLETION(tspi->xfer_completion);
+
                ret = tegra_spi_start_transfer_one(spi, xfer,
                                        is_first_msg, single_xfer);
                if (ret < 0) {
                        dev_err(tspi->dev,
                                "spi can not start transfer, err %d\n", ret);
-                       goto exit;
+                       goto complete_xfer;
+               }
+
+               if (xfer->len == 0 && !xfer->tx_buf && !xfer->rx_buf) {
+                       skip = true;
+                       goto complete_xfer;
                }
+
                is_first_msg = false;
                ret = wait_for_completion_timeout(&tspi->xfer_completion,
                                                SPI_DMA_TIMEOUT);
@@ -833,24 +861,41 @@ static int tegra_spi_transfer_one_message(struct 
spi_master *master,
                        dev_err(tspi->dev,
                                "spi trasfer timeout, err %d\n", ret);
                        ret = -EIO;
-                       goto exit;
+                       goto complete_xfer;
                }
 
                if (tspi->tx_status ||  tspi->rx_status) {
                        dev_err(tspi->dev, "Error in Transfer\n");
                        ret = -EIO;
-                       goto exit;
+                       goto complete_xfer;
                }
                msg->actual_length += xfer->len;
-               if (xfer->cs_change && xfer->delay_usecs) {
+
+complete_xfer:
+               if (ret < 0 || skip) {
                        tegra_spi_writel(tspi, tspi->def_command1_reg,
                                        SPI_COMMAND1);
-                       udelay(xfer->delay_usecs);
+                       tegra_spi_transfer_delay(xfer->delay_usecs);
+                       goto exit;
+               } else if (msg->transfers.prev == &xfer->transfer_list) {
+                       /* This is the last transfer in message */
+                       if (xfer->cs_change)
+                               tspi->cs_control = spi;
+                       else {
+                               tegra_spi_writel(tspi, tspi->def_command1_reg,
+                                               SPI_COMMAND1);
+                               tegra_spi_transfer_delay(xfer->delay_usecs);
+                       }
+               } else if (xfer->cs_change) {
+                       tegra_spi_writel(tspi, tspi->def_command1_reg,
+                                       SPI_COMMAND1);
+                       tegra_spi_transfer_delay(xfer->delay_usecs);
                }
+
        }
        ret = 0;
 exit:
-       tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
+
        msg->status = ret;
        spi_finalize_current_message(master);
        return ret;
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to