From patchwork Fri Sep 11 05:53:52 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: yao yuan X-Patchwork-Id: 7157621 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 38CF9BEEC1 for ; Fri, 11 Sep 2015 06:02:20 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 841BE20845 for ; Fri, 11 Sep 2015 06:02:17 +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 C57DD20844 for ; Fri, 11 Sep 2015 06:02:15 +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 1ZaHNj-00020J-8o; Fri, 11 Sep 2015 06:00:19 +0000 Received: from mail-bn1bbn0103.outbound.protection.outlook.com ([157.56.111.103] helo=na01-bn1-obe.outbound.protection.outlook.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZaHNY-0000Ls-A9 for linux-arm-kernel@lists.infradead.org; Fri, 11 Sep 2015 06:00:10 +0000 Received: from BN3PR0301CA0039.namprd03.prod.outlook.com (10.160.180.177) by BLUPR03MB487.namprd03.prod.outlook.com (10.141.79.27) with Microsoft SMTP Server (TLS) id 15.1.262.15; Fri, 11 Sep 2015 05:59:44 +0000 Received: from BN1AFFO11FD029.protection.gbl (2a01:111:f400:7c10::112) by BN3PR0301CA0039.outlook.office365.com (2a01:111:e400:4000::49) with Microsoft SMTP Server (TLS) id 15.1.262.15 via Frontend Transport; Fri, 11 Sep 2015 05:59:44 +0000 Authentication-Results: spf=fail (sender IP is 192.88.158.2) smtp.mailfrom=freescale.com; vger.kernel.org; dkim=none (message not signed) header.d=none;vger.kernel.org; dmarc=none action=none header.from=freescale.com; Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.158.2 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.158.2; helo=az84smr01.freescale.net; Received: from az84smr01.freescale.net (192.88.158.2) by BN1AFFO11FD029.mail.protection.outlook.com (10.58.52.184) with Microsoft SMTP Server (TLS) id 15.1.262.18 via Frontend Transport; Fri, 11 Sep 2015 05:59:44 +0000 Received: from titan.ap.freescale.net ([10.192.208.233]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id t8B5xciE003831; Thu, 10 Sep 2015 22:59:39 -0700 From: Yuan Yao To: , Subject: [PATCH 1/2] dma: Add Freescale qDMA engine driver support Date: Fri, 11 Sep 2015 13:53:52 +0800 Message-ID: <1441950833-27684-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; BN1AFFO11FD029; 1:EFP1B0P8SgG2Y2nz8/z92RaiNr/JXDdd3rXFjKTIa/2ff2ZOaqmHWgA06Bg2Opqtr7Mk6XuKi5qB7v08unsCaxOC77wUOgTs+Y0Xt28jlxRfW2bhs9urnwQXbHXqNE3xbjoZ1iXy9su5UQbHcRNuWhbx5Y+tH849lL5efqaY1Xx9fbMCG2wLF1bQKK5QHLBe64Ym/GgAAA7Y3Q91JNvcdFZsRkrYJg8qprAEiGb5m2WCtmu6yS+a7NUaMZaNKzX1SY2qfDgucz5maek0E1+TNbmKajmk1NVXr36ecLupzbv8x0g05RNT6rlY4E5jphR740PMr6QhiP2lFOV/bSAFVze5FGzdCsphpoInYpsYSlsm6hAzjpfRRxXTG85Aeu9lgXFjlnqB4Rh8dvX4V8rHDw== X-Forefront-Antispam-Report: CIP:192.88.158.2; CTRY:US; IPV:NLI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(2980300002)(1110001)(1109001)(339900001)(189002)(199003)(5001860100001)(5001830100001)(189998001)(77096005)(5001960100002)(46102003)(77156002)(36756003)(87936001)(68736005)(97736004)(85426001)(5003940100001)(50226001)(64706001)(5001770100001)(11100500001)(62966003)(81156007)(4001540100001)(19580395003)(47776003)(50986999)(92566002)(5007970100001)(33646002)(50466002)(86362001)(104016003)(6806004)(19580405001)(48376002)(229853001)(69596002)(106466001)(105606002)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:BLUPR03MB487; H:az84smr01.freescale.net; FPR:; SPF:Fail; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Exchange-Diagnostics: 1; BLUPR03MB487; 2:rlo6JorAzZEFRLHOegF52skzg6Fz9s+krtD44zUVd2JWy3erKCdprq5oE/mRsQz1n/oeSFKuS3J7OYAi46vCWacW8R1Mci87ryG8td/uV14SJBepfeueVXhgoC04nhT8+W6m1G1gUhz1IsuNrbWp+Rji4k9zCAFjwAD86hWEUlE=; 3:jFn3tmmgtnUE6BOjIkxDuRPhIJKvAvFhYOpFcZjArgnTn9ISaZfW+eJMK6r0dRPkr3+vgPNJiAYqZrzcFqXNFTWVTbRe2WLxcCkqxeNwtv5tJGWL+33A8wtFp0oGGsPfhxv3tJWw9kOxGLtfFB8Kt9fFg2HI9cftguLXzzz0c0Cbp4zT1LvLEskNP+oqHctAEWK44clCCnU9+1rOEkBHVnC4SNI0dbBkvUnT82y96as=; 25:zkTNFpRsVtXSX1M7lhiFRN0kvEmy74dArtrgSJQ9eqNDERnH8TnbGRaFfFp9gcwgcBmDYQ/2LxRoRpvEpkcZCX8LoqKaQsEqfr09on81r9ROQlOEkpZvOS8pW6g4l3UxwvlhH7qteZTMs4JiPSSHUsKnaQzrKPvQhaHztdFcUqu8HegYje94YydlHMTyJyEH8iclSJBwEBM/wlISRfyu7JVnG8q4KMoUxgTaFKLdpqGKYM3CmwjcImKqqG1DjgVg X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BLUPR03MB487; X-Microsoft-Exchange-Diagnostics: 1; BLUPR03MB487; 20:3mxBKedgYUJhJn8Mg8b2fayhd8s4yJ7xZTJpnY4kWrWSbY2Gn2SYObD1llMd2eV10XI41aLJ+4D4zoYg8Iv8H+v7D9Ln5xy7r1cpM0yEHCe9HRxwn7f1f1fNIIDFfLJ67ZCJKtimH7A68FMcvmeMDiXJ3jJXqk9bazy0YYInc+0YxJ1SSwyUT87mCD+Z0y7Syzsq0oflkvj2CQt8P52clScR6hZFh5Mo/MdBOyKVMPV8Yt5ZOfoPfxG8YYcYDgJsKAZCsmrhvMq6F38QYKBeJFiRwuC3g75jy5EavfIAsRcsJ5o+Sv9RXH78aJYKvJgaudD8uw5hpoFba7iHxLhUhSAGG9U4kqe4kfY1mk7LVxI=; 4:BkG8PfmB1PsPdwEHGU5GFih16tM9Dvq2u2o423509eDYoIAz8iWAgEMO+CS5g2CKdNtIyahUs1ghpFiHHSYXep//Up3w6bLqpmXUO6zG1mPATtdk4t/XmL+Om3dNEslDXz+k4061SdTNlvhulPz/R0avBQYjnE3NmbeMmzwZP+hD9ous9n/80a/cSecJck1OgMebojV9+2FCbgw6WFaMgk8FdZR+EgfHhwdG3Wst0FcsTT6Jd+ixDx0zlLkMwb9iNEqA3aS5699q1VMYPgzi6zfC8+sj55uIBb0GxRdlUMnsMv+9ONJxhAf+Ad244zsm X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5005006)(8121501046)(3002001); SRVR:BLUPR03MB487; BCL:0; PCL:0; RULEID:; SRVR:BLUPR03MB487; X-Forefront-PRVS: 06968FD8C4 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BLUPR03MB487; 23:7EBvbyOVihAmIzW0W9fEax+twnFYRtmOJgLTZs3mHi?= =?us-ascii?Q?QNevxEgJA4aW6ZZNgCdHBJ+uQoHq1nn1fd5goNl+sqLP/mpLtGSir8646Llt?= =?us-ascii?Q?XV1STD1Kk6SM+izf9MNwJeurftlF0ScMlU2+GLjuIVfQ/bG8UtwWVEt58Sdu?= =?us-ascii?Q?F2PeGzGJJ5aGjYNxdHXe7W75ReHu5vuHiZZcY4EYNEz7OQmdMr2a9P01VacD?= =?us-ascii?Q?Ywz76XkXccKK9eEwR82YYWJX+UddexoLLtWAkTvXzz/iNhoqbZG+TJ4Ph5L1?= =?us-ascii?Q?pXRrOC66Ntyqa6mnGcAhAKyqSFckA60ByF34l0lXbe8SEypwHtr/mnyzxDXr?= =?us-ascii?Q?XvarNHrLgGFpfVNsKv9EfGjZVyBi0/tSFn1V6YMy15DH7fcYVELyD/W0AUgL?= =?us-ascii?Q?MaFZcylsFJQWac2XTmYC8Psi2TVmFvG2DstWVwgDltHP9TXRgcgHNdrtbGx1?= =?us-ascii?Q?lPBQquknaFos5rlgZftbVfB3tmuzDnXo5UlPlZ7junItIJcOpuavj1fnEtOQ?= =?us-ascii?Q?5Cvi+7uhTKMVvRElJoFk2gtTEAXBxZoNe3tnRiXYlOKK4lOqqi9G5gY5Wi16?= =?us-ascii?Q?WmnOzTmotiJFKxLMIvrOhvdkznvHuuZMV+zMtegsLQT62Uqt+jFligNLTmPY?= =?us-ascii?Q?pFT/cJPIheFBwkifZjxibnNfpqs+O1xPS6JcEVrx12qma5KVOsAhKGqdeQ0E?= =?us-ascii?Q?X6dnK0dnoMpBRxaGqe3q0T+GfsLGnH8cjTmTMw7VUgtoDkrDvRqeNrQLtb9N?= =?us-ascii?Q?aQda/aoYychbAynCNsfCH0+ai1Dh30mKAkaL23h8mKxzavXAbmyrFYkcNwBD?= =?us-ascii?Q?9RGGJR1CNx0MH3scoK0RgYS0/v2mPsC6qZXcsaXDxhSphKp96oXuERtFV8oQ?= =?us-ascii?Q?Rnjy6JgjoXTbZs1esuv1N406gaWXvps1c+jnBUZ5o+rjShQPswVTVEVXSW4V?= =?us-ascii?Q?ONo1ujHkPLcovn7jrerNsdZDICKNQ4roYceLUGNFF7Qqsp0xuRYIhOiiwbvF?= =?us-ascii?Q?I91CdkZf9YlpLOrEnFfbEoWJbIZwj2FMZ9DD949hsyL/Nrnm9mpn5MJisjzy?= =?us-ascii?Q?f+1iQWdxK7PKly23oWDGDXkHg4XjREkC5/rN5ceeNdsQfW6Vw/aqxnL5Zd7c?= =?us-ascii?Q?29cZjVMWM83w/Nh0esO6v014Upe8FeULQKhx6i1XY5FcvxAd7Pe7Qj0u0IXM?= =?us-ascii?Q?ObK3oQpdc6LEM=3D?= X-Microsoft-Exchange-Diagnostics: 1; BLUPR03MB487; 5:xf0yKIj93NPhfEEhGa8/TSK4Bq1sFlojRfouKMqTH0yv1/+hhFAwZC40r7GBxO3Lq59DPn6u7X4VZT32WnH5rO2qlk2nJP5kOZw5/ZA9zBmanRsUDemRODmMroQLpCP0R8hpM8Z3O1ho4UsKT+JoJw==; 24:xndczDs+bCkbWyrlvj9avbjuRIgNdVbxJhqc9tWRRipZUD2D3Rgb3lXimqeV+uvNybaMoJ6Nw1HPaKJpkrg3yr5ac3NARAV4ESD2T4iI3cs=; 20:wHgN3oqS6glI9KaJLhHiPD9q3ahizZBjPbsJD4jd+m7GQCF8MRdcJoQ1LQC+idJUjyqPmH9OAODhr1snERefdQ== SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Sep 2015 05:59:44.0616 (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.158.2]; Helo=[az84smr01.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR03MB487 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150910_230008_668212_8DB4D71D X-CRM114-Status: GOOD ( 25.04 ) 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: dmaengine@vger.kernel.org, devicetree@vger.kernel.org, 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, T_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 --- Documentation/devicetree/bindings/dma/fsl-qdma.txt | 43 ++ MAINTAINERS | 7 + drivers/dma/Kconfig | 10 + drivers/dma/Makefile | 1 + drivers/dma/fsl-qdma.c | 521 +++++++++++++++++++++ 5 files changed, 582 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/fsl-qdma.txt create mode 100644 drivers/dma/fsl-qdma.c diff --git a/Documentation/devicetree/bindings/dma/fsl-qdma.txt b/Documentation/devicetree/bindings/dma/fsl-qdma.txt new file mode 100644 index 0000000..cdae71c --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl-qdma.txt @@ -0,0 +1,43 @@ +* Freescale queue Direct Memory Access Controller(qDMA) Controller + + The qDMA controller transfers blocks of data between one source and one or more +destinations. The blocks of data transferred can be represented in memory as contiguous +or non-contiguous using scatter/gather table(s). Channel virtualization is supported +through enqueuing of DMA jobs to, or dequeuing DMA jobs from, different work +queues. + Legacy mode is primarily included for software requiring the earlier +QorIQ DMA programming model. This mode provides a simple programming +model not utilizing the datapath architecture. In legacy mode, DMA +operations are directly configured through a set of architectural +registers per channel. + +* qDMA Controller +Required properties: +- compatible : + - "fsl,ls-qdma" for qDMA used similar to that on LS SoC +- reg : Specifies base physical address(s) and size of the qDMA registers. + The region is qDMA control register's address and size. +- interrupts : A list of interrupt-specifiers, one for each entry in + interrupt-names. +- interrupt-names : Should contain: + "qdma-tx" - the interrupt + "qdma-err" - the error interrupt +- channels : Number of channels supported by the controller + +Optional properties: +- big-endian: If present registers and hardware scatter/gather descriptors + of the qDMA are implemented in big endian mode, otherwise in little + mode. + + +Examples: + + qdma: qdma@8390000 { + compatible = "fsl,ls-qdma"; + reg = <0x0 0x8380000 0x0 0x20000>; + interrupts = , + ; + interrupt-names = "qdma-tx", "qdma-err"; + big-endian; + channels = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 5772ccf..a4d1b52 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4357,6 +4357,13 @@ 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 +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 b458475..e29e985 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -193,6 +193,16 @@ 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" + 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 7711a71..8de7526 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..846cdba --- /dev/null +++ b/drivers/dma/fsl-qdma.c @@ -0,0 +1,521 @@ +/* + * 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 int fsl_qdma_alloc_chan_resources(struct dma_chan *chan) +{ + struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan); + + fsl_chan->desc = NULL; + return 0; +} + +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_SLAVE, 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_alloc_chan_resources + = fsl_qdma_alloc_chan_resources; + 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,ls-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");