Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/818386/?format=api
{ "id": 818386, "url": "http://patchwork.ozlabs.org/api/patches/818386/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-tegra/patch/0a45e058baba72124b91c663ce1d908d275f4044.1506380746.git.digetx@gmail.com/", "project": { "id": 21, "url": "http://patchwork.ozlabs.org/api/projects/21/?format=api", "name": "Linux Tegra Development", "link_name": "linux-tegra", "list_id": "linux-tegra.vger.kernel.org", "list_email": "linux-tegra@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<0a45e058baba72124b91c663ce1d908d275f4044.1506380746.git.digetx@gmail.com>", "list_archive_url": null, "date": "2017-09-25T23:22:05", "name": "[v1,4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA controller", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "d4cda062a027d6afb956ec4e79ef1e429276c8e8", "submitter": { "id": 18124, "url": "http://patchwork.ozlabs.org/api/people/18124/?format=api", "name": "Dmitry Osipenko", "email": "digetx@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-tegra/patch/0a45e058baba72124b91c663ce1d908d275f4044.1506380746.git.digetx@gmail.com/mbox/", "series": [ { "id": 5029, "url": "http://patchwork.ozlabs.org/api/series/5029/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-tegra/list/?series=5029", "date": "2017-09-25T23:22:01", "name": "NVIDIA Tegra AHB DMA controller driver", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/5029/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/818386/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/818386/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<linux-tegra-owner@vger.kernel.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org", "Authentication-Results": [ "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-tegra-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"pP/mEWRM\"; dkim-atps=neutral" ], "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y1KvD3DhNz9t2l\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 26 Sep 2017 09:25:28 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S966230AbdIYXZN (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tMon, 25 Sep 2017 19:25:13 -0400", "from mail-wr0-f194.google.com ([209.85.128.194]:34402 \"EHLO\n\tmail-wr0-f194.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S965878AbdIYXYb (ORCPT\n\t<rfc822;linux-tegra@vger.kernel.org>);\n\tMon, 25 Sep 2017 19:24:31 -0400", "by mail-wr0-f194.google.com with SMTP id k20so918650wre.1;\n\tMon, 25 Sep 2017 16:24:30 -0700 (PDT)", "from localhost.localdomain (ppp109-252-90-109.pppoe.spdop.ru.\n\t[109.252.90.109]) by smtp.gmail.com with ESMTPSA id\n\ta139sm1161965lfa.39.2017.09.25.16.24.28\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tMon, 25 Sep 2017 16:24:28 -0700 (PDT)" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:in-reply-to:references;\n\tbh=PVgzROyVVbwVMXWDJwyudz2CcfTtl1EgQrSXN8qTxlM=;\n\tb=pP/mEWRMC7TmM9h1FOwOg+Ehz+yCiQ7aSgxsfSiV2juZFXUiDGBp7tHT16Gl/wnYEx\n\tmM39FDmTOqpcZpm/5abVhyj8enFLGaiBxm4sNoYZ8m66YplXXJdxjrmrLDKcxcji/Ff2\n\t4roT7Bp3xK8888sSdaf4ocLBhicaqXFH0z5+ItE6zPm3Ji7Kp+JxTW3HbVEk6c75Qc/H\n\tMur29kLiU0AszZfwknRDGalCVMT1KWxyF/j4cAlj6/vasxPpajJqvF/VCnObigrD/Y1j\n\ttmxVg1mqCBR4fsFIhBI04SCx3f+af+/wXNre9pSWgdERCBrjImElw6NzKhEinBMGg37+\n\tuNPw==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:in-reply-to:references;\n\tbh=PVgzROyVVbwVMXWDJwyudz2CcfTtl1EgQrSXN8qTxlM=;\n\tb=N0tSK9JvubVTv1/b9usXWvaINIuPZaVP/BYL/v03V9kkTjo/vJyz503PBElXFPCbXR\n\t02UBv3TF4RL9EYgHHOv16ND6Y4rP7Wb1RoLfycB7HvfaKBF36RLI9m8xgyHapyrKQKRp\n\tw6lqopUM2aRKce/VGn5Sky3l6JU/FrKpGOrZcI7hOsIMXzIYUcvQcbnzsom6hb2h3aTv\n\teorEyX6F4mXINsouWu6zIOVWCY+rmUJH2MwYnZZH655ByuQgKpkhob6KMwGQyI6wbIy/\n\tqWYvOjAVFAFxY8nPkuHSxzN2F6CuikN5REwq3DhPE0tVKX1zjkdxwCx4WD3N+J0wzyzS\n\typiw==", "X-Gm-Message-State": "AHPjjUhsFBjJLBYe/1t3XJbWgWhNbxTcubCJRHYZ3lLe7WHY2BYMK4kq\n\tgFPWg0semntQy+/LbGJNu4M=", "X-Google-Smtp-Source": "AOwi7QDCYQxT8ZT0BJXfRKFWy4rcyHLCZWlOpmB+xV9/iQ4oLCwfuOEF1ognRcLpWC0vcurCOlHR1A==", "X-Received": "by 10.25.225.66 with SMTP id y63mr453351lfg.209.1506381869423;\n\tMon, 25 Sep 2017 16:24:29 -0700 (PDT)", "From": "Dmitry Osipenko <digetx@gmail.com>", "To": "Thierry Reding <thierry.reding@gmail.com>,\n\tJonathan Hunter <jonathanh@nvidia.com>,\n\tLaxman Dewangan <ldewangan@nvidia.com>,\n\tPeter De Schrijver <pdeschrijver@nvidia.com>,\n\tPrashant Gaikwad <pgaikwad@nvidia.com>,\n\tMichael Turquette <mturquette@baylibre.com>,\n\tStephen Boyd <sboyd@codeaurora.org>, Rob Herring <robh+dt@kernel.org>,\n\tVinod Koul <vinod.koul@intel.com>", "Cc": "linux-tegra@vger.kernel.org, devicetree@vger.kernel.org,\n\tdmaengine@vger.kernel.org, linux-clk@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org", "Subject": "[PATCH v1 4/5] dmaengine: Add driver for NVIDIA Tegra AHB DMA\n\tcontroller", "Date": "Tue, 26 Sep 2017 02:22:05 +0300", "Message-Id": "<0a45e058baba72124b91c663ce1d908d275f4044.1506380746.git.digetx@gmail.com>", "X-Mailer": "git-send-email 2.14.1", "In-Reply-To": [ "<cover.1506380746.git.digetx@gmail.com>", "<cover.1506380746.git.digetx@gmail.com>" ], "References": [ "<cover.1506380746.git.digetx@gmail.com>", "<cover.1506380746.git.digetx@gmail.com>" ], "Sender": "linux-tegra-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<linux-tegra.vger.kernel.org>", "X-Mailing-List": "linux-tegra@vger.kernel.org" }, "content": "AHB DMA controller presents on Tegra20/30 SoC's, it supports transfers\nmemory <-> AHB bus peripherals as well as mem-to-mem transfers. Driver\ndoesn't yet implement transfers larger than 64K and scatter-gather\ntransfers that have NENT > 1, HW doesn't have native support for these\ncases.\n\nSigned-off-by: Dmitry Osipenko <digetx@gmail.com>\n---\n drivers/dma/Kconfig | 9 +\n drivers/dma/Makefile | 1 +\n drivers/dma/tegra20-ahb-dma.c | 679 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 689 insertions(+)\n create mode 100644 drivers/dma/tegra20-ahb-dma.c", "diff": "diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig\nindex fadc4d8783bd..937c110a715b 100644\n--- a/drivers/dma/Kconfig\n+++ b/drivers/dma/Kconfig\n@@ -503,6 +503,15 @@ config TXX9_DMAC\n \t Support the TXx9 SoC internal DMA controller. This can be\n \t integrated in chips such as the Toshiba TX4927/38/39.\n \n+config TEGRA20_AHB_DMA\n+\ttristate \"NVIDIA Tegra20 AHB DMA support\"\n+\tdepends on ARCH_TEGRA\n+\tselect DMA_ENGINE\n+\thelp\n+\t Enable support for the NVIDIA Tegra20 AHB DMA controller driver.\n+\t This DMA controller transfers data from memory to AHB peripherals\n+\t or vice versa, it supports memory to memory data transfer as well.\n+\n config TEGRA20_APB_DMA\n \tbool \"NVIDIA Tegra20 APB DMA support\"\n \tdepends on ARCH_TEGRA\ndiff --git a/drivers/dma/Makefile b/drivers/dma/Makefile\nindex f08f8de1b567..82d5d36b0379 100644\n--- a/drivers/dma/Makefile\n+++ b/drivers/dma/Makefile\n@@ -61,6 +61,7 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o\n obj-$(CONFIG_STM32_DMA) += stm32-dma.o\n obj-$(CONFIG_S3C24XX_DMAC) += s3c24xx-dma.o\n obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o\n+obj-$(CONFIG_TEGRA20_AHB_DMA) += tegra20-ahb-dma.o\n obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o\n obj-$(CONFIG_TEGRA210_ADMA) += tegra210-adma.o\n obj-$(CONFIG_TIMB_DMA) += timb_dma.o\ndiff --git a/drivers/dma/tegra20-ahb-dma.c b/drivers/dma/tegra20-ahb-dma.c\nnew file mode 100644\nindex 000000000000..8316d64e35e1\n--- /dev/null\n+++ b/drivers/dma/tegra20-ahb-dma.c\n@@ -0,0 +1,679 @@\n+/*\n+ * Copyright 2017 Dmitry Osipenko <digetx@gmail.com>\n+ *\n+ * This program is free software; you can redistribute it and/or modify it\n+ * under the terms and conditions of the GNU General Public License,\n+ * version 2, as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope it will be useful, but WITHOUT\n+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n+ * more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\n+ */\n+\n+#include <linux/clk.h>\n+#include <linux/delay.h>\n+#include <linux/interrupt.h>\n+#include <linux/io.h>\n+#include <linux/module.h>\n+#include <linux/of_device.h>\n+#include <linux/of_dma.h>\n+#include <linux/platform_device.h>\n+#include <linux/reset.h>\n+#include <linux/slab.h>\n+#include <linux/spinlock.h>\n+\n+#include \"dmaengine.h\"\n+\n+#define TEGRA_AHBDMA_CMD\t\t\t0x0\n+#define TEGRA_AHBDMA_CMD_ENABLE\t\t\tBIT(31)\n+\n+#define TEGRA_AHBDMA_IRQ_ENB_MASK\t\t0x20\n+#define TEGRA_AHBDMA_IRQ_ENB_CH(ch)\t\tBIT(ch)\n+\n+#define TEGRA_AHBDMA_CHANNEL_BASE(ch)\t\t(0x1000 + (ch) * 0x20)\n+\n+#define TEGRA_AHBDMA_CHANNEL_CSR\t\t0x0\n+#define TEGRA_AHBDMA_CHANNEL_ADDR_WRAP\t\tBIT(18)\n+#define TEGRA_AHBDMA_CHANNEL_FLOW\t\tBIT(24)\n+#define TEGRA_AHBDMA_CHANNEL_ONCE\t\tBIT(26)\n+#define TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB\t\tBIT(27)\n+#define TEGRA_AHBDMA_CHANNEL_IE_EOC\t\tBIT(30)\n+#define TEGRA_AHBDMA_CHANNEL_ENABLE\t\tBIT(31)\n+#define TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT\t16\n+#define TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK\t0xFFFC\n+\n+#define TEGRA_AHBDMA_CHANNEL_STA\t\t0x4\n+#define TEGRA_AHBDMA_CHANNEL_IS_EOC\t\tBIT(30)\n+\n+#define TEGRA_AHBDMA_CHANNEL_AHB_PTR\t\t0x10\n+\n+#define TEGRA_AHBDMA_CHANNEL_AHB_SEQ\t\t0x14\n+#define TEGRA_AHBDMA_CHANNEL_INTR_ENB\t\tBIT(31)\n+#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT\t24\n+#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_1\t2\n+#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_4\t3\n+#define TEGRA_AHBDMA_CHANNEL_AHB_BURST_8\t4\n+\n+#define TEGRA_AHBDMA_CHANNEL_XMB_PTR\t\t0x18\n+\n+#define TEGRA_AHBDMA_BUS_WIDTH\t\t\tBIT(DMA_SLAVE_BUSWIDTH_4_BYTES)\n+\n+#define TEGRA_AHBDMA_DIRECTIONS\t\t\tBIT(DMA_DEV_TO_MEM) | \\\n+\t\t\t\t\t\tBIT(DMA_MEM_TO_DEV)\n+\n+struct tegra_ahbdma_tx_desc {\n+\tstruct dma_async_tx_descriptor desc;\n+\tstruct tasklet_struct tasklet;\n+\tstruct list_head node;\n+\tenum dma_transfer_direction dir;\n+\tdma_addr_t mem_paddr;\n+\tunsigned long flags;\n+\tsize_t size;\n+\tbool in_fly;\n+\tbool cyclic;\n+};\n+\n+struct tegra_ahbdma_chan {\n+\tstruct dma_chan dma_chan;\n+\tstruct list_head active_list;\n+\tstruct list_head pending_list;\n+\tstruct completion idling;\n+\tvoid __iomem *regs;\n+\tspinlock_t lock;\n+\tunsigned int id;\n+};\n+\n+struct tegra_ahbdma {\n+\tstruct tegra_ahbdma_chan channels[4];\n+\tstruct dma_device dma_dev;\n+\tstruct reset_control *rst;\n+\tstruct clk *clk;\n+\tvoid __iomem *regs;\n+};\n+\n+static inline struct tegra_ahbdma *to_ahbdma(struct dma_device *dev)\n+{\n+\treturn container_of(dev, struct tegra_ahbdma, dma_dev);\n+}\n+\n+static inline struct tegra_ahbdma_chan *to_ahbdma_chan(struct dma_chan *chan)\n+{\n+\treturn container_of(chan, struct tegra_ahbdma_chan, dma_chan);\n+}\n+\n+static inline struct tegra_ahbdma_tx_desc *to_ahbdma_tx_desc(\n+\t\t\t\tstruct dma_async_tx_descriptor *tx)\n+{\n+\treturn container_of(tx, struct tegra_ahbdma_tx_desc, desc);\n+}\n+\n+static void tegra_ahbdma_submit_tx(struct tegra_ahbdma_chan *chan,\n+\t\t\t\t struct tegra_ahbdma_tx_desc *tx)\n+{\n+\tu32 csr;\n+\n+\twritel_relaxed(tx->mem_paddr,\n+\t\t chan->regs + TEGRA_AHBDMA_CHANNEL_XMB_PTR);\n+\n+\tcsr = readl_relaxed(chan->regs + TEGRA_AHBDMA_CHANNEL_CSR);\n+\n+\tcsr &= ~TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK;\n+\tcsr &= ~TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB;\n+\tcsr |= TEGRA_AHBDMA_CHANNEL_ENABLE;\n+\tcsr |= TEGRA_AHBDMA_CHANNEL_IE_EOC;\n+\tcsr |= tx->size - sizeof(u32);\n+\n+\tif (tx->dir == DMA_DEV_TO_MEM)\n+\t\tcsr |= TEGRA_AHBDMA_CHANNEL_DIR_TO_XMB;\n+\n+\tif (!tx->cyclic)\n+\t\tcsr |= TEGRA_AHBDMA_CHANNEL_ONCE;\n+\n+\twritel_relaxed(csr, chan->regs + TEGRA_AHBDMA_CHANNEL_CSR);\n+\n+\ttx->in_fly = true;\n+}\n+\n+static void tegra_ahbdma_tasklet(unsigned long data)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx = (struct tegra_ahbdma_tx_desc *)data;\n+\tstruct dma_async_tx_descriptor *desc = &tx->desc;\n+\n+\tdmaengine_desc_get_callback_invoke(desc, NULL);\n+\n+\tif (!tx->cyclic && !dmaengine_desc_test_reuse(desc))\n+\t\tkfree(tx);\n+}\n+\n+static bool tegra_ahbdma_tx_completed(struct tegra_ahbdma_chan *chan,\n+\t\t\t\t struct tegra_ahbdma_tx_desc *tx)\n+{\n+\tstruct dma_async_tx_descriptor *desc = &tx->desc;\n+\tbool reuse = dmaengine_desc_test_reuse(desc);\n+\tbool interrupt = tx->flags & DMA_PREP_INTERRUPT;\n+\tbool completed = !tx->cyclic;\n+\n+\tif (completed)\n+\t\tdma_cookie_complete(desc);\n+\n+\tif (interrupt)\n+\t\ttasklet_schedule(&tx->tasklet);\n+\n+\tif (completed) {\n+\t\tlist_del(&tx->node);\n+\n+\t\tif (reuse)\n+\t\t\ttx->in_fly = false;\n+\n+\t\tif (!interrupt && !reuse)\n+\t\t\tkfree(tx);\n+\t}\n+\n+\treturn completed;\n+}\n+\n+static bool tegra_ahbdma_next_tx_issued(struct tegra_ahbdma_chan *chan)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\n+\ttx = list_first_entry_or_null(&chan->active_list,\n+\t\t\t\t struct tegra_ahbdma_tx_desc,\n+\t\t\t\t node);\n+\tif (tx)\n+\t\ttegra_ahbdma_submit_tx(chan, tx);\n+\n+\treturn !!tx;\n+}\n+\n+static void tegra_ahbdma_handle_channel(struct tegra_ahbdma_chan *chan)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\tunsigned long flags;\n+\tu32 status;\n+\n+\tstatus = readl_relaxed(chan->regs + TEGRA_AHBDMA_CHANNEL_STA);\n+\tif (!(status & TEGRA_AHBDMA_CHANNEL_IS_EOC))\n+\t\treturn;\n+\n+\twritel_relaxed(TEGRA_AHBDMA_CHANNEL_IS_EOC,\n+\t\t chan->regs + TEGRA_AHBDMA_CHANNEL_STA);\n+\n+\tspin_lock_irqsave(&chan->lock, flags);\n+\n+\tif (!completion_done(&chan->idling)) {\n+\t\ttx = list_first_entry(&chan->active_list,\n+\t\t\t\t struct tegra_ahbdma_tx_desc,\n+\t\t\t\t node);\n+\n+\t\tif (tegra_ahbdma_tx_completed(chan, tx) &&\n+\t\t !tegra_ahbdma_next_tx_issued(chan))\n+\t\t\tcomplete_all(&chan->idling);\n+\t}\n+\n+\tspin_unlock_irqrestore(&chan->lock, flags);\n+}\n+\n+static irqreturn_t tegra_ahbdma_isr(int irq, void *dev_id)\n+{\n+\tstruct tegra_ahbdma *tdma = dev_id;\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(tdma->channels); i++)\n+\t\ttegra_ahbdma_handle_channel(&tdma->channels[i]);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static dma_cookie_t tegra_ahbdma_tx_submit(struct dma_async_tx_descriptor *desc)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx = to_ahbdma_tx_desc(desc);\n+\tstruct tegra_ahbdma_chan *chan = to_ahbdma_chan(desc->chan);\n+\tdma_cookie_t cookie;\n+\n+\tcookie = dma_cookie_assign(desc);\n+\n+\tspin_lock_irq(&chan->lock);\n+\tlist_add_tail(&tx->node, &chan->pending_list);\n+\tspin_unlock_irq(&chan->lock);\n+\n+\treturn cookie;\n+}\n+\n+static int tegra_ahbdma_tx_desc_free(struct dma_async_tx_descriptor *desc)\n+{\n+\tkfree(to_ahbdma_tx_desc(desc));\n+\n+\treturn 0;\n+}\n+\n+static struct dma_async_tx_descriptor *tegra_ahbdma_prep_slave_sg(\n+\t\t\t\t\tstruct dma_chan *chan,\n+\t\t\t\t\tstruct scatterlist *sgl,\n+\t\t\t\t\tunsigned int sg_len,\n+\t\t\t\t\tenum dma_transfer_direction dir,\n+\t\t\t\t\tunsigned long flags,\n+\t\t\t\t\tvoid *context)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\n+\t/* unimplemented */\n+\tif (sg_len != 1 || sg_dma_len(sgl) > SZ_64K)\n+\t\treturn NULL;\n+\n+\ttx = kzalloc(sizeof(*tx), GFP_KERNEL);\n+\tif (!tx)\n+\t\treturn NULL;\n+\n+\tdma_async_tx_descriptor_init(&tx->desc, chan);\n+\n+\ttx->desc.tx_submit\t= tegra_ahbdma_tx_submit;\n+\ttx->desc.desc_free\t= tegra_ahbdma_tx_desc_free;\n+\ttx->mem_paddr\t\t= sg_dma_address(sgl);\n+\ttx->size\t\t= sg_dma_len(sgl);\n+\ttx->flags\t\t= flags;\n+\ttx->dir\t\t\t= dir;\n+\n+\ttasklet_init(&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long)tx);\n+\n+\treturn &tx->desc;\n+}\n+\n+static struct dma_async_tx_descriptor *tegra_ahbdma_prep_dma_cyclic(\n+\t\t\t\t\tstruct dma_chan *chan,\n+\t\t\t\t\tdma_addr_t buf_addr,\n+\t\t\t\t\tsize_t buf_len,\n+\t\t\t\t\tsize_t period_len,\n+\t\t\t\t\tenum dma_transfer_direction dir,\n+\t\t\t\t\tunsigned long flags)\n+{\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\n+\t/* unimplemented */\n+\tif (buf_len != period_len || buf_len > SZ_64K)\n+\t\treturn NULL;\n+\n+\ttx = kzalloc(sizeof(*tx), GFP_KERNEL);\n+\tif (!tx)\n+\t\treturn NULL;\n+\n+\tdma_async_tx_descriptor_init(&tx->desc, chan);\n+\n+\ttx->desc.tx_submit\t= tegra_ahbdma_tx_submit;\n+\ttx->mem_paddr\t\t= buf_addr;\n+\ttx->size\t\t= buf_len;\n+\ttx->flags\t\t= flags;\n+\ttx->cyclic\t\t= true;\n+\ttx->dir\t\t\t= dir;\n+\n+\ttasklet_init(&tx->tasklet, tegra_ahbdma_tasklet, (unsigned long)tx);\n+\n+\treturn &tx->desc;\n+}\n+\n+static void tegra_ahbdma_issue_pending(struct dma_chan *chan)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = to_ahbdma_chan(chan);\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\tstruct list_head *entry, *tmp;\n+\tunsigned long flags;\n+\n+\tspin_lock_irqsave(&ahbdma_chan->lock, flags);\n+\n+\tlist_for_each_safe(entry, tmp, &ahbdma_chan->pending_list)\n+\t\tlist_move_tail(entry, &ahbdma_chan->active_list);\n+\n+\tif (completion_done(&ahbdma_chan->idling)) {\n+\t\ttx = list_first_entry_or_null(&ahbdma_chan->active_list,\n+\t\t\t\t\t struct tegra_ahbdma_tx_desc,\n+\t\t\t\t\t node);\n+\t\tif (tx) {\n+\t\t\ttegra_ahbdma_submit_tx(ahbdma_chan, tx);\n+\t\t\treinit_completion(&ahbdma_chan->idling);\n+\t\t}\n+\t}\n+\n+\tspin_unlock_irqrestore(&ahbdma_chan->lock, flags);\n+}\n+\n+static enum dma_status tegra_ahbdma_tx_status(struct dma_chan *chan,\n+\t\t\t\t\t dma_cookie_t cookie,\n+\t\t\t\t\t struct dma_tx_state *state)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = to_ahbdma_chan(chan);\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\tenum dma_status cookie_status;\n+\tunsigned long flags;\n+\tsize_t residual;\n+\tu32 status;\n+\n+\tspin_lock_irqsave(&ahbdma_chan->lock, flags);\n+\n+\tcookie_status = dma_cookie_status(chan, cookie, state);\n+\tif (cookie_status != DMA_COMPLETE) {\n+\t\tlist_for_each_entry(tx, &ahbdma_chan->active_list, node) {\n+\t\t\tif (tx->desc.cookie == cookie)\n+\t\t\t\tgoto found;\n+\t\t}\n+\t}\n+\n+\tgoto unlock;\n+\n+found:\n+\tif (tx->in_fly) {\n+\t\tstatus = readl_relaxed(\n+\t\t\tahbdma_chan->regs + TEGRA_AHBDMA_CHANNEL_STA);\n+\t\tstatus &= TEGRA_AHBDMA_CHANNEL_WCOUNT_MASK;\n+\n+\t\tresidual = status;\n+\t} else\n+\t\tresidual = tx->size;\n+\n+\tdma_set_residue(state, residual);\n+\n+unlock:\n+\tspin_unlock_irqrestore(&ahbdma_chan->lock, flags);\n+\n+\treturn cookie_status;\n+}\n+\n+static int tegra_ahbdma_terminate_all(struct dma_chan *chan)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = to_ahbdma_chan(chan);\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\tstruct list_head *entry, *tmp;\n+\tu32 csr;\n+\n+\tspin_lock_irq(&ahbdma_chan->lock);\n+\n+\tcsr = readl_relaxed(ahbdma_chan->regs + TEGRA_AHBDMA_CHANNEL_CSR);\n+\tcsr &= ~TEGRA_AHBDMA_CHANNEL_ENABLE;\n+\n+\twritel_relaxed(csr, ahbdma_chan->regs + TEGRA_AHBDMA_CHANNEL_CSR);\n+\n+\tlist_for_each_safe(entry, tmp, &ahbdma_chan->active_list) {\n+\t\ttx = list_entry(entry, struct tegra_ahbdma_tx_desc, node);\n+\t\tlist_del(entry);\n+\t\tkfree(tx);\n+\t}\n+\n+\tlist_for_each_safe(entry, tmp, &ahbdma_chan->pending_list) {\n+\t\ttx = list_entry(entry, struct tegra_ahbdma_tx_desc, node);\n+\t\tlist_del(entry);\n+\t\tkfree(tx);\n+\t}\n+\n+\tcomplete_all(&ahbdma_chan->idling);\n+\n+\tspin_unlock_irq(&ahbdma_chan->lock);\n+\n+\treturn 0;\n+}\n+\n+static int tegra_ahbdma_config(struct dma_chan *chan,\n+\t\t\t struct dma_slave_config *sconfig)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = to_ahbdma_chan(chan);\n+\tenum dma_transfer_direction dir = sconfig->direction;\n+\tu32 burst, ahb_seq, ahb_addr;\n+\n+\tif (sconfig->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES ||\n+\t sconfig->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)\n+\t\treturn -EINVAL;\n+\n+\tif (dir == DMA_DEV_TO_MEM) {\n+\t\tburst = sconfig->src_maxburst;\n+\t\tahb_addr = sconfig->src_addr;\n+\t} else {\n+\t\tburst = sconfig->dst_maxburst;\n+\t\tahb_addr = sconfig->dst_addr;\n+\t}\n+\n+\tswitch (burst) {\n+\tcase 1: burst = TEGRA_AHBDMA_CHANNEL_AHB_BURST_1; break;\n+\tcase 4: burst = TEGRA_AHBDMA_CHANNEL_AHB_BURST_4; break;\n+\tcase 8: burst = TEGRA_AHBDMA_CHANNEL_AHB_BURST_8; break;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tahb_seq = burst << TEGRA_AHBDMA_CHANNEL_AHB_BURST_SHIFT;\n+\tahb_seq |= TEGRA_AHBDMA_CHANNEL_ADDR_WRAP;\n+\tahb_seq |= TEGRA_AHBDMA_CHANNEL_INTR_ENB;\n+\n+\twritel_relaxed(ahb_seq,\n+\t\t ahbdma_chan->regs + TEGRA_AHBDMA_CHANNEL_AHB_SEQ);\n+\n+\twritel_relaxed(ahb_addr,\n+\t\t ahbdma_chan->regs + TEGRA_AHBDMA_CHANNEL_AHB_PTR);\n+\n+\treturn 0;\n+}\n+\n+static void tegra_ahbdma_synchronize(struct dma_chan *chan)\n+{\n+\twait_for_completion(&to_ahbdma_chan(chan)->idling);\n+}\n+\n+static void tegra_ahbdma_free_chan_resources(struct dma_chan *chan)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = to_ahbdma_chan(chan);\n+\tstruct tegra_ahbdma_tx_desc *tx;\n+\tstruct list_head *entry, *tmp;\n+\n+\tlist_for_each_safe(entry, tmp, &ahbdma_chan->pending_list) {\n+\t\ttx = list_entry(entry, struct tegra_ahbdma_tx_desc, node);\n+\t\tlist_del(entry);\n+\t\tkfree(tx);\n+\t}\n+}\n+\n+static void tegra_ahbdma_init_channel(struct tegra_ahbdma *tdma,\n+\t\t\t\t unsigned int chan_id)\n+{\n+\tstruct tegra_ahbdma_chan *ahbdma_chan = &tdma->channels[chan_id];\n+\tstruct dma_chan *dma_chan = &ahbdma_chan->dma_chan;\n+\tstruct dma_device *dma_dev = &tdma->dma_dev;\n+\n+\tINIT_LIST_HEAD(&ahbdma_chan->active_list);\n+\tINIT_LIST_HEAD(&ahbdma_chan->pending_list);\n+\tinit_completion(&ahbdma_chan->idling);\n+\tspin_lock_init(&ahbdma_chan->lock);\n+\tcomplete(&ahbdma_chan->idling);\n+\n+\tahbdma_chan->regs = tdma->regs + TEGRA_AHBDMA_CHANNEL_BASE(chan_id);\n+\tahbdma_chan->id = chan_id;\n+\n+\tdma_cookie_init(dma_chan);\n+\tdma_chan->device = dma_dev;\n+\n+\tlist_add_tail(&dma_chan->device_node, &dma_dev->channels);\n+}\n+\n+static struct dma_chan *tegra_ahbdma_of_xlate(struct of_phandle_args *dma_spec,\n+\t\t\t\t\t struct of_dma *ofdma)\n+{\n+\tstruct tegra_ahbdma *tdma = ofdma->of_dma_data;\n+\tstruct dma_chan *chan;\n+\tu32 csr;\n+\n+\tchan = dma_get_any_slave_channel(&tdma->dma_dev);\n+\tif (!chan)\n+\t\treturn NULL;\n+\n+\t/* enable channels flow control */\n+\tif (dma_spec->args_count == 1) {\n+\t\tcsr = TEGRA_AHBDMA_CHANNEL_FLOW;\n+\t\tcsr |= dma_spec->args[0] << TEGRA_AHBDMA_CHANNEL_REQ_SEL_SHIFT;\n+\n+\t\twritel_relaxed(csr,\n+\t\t\tto_ahbdma_chan(chan)->regs + TEGRA_AHBDMA_CHANNEL_CSR);\n+\t}\n+\n+\treturn chan;\n+}\n+\n+static int tegra_ahbdma_init_hw(struct tegra_ahbdma *tdma, struct device *dev)\n+{\n+\tint err;\n+\n+\terr = reset_control_assert(tdma->rst);\n+\tif (err) {\n+\t\tdev_err(dev, \"Failed to assert reset: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\terr = clk_prepare_enable(tdma->clk);\n+\tif (err) {\n+\t\tdev_err(dev, \"Failed to enable clock: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\tusleep_range(1000, 2000);\n+\n+\terr = reset_control_deassert(tdma->rst);\n+\tif (err) {\n+\t\tdev_err(dev, \"Failed to deassert reset: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\twritel_relaxed(TEGRA_AHBDMA_CMD_ENABLE, tdma->regs + TEGRA_AHBDMA_CMD);\n+\n+\twritel_relaxed(TEGRA_AHBDMA_IRQ_ENB_CH(0) |\n+\t\t TEGRA_AHBDMA_IRQ_ENB_CH(1) |\n+\t\t TEGRA_AHBDMA_IRQ_ENB_CH(2) |\n+\t\t TEGRA_AHBDMA_IRQ_ENB_CH(3),\n+\t\t tdma->regs + TEGRA_AHBDMA_IRQ_ENB_MASK);\n+\n+\treturn 0;\n+}\n+\n+static int tegra_ahbdma_probe(struct platform_device *pdev)\n+{\n+\tstruct dma_device *dma_dev;\n+\tstruct tegra_ahbdma *tdma;\n+\tstruct resource *res_regs;\n+\tunsigned int i;\n+\tint irq;\n+\tint err;\n+\n+\ttdma = devm_kzalloc(&pdev->dev, sizeof(*tdma), GFP_KERNEL);\n+\tif (!tdma)\n+\t\treturn -ENOMEM;\n+\n+\tirq = platform_get_irq(pdev, 0);\n+\tif (irq < 0) {\n+\t\tdev_err(&pdev->dev, \"Failed to get IRQ\\n\");\n+\t\treturn irq;\n+\t}\n+\n+\terr = devm_request_irq(&pdev->dev, irq, tegra_ahbdma_isr, 0,\n+\t\t\t dev_name(&pdev->dev), tdma);\n+\tif (err) {\n+\t\tdev_err(&pdev->dev, \"Failed to request IRQ\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tres_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);\n+\tif (!res_regs)\n+\t\treturn -ENODEV;\n+\n+\ttdma->regs = devm_ioremap_resource(&pdev->dev, res_regs);\n+\tif (IS_ERR(tdma->regs))\n+\t\treturn PTR_ERR(tdma->regs);\n+\n+\ttdma->clk = devm_clk_get(&pdev->dev, NULL);\n+\tif (IS_ERR(tdma->clk)) {\n+\t\tdev_err(&pdev->dev, \"Failed to get AHB-DMA clock\\n\");\n+\t\treturn PTR_ERR(tdma->clk);\n+\t}\n+\n+\ttdma->rst = devm_reset_control_get(&pdev->dev, NULL);\n+\tif (IS_ERR(tdma->rst)) {\n+\t\tdev_err(&pdev->dev, \"Failed to get AHB-DMA reset\\n\");\n+\t\treturn PTR_ERR(tdma->rst);\n+\t}\n+\n+\terr = tegra_ahbdma_init_hw(tdma, &pdev->dev);\n+\tif (err)\n+\t\treturn err;\n+\n+\tdma_dev = &tdma->dma_dev;\n+\n+\tINIT_LIST_HEAD(&dma_dev->channels);\n+\n+\tfor (i = 0; i < ARRAY_SIZE(tdma->channels); i++)\n+\t\ttegra_ahbdma_init_channel(tdma, i);\n+\n+\tdma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);\n+\tdma_cap_set(DMA_CYCLIC, dma_dev->cap_mask);\n+\tdma_cap_set(DMA_SLAVE, dma_dev->cap_mask);\n+\n+\tdma_dev->max_burst\t\t= 8;\n+\tdma_dev->directions\t\t= TEGRA_AHBDMA_DIRECTIONS;\n+\tdma_dev->src_addr_widths\t= TEGRA_AHBDMA_BUS_WIDTH;\n+\tdma_dev->dst_addr_widths\t= TEGRA_AHBDMA_BUS_WIDTH;\n+\tdma_dev->descriptor_reuse\t= true;\n+\tdma_dev->residue_granularity\t= DMA_RESIDUE_GRANULARITY_BURST;\n+\tdma_dev->device_free_chan_resources = tegra_ahbdma_free_chan_resources;\n+\tdma_dev->device_prep_slave_sg\t= tegra_ahbdma_prep_slave_sg;\n+\tdma_dev->device_prep_dma_cyclic\t= tegra_ahbdma_prep_dma_cyclic;\n+\tdma_dev->device_terminate_all\t= tegra_ahbdma_terminate_all;\n+\tdma_dev->device_issue_pending\t= tegra_ahbdma_issue_pending;\n+\tdma_dev->device_tx_status\t= tegra_ahbdma_tx_status;\n+\tdma_dev->device_config\t\t= tegra_ahbdma_config;\n+\tdma_dev->device_synchronize\t= tegra_ahbdma_synchronize;\n+\tdma_dev->dev\t\t\t= &pdev->dev;\n+\n+\terr = dma_async_device_register(dma_dev);\n+\tif (err) {\n+\t\tdev_err(&pdev->dev, \"Device registration failed %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\terr = of_dma_controller_register(pdev->dev.of_node,\n+\t\t\t\t\t tegra_ahbdma_of_xlate, tdma);\n+\tif (err) {\n+\t\tdev_err(&pdev->dev, \"OF registration failed %d\\n\", err);\n+\t\tdma_async_device_unregister(dma_dev);\n+\t\treturn err;\n+\t}\n+\n+\tplatform_set_drvdata(pdev, tdma);\n+\n+\treturn 0;\n+}\n+\n+static int tegra_ahbdma_remove(struct platform_device *pdev)\n+{\n+\tstruct tegra_ahbdma *tdma = platform_get_drvdata(pdev);\n+\n+\tof_dma_controller_free(pdev->dev.of_node);\n+\tdma_async_device_unregister(&tdma->dma_dev);\n+\tclk_disable_unprepare(tdma->clk);\n+\n+\treturn 0;\n+}\n+\n+static const struct of_device_id tegra_ahbdma_of_match[] = {\n+\t{ .compatible = \"nvidia,tegra20-ahbdma\" },\n+\t{ },\n+};\n+MODULE_DEVICE_TABLE(of, tegra_ahbdma_of_match);\n+\n+static struct platform_driver tegra_ahbdma_driver = {\n+\t.driver = {\n+\t\t.name\t= \"tegra-ahbdma\",\n+\t\t.of_match_table = tegra_ahbdma_of_match,\n+\t},\n+\t.probe\t= tegra_ahbdma_probe,\n+\t.remove\t= tegra_ahbdma_remove,\n+};\n+module_platform_driver(tegra_ahbdma_driver);\n+\n+MODULE_DESCRIPTION(\"NVIDIA Tegra AHB DMA Controller driver\");\n+MODULE_AUTHOR(\"Dmitry Osipenko <digetx@gmail.com>\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "v1", "4/5" ] }