ipq806x: fix 3.18 support
[openwrt/staging/lynxis/omap.git] / target / linux / ipq806x / patches-3.18 / 001-spi-qup-Add-DMA-capabilities.patch
diff --git a/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-3.18/001-spi-qup-Add-DMA-capabilities.patch
new file mode 100644 (file)
index 0000000..e110bf7
--- /dev/null
@@ -0,0 +1,522 @@
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: spi: qup: Add DMA capabilities
+From: Andy Gross <agross@codeaurora.org>
+X-Patchwork-Id: 4432401
+Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
+To: Mark Brown <broonie@kernel.org>
+Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
+       Daniel Sneddon <dsneddon@codeaurora.org>,
+       Bjorn Andersson <bjorn.andersson@sonymobile.com>,
+       "Ivan T. Ivanov" <iivanov@mm-sol.com>,
+       linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
+       linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
+Date: Thu, 26 Jun 2014 16:06:21 -0500
+
+This patch adds DMA capabilities to the spi-qup driver.  If DMA channels are
+present, the QUP will use DMA instead of block mode for transfers to/from SPI
+peripherals for transactions larger than the length of a block.
+
+Signed-off-by: Andy Gross <agross@codeaurora.org>
+
+---
+.../devicetree/bindings/spi/qcom,spi-qup.txt       |   10 +
+ drivers/spi/spi-qup.c                              |  361 ++++++++++++++++++--
+ 2 files changed, 350 insertions(+), 21 deletions(-)
+
+--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+@@ -27,6 +27,11 @@ Optional properties:
+ - spi-max-frequency: Specifies maximum SPI clock frequency,
+                      Units - Hz. Definition as per
+                      Documentation/devicetree/bindings/spi/spi-bus.txt
++- dmas :             Two DMA channel specifiers following the convention outlined
++                     in bindings/dma/dma.txt
++- dma-names:         Names for the dma channels, if present. There must be at
++                     least one channel named "tx" for transmit and named "rx" for
++                     receive.
+ - num-cs:     total number of chipselects
+ - cs-gpios:   should specify GPIOs used for chipselects.
+               The gpios will be referred to as reg = <index> in the SPI child
+@@ -51,6 +56,10 @@ Example:
+               clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
+               clock-names = "core", "iface";
++              dmas = <&blsp2_bam 2>,
++                      <&blsp2_bam 3>;
++              dma-names = "rx", "tx";
++
+               pinctrl-names = "default";
+               pinctrl-0 = <&spi8_default>;
+--- a/drivers/spi/spi-qup.c
++++ b/drivers/spi/spi-qup.c
+@@ -22,6 +22,8 @@
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
+ #define QUP_CONFIG                    0x0000
+ #define QUP_STATE                     0x0004
+@@ -116,6 +118,8 @@
+ #define SPI_NUM_CHIPSELECTS           4
++#define SPI_MAX_XFER                  (SZ_64K - 64)
++
+ /* high speed mode is when bus rate is greater then 26MHz */
+ #define SPI_HS_MIN_RATE                       26000000
+ #define SPI_MAX_RATE                  50000000
+@@ -143,6 +147,17 @@ struct spi_qup {
+       int                     tx_bytes;
+       int                     rx_bytes;
+       int                     qup_v1;
++
++      int                     use_dma;
++
++      struct dma_chan         *rx_chan;
++      struct dma_slave_config rx_conf;
++      struct dma_chan         *tx_chan;
++      struct dma_slave_config tx_conf;
++      dma_addr_t              rx_dma;
++      dma_addr_t              tx_dma;
++      void                    *dummy;
++      atomic_t                dma_outstanding;
+ };
+@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp
+       }
+ }
++static void qup_dma_callback(void *data)
++{
++      struct spi_qup *controller = data;
++
++      if (atomic_dec_and_test(&controller->dma_outstanding))
++              complete(&controller->done);
++}
++
++static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer)
++{
++      struct dma_async_tx_descriptor *rxd, *txd;
++      dma_cookie_t rx_cookie, tx_cookie;
++      u32 xfer_len, rx_align = 0, tx_align = 0, n_words;
++      struct scatterlist tx_sg[2], rx_sg[2];
++      int ret = 0;
++      u32 bytes_to_xfer = xfer->len;
++      u32 offset = 0;
++      u32 rx_nents = 0, tx_nents = 0;
++      dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0;
++
++
++      if (xfer->rx_buf) {
++              rx_dma = dma_map_single(controller->dev, xfer->rx_buf,
++                      xfer->len, DMA_FROM_DEVICE);
++
++              if (dma_mapping_error(controller->dev, rx_dma)) {
++                      ret = -ENOMEM;
++                      return ret;
++              }
++
++              /* check to see if we need dummy buffer for leftover bytes */
++              rx_align = xfer->len % controller->in_blk_sz;
++              if (rx_align) {
++                      rx_dummy_dma = dma_map_single(controller->dev,
++                              controller->dummy, controller->in_fifo_sz,
++                              DMA_FROM_DEVICE);
++
++                      if (dma_mapping_error(controller->dev, rx_dummy_dma)) {
++                              ret = -ENOMEM;
++                              goto err_map_rx_dummy;
++                      }
++              }
++      }
++
++      if (xfer->tx_buf) {
++              tx_dma = dma_map_single(controller->dev,
++                      (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
++
++              if (dma_mapping_error(controller->dev, tx_dma)) {
++                      ret = -ENOMEM;
++                      goto err_map_tx;
++              }
++
++              /* check to see if we need dummy buffer for leftover bytes */
++              tx_align = xfer->len % controller->out_blk_sz;
++              if (tx_align) {
++                      memcpy(controller->dummy + SZ_1K,
++                              xfer->tx_buf + xfer->len - tx_align,
++                              tx_align);
++                      memset(controller->dummy + SZ_1K + tx_align, 0,
++                              controller->out_blk_sz - tx_align);
++
++                      tx_dummy_dma = dma_map_single(controller->dev,
++                              controller->dummy + SZ_1K,
++                              controller->out_blk_sz, DMA_TO_DEVICE);
++
++                      if (dma_mapping_error(controller->dev, tx_dummy_dma)) {
++                              ret = -ENOMEM;
++                              goto err_map_tx_dummy;
++                      }
++              }
++      }
++
++      atomic_set(&controller->dma_outstanding, 0);
++
++      while (bytes_to_xfer > 0) {
++              xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER);
++              n_words = DIV_ROUND_UP(xfer_len, controller->w_size);
++
++              /* write out current word count to controller */
++              writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
++              writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
++
++              reinit_completion(&controller->done);
++
++              if (xfer->tx_buf) {
++                      /* recalc align for each transaction */
++                      tx_align = xfer_len % controller->out_blk_sz;
++
++                      if (tx_align)
++                              tx_nents = 2;
++                      else
++                              tx_nents = 1;
++
++                      /* initialize scatterlists */
++                      sg_init_table(tx_sg, tx_nents);
++                      sg_dma_len(&tx_sg[0]) = xfer_len - tx_align;
++                      sg_dma_address(&tx_sg[0]) = tx_dma + offset;
++
++                      /* account for non block size transfer */
++                      if (tx_align) {
++                              sg_dma_len(&tx_sg[1]) = controller->out_blk_sz;
++                              sg_dma_address(&tx_sg[1]) = tx_dummy_dma;
++                      }
++
++                      txd = dmaengine_prep_slave_sg(controller->tx_chan,
++                                      tx_sg, tx_nents, DMA_MEM_TO_DEV, 0);
++                      if (!txd) {
++                              ret = -ENOMEM;
++                              goto err_unmap;
++                      }
++
++                      atomic_inc(&controller->dma_outstanding);
++
++                      txd->callback = qup_dma_callback;
++                      txd->callback_param = controller;
++
++                      tx_cookie = dmaengine_submit(txd);
++
++                      dma_async_issue_pending(controller->tx_chan);
++              }
++
++              if (xfer->rx_buf) {
++                      /* recalc align for each transaction */
++                      rx_align = xfer_len % controller->in_blk_sz;
++
++                      if (rx_align)
++                              rx_nents = 2;
++                      else
++                              rx_nents = 1;
++
++                      /* initialize scatterlists */
++                      sg_init_table(rx_sg, rx_nents);
++                      sg_dma_address(&rx_sg[0]) = rx_dma + offset;
++                      sg_dma_len(&rx_sg[0]) = xfer_len - rx_align;
++
++                      /* account for non block size transfer */
++                      if (rx_align) {
++                              sg_dma_len(&rx_sg[1]) = controller->in_blk_sz;
++                              sg_dma_address(&rx_sg[1]) = rx_dummy_dma;
++                      }
++
++                      rxd = dmaengine_prep_slave_sg(controller->rx_chan,
++                                      rx_sg, rx_nents, DMA_DEV_TO_MEM, 0);
++                      if (!rxd) {
++                              ret = -ENOMEM;
++                              goto err_unmap;
++                      }
++
++                      atomic_inc(&controller->dma_outstanding);
++
++                      rxd->callback = qup_dma_callback;
++                      rxd->callback_param = controller;
++
++                      rx_cookie = dmaengine_submit(rxd);
++
++                      dma_async_issue_pending(controller->rx_chan);
++              }
++
++              if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++                      dev_warn(controller->dev, "cannot set EXECUTE state\n");
++                      goto err_unmap;
++              }
++
++              if (!wait_for_completion_timeout(&controller->done,
++                      msecs_to_jiffies(1000))) {
++                      ret = -ETIMEDOUT;
++
++                      /* clear out all the DMA transactions */
++                      if (xfer->tx_buf)
++                              dmaengine_terminate_all(controller->tx_chan);
++                      if (xfer->rx_buf)
++                              dmaengine_terminate_all(controller->rx_chan);
++
++                      goto err_unmap;
++              }
++
++              if (rx_align)
++                      memcpy(xfer->rx_buf + offset + xfer->len - rx_align,
++                              controller->dummy, rx_align);
++
++              /* adjust remaining bytes to transfer */
++              bytes_to_xfer -= xfer_len;
++              offset += xfer_len;
++
++
++              /* reset mini-core state so we can program next transaction */
++              if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
++                      dev_err(controller->dev, "cannot set RESET state\n");
++                      goto err_unmap;
++              }
++      }
++
++      ret = 0;
++
++err_unmap:
++      if (tx_align)
++              dma_unmap_single(controller->dev, tx_dummy_dma,
++                      controller->out_fifo_sz, DMA_TO_DEVICE);
++err_map_tx_dummy:
++      if (xfer->tx_buf)
++              dma_unmap_single(controller->dev, tx_dma, xfer->len,
++                      DMA_TO_DEVICE);
++err_map_tx:
++      if (rx_align)
++              dma_unmap_single(controller->dev, rx_dummy_dma,
++                      controller->in_fifo_sz, DMA_FROM_DEVICE);
++err_map_rx_dummy:
++      if (xfer->rx_buf)
++              dma_unmap_single(controller->dev, rx_dma, xfer->len,
++                      DMA_FROM_DEVICE);
++
++      return ret;
++}
++
+ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
+ {
+       struct spi_qup *controller = dev_id;
+@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i
+               error = -EIO;
+       }
+-      if (opflags & QUP_OP_IN_SERVICE_FLAG)
+-              spi_qup_fifo_read(controller, xfer);
++      if (!controller->use_dma) {
++              if (opflags & QUP_OP_IN_SERVICE_FLAG)
++                      spi_qup_fifo_read(controller, xfer);
+-      if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+-              spi_qup_fifo_write(controller, xfer);
++              if (opflags & QUP_OP_OUT_SERVICE_FLAG)
++                      spi_qup_fifo_write(controller, xfer);
++      }
+       spin_lock_irqsave(&controller->lock, flags);
+       controller->error = error;
+@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_
+       struct spi_qup *controller = spi_master_get_devdata(spi->master);
+       u32 config, iomode, mode;
+       int ret, n_words, w_size;
++      size_t dma_align = dma_get_cache_alignment();
++      u32 dma_available = 0;
+       if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
+               dev_err(controller->dev, "too big size for loopback %d > %d\n",
+@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_
+       n_words = xfer->len / w_size;
+       controller->w_size = w_size;
++      if (controller->rx_chan &&
++              IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
++              IS_ALIGNED((size_t)xfer->rx_buf, dma_align))
++              dma_available = 1;
++
+       if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+               mode = QUP_IO_M_MODE_FIFO;
+               writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
+@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_
+               /* must be zero for FIFO */
+               writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
+               writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
+-      } else {
++              controller->use_dma = 0;
++      } else if (!dma_available) {
+               mode = QUP_IO_M_MODE_BLOCK;
+               writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+               writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+               /* must be zero for BLOCK and BAM */
+               writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+               writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++              controller->use_dma = 0;
++      } else {
++              mode = QUP_IO_M_MODE_DMOV;
++              writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
++              writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
++              controller->use_dma = 1;
+       }
+       iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
+       /* Set input and output transfer mode */
+       iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
+-      iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++
++      if (!controller->use_dma)
++              iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
++      else
++              iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
++
+       iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+       iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_
+       config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
+       config |= xfer->bits_per_word - 1;
+       config |= QUP_CONFIG_SPI_MODE;
++
++      if (controller->use_dma) {
++              if (!xfer->tx_buf)
++                      config |= QUP_CONFIG_NO_OUTPUT;
++              if (!xfer->rx_buf)
++                      config |= QUP_CONFIG_NO_INPUT;
++      }
++
+       writel_relaxed(config, controller->base + QUP_CONFIG);
+       /* only write to OPERATIONAL_MASK when register is present */
+@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s
+       controller->tx_bytes = 0;
+       spin_unlock_irqrestore(&controller->lock, flags);
+-      if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+-              dev_warn(controller->dev, "cannot set RUN state\n");
+-              goto exit;
+-      }
++      if (controller->use_dma) {
++              ret = spi_qup_do_dma(controller, xfer);
++      } else {
++              if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++                      dev_warn(controller->dev, "cannot set RUN state\n");
++                      goto exit;
++              }
+-      if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
+-              dev_warn(controller->dev, "cannot set PAUSE state\n");
+-              goto exit;
+-      }
++              if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
++                      dev_warn(controller->dev, "cannot set PAUSE state\n");
++                      goto exit;
++              }
+-      spi_qup_fifo_write(controller, xfer);
++              spi_qup_fifo_write(controller, xfer);
+-      if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+-              dev_warn(controller->dev, "cannot set EXECUTE state\n");
+-              goto exit;
+-      }
++              if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
++                      dev_warn(controller->dev, "cannot set EXECUTE state\n");
++                      goto exit;
++              }
+-      if (!wait_for_completion_timeout(&controller->done, timeout))
+-              ret = -ETIMEDOUT;
++              if (!wait_for_completion_timeout(&controller->done, timeout))
++                      ret = -ETIMEDOUT;
++      }
+ exit:
+       spi_qup_set_state(controller, QUP_STATE_RESET);
+       spin_lock_irqsave(&controller->lock, flags);
+@@ -554,6 +817,7 @@ static int spi_qup_probe(struct platform
+       master->transfer_one = spi_qup_transfer_one;
+       master->dev.of_node = pdev->dev.of_node;
+       master->auto_runtime_pm = true;
++      master->dma_alignment = dma_get_cache_alignment();
+       platform_set_drvdata(pdev, master);
+@@ -619,6 +883,56 @@ static int spi_qup_probe(struct platform
+                       QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
+                       base + QUP_ERROR_FLAGS_EN);
++      /* allocate dma resources, if available */
++      controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
++      if (controller->rx_chan) {
++              controller->tx_chan =
++                      dma_request_slave_channel(&pdev->dev, "tx");
++
++              if (!controller->tx_chan) {
++                      dev_err(&pdev->dev, "Failed to allocate dma tx chan");
++                      dma_release_channel(controller->rx_chan);
++              }
++
++              /* set DMA parameters */
++              controller->rx_conf.device_fc = 1;
++              controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO;
++              controller->rx_conf.src_maxburst = controller->in_blk_sz;
++
++              controller->tx_conf.device_fc = 1;
++              controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO;
++              controller->tx_conf.dst_maxburst = controller->out_blk_sz;
++
++              if (dmaengine_slave_config(controller->rx_chan,
++                              &controller->rx_conf)) {
++                      dev_err(&pdev->dev, "failed to configure RX channel\n");
++
++                      dma_release_channel(controller->rx_chan);
++                      dma_release_channel(controller->tx_chan);
++                      controller->tx_chan = NULL;
++                      controller->rx_chan = NULL;
++              } else if (dmaengine_slave_config(controller->tx_chan,
++                              &controller->tx_conf)) {
++                      dev_err(&pdev->dev, "failed to configure TX channel\n");
++
++                      dma_release_channel(controller->rx_chan);
++                      dma_release_channel(controller->tx_chan);
++                      controller->tx_chan = NULL;
++                      controller->rx_chan = NULL;
++              }
++
++              controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE,
++                      GFP_KERNEL);
++
++              if (!controller->dummy) {
++                      dma_release_channel(controller->rx_chan);
++                      dma_release_channel(controller->tx_chan);
++                      controller->tx_chan = NULL;
++                      controller->rx_chan = NULL;
++              }
++      }
++
++
+       writel_relaxed(0, base + SPI_CONFIG);
+       writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
+@@ -731,6 +1045,11 @@ static int spi_qup_remove(struct platfor
+       if (ret)
+               return ret;
++      if (controller->rx_chan)
++              dma_release_channel(controller->rx_chan);
++      if (controller->tx_chan)
++              dma_release_channel(controller->tx_chan);
++
+       clk_disable_unprepare(controller->cclk);
+       clk_disable_unprepare(controller->iclk);