]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/spi/spi-tegra114.c
ARM: dts: imx6qdl: add more pinctrls for flexcan
[karo-tx-linux.git] / drivers / spi / spi-tegra114.c
index b3821fb9da3f8212e5cd753bd9f2832bbc41d5a1..9146bb3c24895803904bf67281b38ce9bc9c8395 100644 (file)
@@ -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;
@@ -676,15 +677,12 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
        dma_release_channel(dma_chan);
 }
 
-static int tegra_spi_start_transfer_one(struct spi_device *spi,
-               struct spi_transfer *t, bool is_first_of_msg,
-               bool is_single_xfer)
+static unsigned long tegra_spi_setup_transfer_one(struct spi_device *spi,
+               struct spi_transfer *t, bool is_first_of_msg)
 {
        struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
        u32 speed = t->speed_hz;
        u8 bits_per_word = t->bits_per_word;
-       unsigned total_fifo_words;
-       int ret;
        unsigned long command1;
        int req_mode;
 
@@ -698,7 +696,6 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
        tspi->cur_rx_pos = 0;
        tspi->cur_tx_pos = 0;
        tspi->curr_xfer = t;
-       total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t);
 
        if (is_first_of_msg) {
                tegra_spi_clear_status(tspi);
@@ -717,7 +714,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 +734,18 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
                command1 |= SPI_BIT_LENGTH(bits_per_word - 1);
        }
 
+       return command1;
+}
+
+static int tegra_spi_start_transfer_one(struct spi_device *spi,
+               struct spi_transfer *t, unsigned long command1)
+{
+       struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
+       unsigned total_fifo_words;
+       int ret;
+
+       total_fifo_words = tegra_spi_calculate_curr_xfer_param(spi, tspi, t);
+
        if (tspi->is_packed)
                command1 |= SPI_PACKED;
 
@@ -803,29 +817,50 @@ 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)
 {
        bool is_first_msg = true;
-       int single_xfer;
        struct tegra_spi_data *tspi = spi_master_get_devdata(master);
        struct spi_transfer *xfer;
        struct spi_device *spi = msg->spi;
        int ret;
+       bool skip = false;
 
        msg->status = 0;
        msg->actual_length = 0;
 
-       single_xfer = list_is_singular(&msg->transfers);
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+               unsigned long cmd1;
+
                INIT_COMPLETION(tspi->xfer_completion);
-               ret = tegra_spi_start_transfer_one(spi, xfer,
-                                       is_first_msg, single_xfer);
+
+               cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
+
+               if (!xfer->len) {
+                       ret = 0;
+                       skip = true;
+                       goto complete_xfer;
+               }
+
+               ret = tegra_spi_start_transfer_one(spi, xfer, cmd1);
                if (ret < 0) {
                        dev_err(tspi->dev,
                                "spi can not start transfer, err %d\n", ret);
-                       goto exit;
+                       goto complete_xfer;
                }
+
                is_first_msg = false;
                ret = wait_for_completion_timeout(&tspi->xfer_completion,
                                                SPI_DMA_TIMEOUT);
@@ -833,24 +868,40 @@ 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;