diff options
Diffstat (limited to 'target/linux/ipq806x/patches-4.0/001-spi-qup-Add-DMA-capabilities.patch')
-rw-r--r-- | target/linux/ipq806x/patches-4.0/001-spi-qup-Add-DMA-capabilities.patch | 522 |
1 files changed, 0 insertions, 522 deletions
diff --git a/target/linux/ipq806x/patches-4.0/001-spi-qup-Add-DMA-capabilities.patch b/target/linux/ipq806x/patches-4.0/001-spi-qup-Add-DMA-capabilities.patch deleted file mode 100644 index a500c67cc3..0000000000 --- a/target/linux/ipq806x/patches-4.0/001-spi-qup-Add-DMA-capabilities.patch +++ /dev/null @@ -1,522 +0,0 @@ -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, control; - 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); - -@@ -428,6 +679,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 */ -@@ -461,25 +720,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); -@@ -563,6 +826,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); - -@@ -628,6 +892,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); - -@@ -740,6 +1054,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); - |