From patchwork Fri Oct 23 01:59:11 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: yao yuan X-Patchwork-Id: 7469521 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id A5122BEEA4 for ; Fri, 23 Oct 2015 02:08:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 1B67C2076E for ; Fri, 23 Oct 2015 02:08:11 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 77206207BF for ; Fri, 23 Oct 2015 02:08:09 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZpRkE-0003u1-Bu; Fri, 23 Oct 2015 02:06:14 +0000 Received: from mail-bn1bon0132.outbound.protection.outlook.com ([157.56.111.132] helo=na01-bn1-obe.outbound.protection.outlook.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZpRk8-0003n9-SJ for linux-arm-kernel@lists.infradead.org; Fri, 23 Oct 2015 02:06:11 +0000 Received: from BN3PR0301CA0052.namprd03.prod.outlook.com (10.160.152.148) by BL2PR03MB481.namprd03.prod.outlook.com (10.141.92.140) with Microsoft SMTP Server (TLS) id 15.1.306.13; Fri, 23 Oct 2015 02:05:46 +0000 Received: from BL2FFO11OLC001.protection.gbl (2a01:111:f400:7c09::107) by BN3PR0301CA0052.outlook.office365.com (2a01:111:e400:401e::20) with Microsoft SMTP Server (TLS) id 15.1.306.13 via Frontend Transport; Fri, 23 Oct 2015 02:05:46 +0000 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; lists.infradead.org; dkim=none (message not signed) header.d=none;lists.infradead.org; dmarc=none action=none header.from=freescale.com; Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Received: from tx30smr01.am.freescale.net (192.88.168.50) by BL2FFO11OLC001.mail.protection.outlook.com (10.173.161.185) with Microsoft SMTP Server (TLS) id 15.1.306.13 via Frontend Transport; Fri, 23 Oct 2015 02:05:45 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id t9N25g8V018012; Thu, 22 Oct 2015 19:05:43 -0700 From: Yuan Yao To: , Subject: [PATCH v2 1/3] dma: Add Freescale qDMA engine driver support Date: Fri, 23 Oct 2015 09:59:11 +0800 Message-ID: <1445565553-1141-1-git-send-email-yao.yuan@freescale.com> X-Mailer: git-send-email 2.1.0.27.g96db324 X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1; BL2FFO11OLC001; 1:QAiNX6YdKqMlaMi53FGsbOuJ5IULkHpr3u2x+cVyBuEZ5eR2hxWITgd6ily+wTY3xQOdQ8r3YWLqrnJ9VPcMSoQDN1WFBfd6nLefXSxxsYv9LvP+hd6bX4l97KFmG8FJDMMZVAZLjD09FRzuxceT5kVMJwKVGAUg7Cs5QWCL82SVK1xm7X8NKlQye2t5B4t4ahPUdW8I47WAQl4Vl88UMcqpKl1H32TPNIyH8fwdGPZB1bahNF8dMGRN53RI2Y/MDMaFBB6HLigc7UvHV2CQCLEkLCob0TElYEYDwC3LGCUWZ56m8k3ktBsm8e7schDLFr2P3e3Ml4/7jycGY1fbH6J9Ex/LVNYFSEV8oEHtIJPim70OdOfLi/Ine8dKu6wZqGMM90oggs7Kpx0Bf/Z/YA== X-Forefront-Antispam-Report: CIP:192.88.168.50; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(2980300002)(1109001)(1110001)(339900001)(189002)(199003)(5008740100001)(50986999)(92566002)(36756003)(77096005)(229853001)(105606002)(106466001)(6806005)(5007970100001)(33646002)(11100500001)(104016004)(47776003)(85426001)(87936001)(5001770100001)(97736004)(48376002)(50466002)(5003940100001)(86362001)(81156007)(5001960100002)(189998001)(50226001)(19580395003)(19580405001)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:BL2PR03MB481; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Exchange-Diagnostics: 1; BL2PR03MB481; 2:io9aCTHOxFKBd5zysiamZgblQbkR7pxF1PAFZrIkUKtZjUeEJ114wbPp2W6+G0SMGCB3fXEptU8gmdkODXP26RCc1I0doTYAnTEgBmxAVFyo6MqAR2/h0FuXosT2DB24g8ChXySaAhByR+a0g2VMvbdtuS8MrPzrTtTmVuWnUBk=; 3:7t6hsyu9slfVzH23uMHiz+djarEMMvQVDsRo9ZQyKjxK8PY84Vqr98Gx7WiHLp04+Nd38jFd4zAh6Mb+nrpvaCVnNcr4YWtNuusM3jG5uA8WE6eDsFJ/5smTrM1F+Ir8oehxRqWQfEdv27amX7JD9q9bwJ/rYao35GgTGCo7YSodtKgIhVNLUgi+kRdfng83hbjqpe2hR6OfPAZ8FhPbqIiMjWie/nNEm610rA0RtFY=; 25:CUgHghu4McNO9yAxrdwvJi+WkQBIWsD+6E3RCLQV+3ZSBFSqLcSttqreZ7NZqsHZNYb2+YleFhW6tasVIBi3QrIMpsMauOvarntoS3VxdHKQIc7xFfwFenhOrb1uz7G6CRmzCUSvpeh92uXI/IUmTSQo8LveVC5QUMhLSxSoaC8LJqkVKuG2LsSp8sJmoiD6eFcaiD9NQmcASOhhs6zRr2w8NsYl6bzhb3I9kPJZ593i0zgSb5JS5R7s94Wg+vGV X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BL2PR03MB481; X-Microsoft-Exchange-Diagnostics: 1; BL2PR03MB481; 20:ZVhGG5/Sr8s8yfgDf6fvLgQ1c5nS6DgIW3zTfSwtHk+y/Z+a3uR44B/M0Tluf/jdHo2G3Zy6RsDRzKI53EqNaoUzA1Tjm1qmuPKv6d5rYe4Ssxb/U4SWLD7FSAMeE8lg8uh+a8JKheSjy5NdTGxj6Dqm5a0w6HylTPzfszFohOnyEfOHC7huAbt8LyLQtcZOq6vz5IxBJ4y08vkRdZ44x9A58fY1EI5T1/8BDHT/p1Aesc4B25GfUApyDCcOIn7lrHHtXae6QBfnCGkSbLBFB86EjHEs8R524NyZrqihFkBnFX4eQ94jxClY9GF/jV+y9wd9hrN6cmcgQ67olnIAUSy5ECOM0zM/vunmw2/wsuM=; 4:Oc1mvKsnmhQ0A3V6yk8R/S9Xv/vRByu7FYx6p/bsEh50eCRjE/6j01YxOTz5tUTUEBW7dq2J1ORQG+LvX+rBo6SFwTW9WCGBmJxNoxijh4Sma40bc6EpoIVMJ++275NjGbu1Bf9iyrid77109BzjCA8twbDPhQIUNIPKt2dpdRbTb3NoBGcJA+vnDC6x813Yy8l/W6xHJyukX18HLIDurMyYZaCM9zDCRZBp47tuP0K0BC48wpXGw8L0Sf3sEa0bzhrv256mxEEYnkIUsIOZbWtNoJgM+GRmiS+lVcrk0jeyYZV0Ek8za7L3pC7McmQQZYfHTZggGD8aZpJhaChWGDvf0STlTn4k2XLUMNLbA0hnUgjFYwKvOM0qtlfFAOzs X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(101931422205132); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(2401047)(520078)(5005006)(8121501046)(3002001)(102215026); SRVR:BL2PR03MB481; BCL:0; PCL:0; RULEID:; SRVR:BL2PR03MB481; X-Forefront-PRVS: 0738AF4208 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BL2PR03MB481; 23:Q3HNGo5uS7DOm7FNunk/GhZ59xoDMBpctiUEoX6Cr/?= =?us-ascii?Q?WAE9wYvVH6IENDxz2mcoOJzUX5+CuDn1+72R+6/kYWHiSX8BCj37YpLYkwXM?= =?us-ascii?Q?VVLR/hdbvYb6oAP61PzbyXbJKBzOqb5pdbMPPS41xNTrFvFXpaWwB8cj1t91?= =?us-ascii?Q?OKVnM7pPGcjBZ5lO7+1WTsLxLc2E/FqShV1K/AZlREf/HEIbvjxHKWwBJvGr?= =?us-ascii?Q?ga0z+WU/NPkVXECesERiRI9EYY5p3prhNnTDvD2zA312cTKaaRjdRl/6gj7r?= =?us-ascii?Q?ylJt68LtcrmqVRzcoFzSzg/zHBTSEXEDZRRquB6nMNXSnuR7lF1xZ7KhJJh9?= =?us-ascii?Q?L9rgGp6eOOzgfhdeCB6BQxSJXO3bHrWP6J6IngBSXnsQyhpigc/qszsKeKid?= =?us-ascii?Q?XLGGEqDnbPqsCRMYr1dDHZiohoZY0hRefA5Dg9CDjo7C1yh02ZNV6WzloTb2?= =?us-ascii?Q?0Jk43gxfooDQGvJ3oQADYgQuMW9fG2lwHoWOnTNu5YKN3al8/vKHx68gDnfy?= =?us-ascii?Q?FLFBGkQV781HbbDb1mfKV23Em4IzfMA6g+3fOXhJpOQy8i/2OZJ51pb3Jgh8?= =?us-ascii?Q?X0+PP5d4zr9RmUlPtMiG9b/duaBGZ0eWLiJb6t272LPb14UNiWy/1WaoV8KI?= =?us-ascii?Q?9PF9lQLkh7GDyHQync3WFal9ZKY8CserWqUOUb8Zz/JOEKpEwIExbUbjnuBk?= =?us-ascii?Q?NHmkZnhIL0ODT9FF20ZQDmW71bLaM0gP3Zkcy40dZnK0GGc9OZfCMEO/FHMp?= =?us-ascii?Q?HZT5filAZc8CIStDzZdxdRrabrQh4qJwJChHw6TEX7F8LvMMEbfnUXmZtL3f?= =?us-ascii?Q?TYM2POKoKKGPL3w2D+mJUjGDl1h4zFKqBBSLTtsEvDOZ1ThWc1auRePggOkd?= =?us-ascii?Q?LFFqZJRstGrsHhuAOLRcQXpcEAPBrtyUMvN9/sbw57Eqlq1ceYXPleNZYTGw?= =?us-ascii?Q?ScoNuYNlLSHSO7S+Nfhr4qqST0auG+kYv8QTvGJlb1+SFsVq6JxFG5qmOkKg?= =?us-ascii?Q?g=3D?= X-Microsoft-Exchange-Diagnostics: 1; BL2PR03MB481; 5:5Q7jsBsbsYi5mbQvyKQUwXVATrcDTnm9QkFr4p2PTP1AraviSNzOloRTvBUWry8UfrU5tJfc5IYvMnciTXRXiCjz4R+OkqJ1Ag4PWT7h9niIJF+yLmwxa1wZXLKy/sRGEklYjD0MMK6j+JVUWawf4Q==; 24:WVEYdbvKuOblQq4OKBm491R7Mc8vpg5vnJahZY8YLQ1VVQp4OwLCW69rVN63GoiVpP4pp/MPhvuobmHsEC5sKzkA3N3m4W24RHbs2hL5xiU=; 20:H06msVRLWWesPI5D4s4/q350S7qMLeiUkoyhKHYX+pSHXkyrmSMFRAMejw+Acwux31KRru4QW21U73K0ziC8YA== SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Oct 2015 02:05:45.8309 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BL2PR03MB481 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151022_190609_369088_35F0129F X-CRM114-Status: GOOD ( 27.69 ) X-Spam-Score: -1.9 (-) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: dan.j.williams@intel.com, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add Freescale Queue Direct Memory Access(qDMA) controller support. This module can be found on LS-1 and LS-2 SoCs. This add the legacy mode support for qDMA. Signed-off-by: Yuan Yao --- Changes since v1: 1. Separate Binding. 2. Remove dead code. 3. Add depend on for QDMA. --- MAINTAINERS | 8 + drivers/dma/Kconfig | 11 ++ drivers/dma/Makefile | 1 + drivers/dma/fsl-qdma.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 drivers/dma/fsl-qdma.c diff --git a/MAINTAINERS b/MAINTAINERS index a2d50fe..bc504df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4419,6 +4419,14 @@ L: linuxppc-dev@lists.ozlabs.org S: Maintained F: drivers/dma/fsldma.* +FREESCALE qDMA DRIVER +M: Yuan Yao +L: linux-arm-kernel@lists.infradead.org +L: dmaengine@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/dma/fsl-qdma.txt +F: drivers/dma/fsl-qdma.c + FREESCALE I2C CPM DRIVER M: Jochen Friedrich L: linuxppc-dev@lists.ozlabs.org diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index af81a7a..1743a89 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -193,6 +193,17 @@ config FSL_EDMA multiplexing capability for DMA request sources(slot). This module can be found on Freescale Vybrid and LS-1 SoCs. +config FSL_QDMA + tristate "Freescale qDMA engine support" + depends on SOC_LS1021A || ARCH_LAYERSCAPE + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the Freescale qDMA engine with command queue and legacy mode. + Channel virtualization is supported through enqueuing of DMA jobs to, + or dequeuing DMA jobs from, different work queues. + This module can be found on Freescale LS SoCs. + config FSL_RAID tristate "Freescale RAID engine Support" depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index ef9c099..47d63fe 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_FSL_EDMA) += fsl-edma.o +obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HSU_DMA) += hsu/ obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c new file mode 100644 index 0000000..b2c80d9 --- /dev/null +++ b/drivers/dma/fsl-qdma.c @@ -0,0 +1,510 @@ +/* + * drivers/dma/fsl-qdma.c + * + * Copyright 2014-2015 Freescale Semiconductor, Inc. + * + * Driver for the Freescale qDMA engine with legacy mode. + * This module can be found on Freescale LS SoCs. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "virt-dma.h" + +#define FSL_QDMA_DMR 0x0 +#define FSL_QDMA_DSR_P 0x4 + +#define FSL_QDMA_DSR_M 0x10004 +#define FSL_QDMA_DLMR 0x10100 +#define FSL_QDMA_DLSR 0x10104 +#define FSL_QDMA_DLSATR 0x10110 +#define FSL_QDMA_DLSAR 0x10114 +#define FSL_QDMA_DLDATR 0x10118 +#define FSL_QDMA_DLDAR 0x1011c +#define FSL_QDMA_DLBCR 0x10120 +#define FSL_QDMA_DLESAD 0x10148 +#define FSL_QDMA_DLEDAD 0x1014c + +#define FSL_QDMA_DLMR_CS 0x1 +#define FSL_QDMA_DLMR_EOSIE 0x200 +#define FSL_QDMA_DLMR_EIE 0x40 +#define FSL_QDMA_DLSR_TE 0x80 +#define FSL_QDMA_DLSR_CH 0x20 +#define FSL_QDMA_DLSR_PE 0x10 +#define FSL_QDMA_DLSR_CB 0x4 +#define FSL_QDMA_DLSR_EOSI 0x2 + +#define FSL_QDMA_SRTTYPE_R_N 0x40000 + +struct fsl_qdma_tcd { + u64 saddr; + u32 nbytes; + u64 daddr; +}; + +struct fsl_qdma_chan_config { + enum dma_transfer_direction dir; + enum dma_slave_buswidth addr_width; + u32 burst; + u32 attr; +}; + +struct fsl_qdma_desc { + struct virt_dma_desc vdesc; + struct fsl_qdma_chan *qchan; + struct fsl_qdma_tcd tcd; +}; + +struct fsl_qdma_chan { + struct virt_dma_chan vchan; + struct fsl_qdma_desc *desc; + enum dma_status status; + u32 slave_id; + struct fsl_qdma_engine *qdma; +}; + +struct fsl_qdma_engine { + struct dma_device dma_dev; + void __iomem *membase; + u32 n_chans; + struct mutex fsl_qdma_mutex; + int controller_irq; + int err_irq; + bool big_endian; + struct fsl_qdma_chan chans[]; + +}; + +static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr) +{ + if (qdma->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val, + void __iomem *addr) +{ + if (qdma->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_qdma_chan, vchan.chan); +} + +static struct fsl_qdma_desc *to_fsl_qdma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_qdma_desc, vdesc); +} + +static void fsl_qdma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); +} + +static void fsl_qdma_set_tcd_params(struct fsl_qdma_chan *fsl_chan, + u64 src, u64 dst, u32 nbytes) +{ + void __iomem *addr = fsl_chan->qdma->membase; + u32 reg; + + /* + * Source address. + * Represents address bits 31-0 of a 49-bit source address. + */ + qdma_writel(fsl_chan->qdma, (u32)src, addr + FSL_QDMA_DLSAR); + /* + * Source address. + * Represents address bits 47-32 of a 49-bit source address. + */ + reg = qdma_readl(fsl_chan->qdma, addr + FSL_QDMA_DLSATR); + reg |= (u16)(src >> 32) & 0xffff; + reg |= FSL_QDMA_SRTTYPE_R_N; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLSATR); + /* + * Source address. + * Represents address bits 48 of a 49-bit source address. + */ + reg = qdma_readl(fsl_chan->qdma, addr + FSL_QDMA_DLESAD); + reg |= (src >> 48) & 0x1; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLESAD); + + /* + * Destination address. + * Represents address bits 31-0 of a 49-bit destination address. + */ + qdma_writel(fsl_chan->qdma, (u32)dst, addr + FSL_QDMA_DLDAR); + /* + * Destination address. + * Represents address bits 47-32 of a 49-bit destination address. + */ + reg = qdma_readl(fsl_chan->qdma, addr + FSL_QDMA_DLDATR); + reg |= (u16)(dst >> 32) & 0xffff; + reg |= FSL_QDMA_SRTTYPE_R_N; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLDATR); + /* + * Destination address. + * Represents address bits 48 of a 49-bit destination address. + */ + reg = qdma_readl(fsl_chan->qdma, addr + FSL_QDMA_DLEDAD); + reg |= (dst >> 48) & 0x1; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLEDAD); + + /* + * Byte count. + * Contains the number of bytes to transfer. + */ + qdma_writel(fsl_chan->qdma, nbytes, addr + FSL_QDMA_DLBCR); +} + +static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma) +{ + u32 reg; + + reg = qdma_readl(fsl_qdma, fsl_qdma->membase + FSL_QDMA_DLMR); + reg |= FSL_QDMA_DLMR_EOSIE; + reg |= FSL_QDMA_DLMR_EIE; + qdma_writel(fsl_qdma, reg, fsl_qdma->membase + FSL_QDMA_DLMR); + return 0; +} + +static void fsl_qdma_enable_request(struct fsl_qdma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->qdma->membase; + u32 reg; + + reg = qdma_readl(fsl_chan->qdma, addr + FSL_QDMA_DLMR); + + reg &= ~FSL_QDMA_DLMR_CS; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLMR); + + reg |= FSL_QDMA_DLMR_CS; + qdma_writel(fsl_chan->qdma, reg, addr + FSL_QDMA_DLMR); +} + +static struct fsl_qdma_desc *fsl_qdma_alloc_desc(struct fsl_qdma_chan *fsl_chan) +{ + struct fsl_qdma_desc *fsl_desc; + + fsl_desc = kzalloc(sizeof(*fsl_desc), GFP_NOWAIT); + + if (!fsl_desc) + return NULL; + + fsl_desc->qchan = fsl_chan; + + return fsl_desc; +} + +static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc) +{ + struct fsl_qdma_desc *fsl_desc; + + fsl_desc = to_fsl_qdma_desc(vdesc); + kfree(fsl_desc); +} + +static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan) +{ + struct fsl_qdma_tcd *tcd; + struct virt_dma_desc *vdesc; + + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + + fsl_chan->desc = to_fsl_qdma_desc(vdesc); + tcd = &fsl_chan->desc->tcd; + fsl_qdma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->nbytes); + fsl_qdma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; +} + +static void fsl_qdma_issue_pending(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->desc) + fsl_qdma_enqueue_desc(fsl_chan); + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); +} + +static struct dma_async_tx_descriptor * +fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, + dma_addr_t src, size_t len, unsigned long flags) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + struct fsl_qdma_desc *fsl_desc; + struct fsl_qdma_tcd *tcd; + + fsl_desc = fsl_qdma_alloc_desc(fsl_chan); + if (!fsl_desc) + return NULL; + + tcd = &fsl_desc->tcd; + tcd->saddr = (u64)src; + tcd->nbytes = (u32)len; + tcd->daddr = (u64)dst; + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} + +static enum dma_status fsl_qdma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + return dma_cookie_status(chan, cookie, txstate); +} + +static irqreturn_t fsl_qdma_controller_handler(int irq, void *dev_id) +{ + struct fsl_qdma_engine *fsl_qdma = dev_id; + struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[0]; + void __iomem *addr = fsl_qdma->membase; + u32 reg; + + reg = qdma_readl(fsl_qdma, addr + FSL_QDMA_DLSR); + if (!(reg & FSL_QDMA_DLSR_EOSI)) + return IRQ_NONE; + + /* Don't clean TE and PE bit if they are set. */ + reg &= ~FSL_QDMA_DLSR_TE & ~FSL_QDMA_DLSR_PE; + qdma_writel(fsl_qdma, reg, addr + FSL_QDMA_DLSR); + + spin_lock(&fsl_chan->vchan.lock); + list_del(&fsl_chan->desc->vdesc.node); + vchan_cookie_complete(&fsl_chan->desc->vdesc); + fsl_chan->desc = NULL; + fsl_chan->status = DMA_COMPLETE; + fsl_qdma_enqueue_desc(fsl_chan); + spin_unlock(&fsl_chan->vchan.lock); + + return IRQ_HANDLED; +} + +static irqreturn_t fsl_qdma_controller_handler_err(int irq, void *dev_id) +{ + struct fsl_qdma_engine *fsl_qdma = dev_id; + u32 reg; + + reg = qdma_readl(fsl_qdma, fsl_qdma->membase + FSL_QDMA_DLSR); + + if (reg & FSL_QDMA_DLSR_TE) { + dev_err(fsl_qdma->dma_dev.dev, + "Transfer error. Check your address please!\n"); + } + + if (reg & FSL_QDMA_DLSR_PE) { + dev_err(fsl_qdma->dma_dev.dev, + "Programming error. Check your setting please!\n"); + } + + /* Don't clean EOSI bit if it's set. */ + reg &= ~FSL_QDMA_DLSR_EOSI; + qdma_writel(fsl_qdma, reg, fsl_qdma->membase + FSL_QDMA_DLSR); + + return IRQ_HANDLED; +} + +static irqreturn_t fsl_qdma_irq_handler(int irq, void *dev_id) +{ + if (fsl_qdma_controller_handler(irq, dev_id) == IRQ_HANDLED) + return IRQ_HANDLED; + + return fsl_qdma_controller_handler_err(irq, dev_id); +} + +static int fsl_qdma_irq_init(struct platform_device *pdev, + struct fsl_qdma_engine *fsl_qdma) +{ + int ret; + + fsl_qdma->controller_irq = platform_get_irq_byname(pdev, + "qdma-tx"); + if (fsl_qdma->controller_irq < 0) { + dev_err(&pdev->dev, "Can't get qdma controller irq.\n"); + return fsl_qdma->controller_irq; + } + + fsl_qdma->err_irq = platform_get_irq_byname(pdev, + "qdma-err"); + if (fsl_qdma->err_irq < 0) { + dev_err(&pdev->dev, "Can't get qdma err irq.\n"); + return fsl_qdma->err_irq; + } + + if (fsl_qdma->controller_irq == fsl_qdma->err_irq) { + ret = devm_request_irq(&pdev->dev, fsl_qdma->controller_irq, + fsl_qdma_irq_handler, 0, + "qDMA controller", fsl_qdma); + + if (ret) { + dev_err(&pdev->dev, "Can't register qDMA IRQ.\n"); + return ret; + } + } else { + ret = devm_request_irq(&pdev->dev, fsl_qdma->controller_irq, + fsl_qdma_controller_handler, 0, + "qDMA controller", fsl_qdma); + if (ret) { + dev_err(&pdev->dev, + "Can't register qDMA controller IRQ.\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, fsl_qdma->err_irq, + fsl_qdma_controller_handler_err, 0, + "qDMA err", fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't register qDMA err IRQ.\n"); + return ret; + } + } + + return 0; +} + +static int fsl_qdma_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_qdma_engine *fsl_qdma; + struct fsl_qdma_chan *fsl_chan; + struct resource *res; + unsigned int len, chans; + int ret, i; + + ret = of_property_read_u32(np, "channels", &chans); + if (ret) { + dev_err(&pdev->dev, "Can't get channels.\n"); + return ret; + } + + len = sizeof(*fsl_qdma) + sizeof(*fsl_chan) * chans; + fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_qdma) + return -ENOMEM; + + fsl_qdma->n_chans = chans; + mutex_init(&fsl_qdma->fsl_qdma_mutex); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_qdma->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_qdma->membase)) + return PTR_ERR(fsl_qdma->membase); + + ret = fsl_qdma_irq_init(pdev, fsl_qdma); + if (ret) + return ret; + + fsl_qdma->big_endian = of_property_read_bool(np, "big-endian"); + INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels); + for (i = 0; i < fsl_qdma->n_chans; i++) { + struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i]; + + fsl_chan->qdma = fsl_qdma; + fsl_chan->desc = NULL; + fsl_chan->vchan.desc_free = fsl_qdma_free_desc; + vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev); + } + + dma_cap_set(DMA_PRIVATE, fsl_qdma->dma_dev.cap_mask); + dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask); + + fsl_qdma->dma_dev.dev = &pdev->dev; + fsl_qdma->dma_dev.device_free_chan_resources + = fsl_qdma_free_chan_resources; + fsl_qdma->dma_dev.device_tx_status = fsl_qdma_tx_status; + fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy; + fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending; + + platform_set_drvdata(pdev, fsl_qdma); + + ret = dma_async_device_register(&fsl_qdma->dma_dev); + if (ret) { + dev_err(&pdev->dev, "Can't register Freescale qDMA engine.\n"); + return ret; + } + + ret = fsl_qdma_reg_init(fsl_qdma); + if (ret) { + dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n"); + return ret; + } + + return 0; +} + +static int fsl_qdma_remove(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev); + + of_dma_controller_free(np); + dma_async_device_unregister(&fsl_qdma->dma_dev); + return 0; +} + +static const struct of_device_id fsl_qdma_dt_ids[] = { + { .compatible = "fsl,ls1021a-qdma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids); + +static struct platform_driver fsl_qdma_driver = { + .driver = { + .name = "fsl-qdma", + .owner = THIS_MODULE, + .of_match_table = fsl_qdma_dt_ids, + }, + .probe = fsl_qdma_probe, + .remove = fsl_qdma_remove, +}; + +static int __init fsl_qdma_init(void) +{ + return platform_driver_register(&fsl_qdma_driver); +} +subsys_initcall(fsl_qdma_init); + +static void __exit fsl_qdma_exit(void) +{ + platform_driver_unregister(&fsl_qdma_driver); +} +module_exit(fsl_qdma_exit); + +MODULE_ALIAS("platform:fsl-qdma"); +MODULE_DESCRIPTION("Freescale qDMA engine driver"); +MODULE_LICENSE("GPL v2");