From patchwork Wed Mar 6 06:30:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Clark Wang X-Patchwork-Id: 10840439 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CE5A617E0 for ; Wed, 6 Mar 2019 06:31:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA3452C75A for ; Wed, 6 Mar 2019 06:31:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id ADF3A2C776; Wed, 6 Mar 2019 06:31:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA30C2C756 for ; Wed, 6 Mar 2019 06:31:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729077AbfCFGa4 (ORCPT ); Wed, 6 Mar 2019 01:30:56 -0500 Received: from mail-eopbgr50080.outbound.protection.outlook.com ([40.107.5.80]:11489 "EHLO EUR03-VE1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1729063AbfCFGaz (ORCPT ); Wed, 6 Mar 2019 01:30:55 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=gC3JqbsctjR9Zfq8KvrJNyIX0h08B5bpWFDR9qTyfVw=; b=arOp9k7wywqBQDDyemY/HF+FHtYZS4eWLZpOjZirWJkxjMjabT9gdR+EFoQZxEs3rZsmD0XmgU2e4U9oPM2IzqbL1cozcKnkjEpBsrBJ9bYYLM28u6TDvcSUmatI/oCgUZElnCwsFqCjq6srVAPMfNhQCWL+8Vw3HghWtoFd8Ew= Received: from AM6PR04MB5016.eurprd04.prod.outlook.com (20.177.34.88) by AM6PR04MB4373.eurprd04.prod.outlook.com (20.177.38.91) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1665.19; Wed, 6 Mar 2019 06:30:45 +0000 Received: from AM6PR04MB5016.eurprd04.prod.outlook.com ([fe80::5841:afe:53fd:42bb]) by AM6PR04MB5016.eurprd04.prod.outlook.com ([fe80::5841:afe:53fd:42bb%6]) with mapi id 15.20.1665.020; Wed, 6 Mar 2019 06:30:45 +0000 From: Clark Wang To: "broonie@kernel.org" CC: "linux-spi@vger.kernel.org" , "linux-kernel@vger.kernel.org" Subject: [PATCH V2 6/8] spi: lpspi: add dma mode support Thread-Topic: [PATCH V2 6/8] spi: lpspi: add dma mode support Thread-Index: AQHU0+Yh6Oh4Uxb7nEmgG98iwvig9g== Date: Wed, 6 Mar 2019 06:30:45 +0000 Message-ID: <20190306063020.793-7-xiaoning.wang@nxp.com> References: <20190306063020.793-1-xiaoning.wang@nxp.com> In-Reply-To: <20190306063020.793-1-xiaoning.wang@nxp.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: SG2PR02CA0029.apcprd02.prod.outlook.com (2603:1096:3:18::17) To AM6PR04MB5016.eurprd04.prod.outlook.com (2603:10a6:20b:9::24) x-mailer: git-send-email 2.17.1 authentication-results: spf=none (sender IP is ) smtp.mailfrom=xiaoning.wang@nxp.com; x-ms-exchange-messagesentrepresentingtype: 1 x-originating-ip: [119.31.174.71] x-ms-publictraffictype: Email x-ms-office365-filtering-correlation-id: f4e305fb-8209-4fcc-634b-08d6a1fd4354 x-ms-office365-filtering-ht: Tenant x-microsoft-antispam: BCL:0;PCL:0;RULEID:(2390118)(7020095)(4652040)(8989299)(4534185)(4627221)(201703031133081)(201702281549075)(8990200)(5600127)(711020)(4605104)(4618075)(2017052603328)(7153060)(7193020);SRVR:AM6PR04MB4373; x-ms-traffictypediagnostic: AM6PR04MB4373: x-microsoft-exchange-diagnostics: =?iso-8859-1?q?1=3BAM6PR04MB4373=3B23=3Alx?= =?iso-8859-1?q?8TP3XXymNTEnyC8VNYKt159PUy4eRW1+97542DgW3MHsjdRAQvDy5mhLJSs8?= =?iso-8859-1?q?ap2liNJKipP7tQR/dIa8kbcyKV2tZMvFbeNI2j1AcLY0EeJVHK/9x1XtNdBW?= =?iso-8859-1?q?ISpu4YKgdQVjOC1Mg5QHNiThsi0yKH7tIZN+96gIDtwwd0QojRFC+D4sPgWM?= =?iso-8859-1?q?/HaJiC/72js3OzvAZA2j0t4MVnSFQbbCXOqWPWZxsaw6GjbAvX96o7R/2SVu?= =?iso-8859-1?q?sfhPuFcFa1yQF8sTt9haRzJBlGMFdk8ViDb+PZut7j+g7eFh/Y/13wdLx8Gt?= =?iso-8859-1?q?qd7UxOF9Q3PyIHdMsMfGO+VmfGIXiHamzegifXk/iS3tloiS9wOyIx8xOh0f?= =?iso-8859-1?q?rWibV73UoUrABKAkkmehCb8yI/zmFxetubBIdA467/LdG6lK3ZXoLdxUo58D?= =?iso-8859-1?q?4YmviwwsJvH75paDlIlieq0QHb751AdnTb8RjxxzgPj/vAWexqR+0wWkD4Fr?= =?iso-8859-1?q?D8IbX92Y+OpBL9XKb//ZeSKfC/BzZD2iuP+nrFm+HlP3nIIOTY2FZ8u6s1lL?= =?iso-8859-1?q?7HCJzWwf9CSIcbp7iI+RA8+WHX0RXhKHeb0WRKUzcWG+haVb7H3rEiJ3eh1r?= =?iso-8859-1?q?+pDhGtkRwfvPhcubNuNgFCJQsytI68ImF/37ffjRl3ChwFN/fgWTdYKljA26?= =?iso-8859-1?q?TZRoXZQI84RUY2zXuLTZCadglpoACdFDnL6eT9wg3VoSn+t6AVIZ1Ob5FIib?= =?iso-8859-1?q?c+Xs5mqAjTfdVrtjxc1Iyj+BTBu8iPUVaht27t9nUeCR8HUCSeISaIamU+pl?= =?iso-8859-1?q?ETt0eC8zsSBV+7tZGAEe22OrSVl0xTvqowgBq1alyAJl3ON2AS5IEkOFXksI?= =?iso-8859-1?q?mgZJhkNAJQBJ6uU5znRe3h0lxl6nXia6y+cz0qO/GTEpWnBM1BxJ3Acbeq8V?= =?iso-8859-1?q?UD9epLF7yS54MrULz0uiPCBFjJ452HjTxr0MnxMPRzWpxmoqxRja5hmTE4QP?= =?iso-8859-1?q?y4tiOWYq2jiV1e+tiWIgnFOAPhk8fpdY0D5OK85/8x3xbiqq44CT+VRgeZxc?= =?iso-8859-1?q?9R2zvGErrtZGK0fb58hW6zpwBjQk3wrM6hwuXqpIdaxZmuUTISkIZqVFqO3t?= =?iso-8859-1?q?FMkpuG1xcO3o46q98O+VocNcO3F42KWlX56DgAEp/72ouXcJlNlesg/I3e9O?= =?iso-8859-1?q?chaJINVcHDEaZvxe24SzIQNMu4eKt05GNGQfZaAgV+06hD9GZKQnG/6y2btA?= =?iso-8859-1?q?2mCjITOgOzKxtfHzbgKHHuq+BYCVB91990cuNhjt/a3rqRAKTr8NnABemXWS?= =?iso-8859-1?q?kdzuiKtDqsEL35J/fzzCio8EQY6PKq1rpVXdRob849YhkyJp0ZrckkD87BQA?= =?iso-8859-1?q?Tr+I5PuiUb3hmnYZjW/uvu?= x-microsoft-antispam-prvs: x-forefront-prvs: 0968D37274 x-forefront-antispam-report: SFV:NSPM;SFS:(10009020)(346002)(136003)(376002)(366004)(39860400002)(396003)(199004)(189003)(97736004)(6116002)(386003)(8936002)(6506007)(106356001)(52116002)(1730700003)(8676002)(26005)(81156014)(81166006)(14444005)(256004)(105586002)(102836004)(30864003)(50226002)(2501003)(76176011)(99286004)(6512007)(305945005)(71200400001)(6436002)(5640700003)(6486002)(4326008)(53936002)(71190400001)(476003)(25786009)(486006)(66066001)(2616005)(7736002)(186003)(68736007)(446003)(1076003)(11346002)(54906003)(478600001)(316002)(36756003)(6916009)(2351001)(14454004)(86362001)(2906002)(5660300002)(3846002);DIR:OUT;SFP:1101;SCL:1;SRVR:AM6PR04MB4373;H:AM6PR04MB5016.eurprd04.prod.outlook.com;FPR:;SPF:None;LANG:en;PTR:InfoNoRecords;A:1;MX:1; received-spf: None (protection.outlook.com: nxp.com does not designate permitted sender hosts) x-ms-exchange-senderadcheck: 1 x-microsoft-antispam-message-info: ZNxMxpSHcrjuuE6iXhcnfu6Xlc+YAjwY20UuP699UTTHovPkxEwkPLrnoPdIhjYaPvEnyzmJoHRmUieczyLQRoU6SiLXQndxBmAaCb0RkaKW0S8Vq66XDmV3G1FYb55vQfJWTMAGkhixTH1jPWV6ODUdnFQXtKqXNB6YPzA8bMTW2Esb/Mj38kmPe/CsSymCGRegqyjNBzLokwquqHqWO6Xtah3UK55etBkVS84uqaZXG9KOJuxU8qG8zCUNIsvGHNG9vHIiXt9TXMFokd9mNtKueY0dChuZZ5o092fE8ztImUMtYTFl7YRCiSnArtTvnNYeSDcYDYe7+tA7IgI5NIZup4Im9linIK8I9BAOO+OH4uBV+lmm6V51a1Ah6B5Fatcy3nwPbVI+yNqV/z3f1s7cg6Bqdgfgkdmx68U5HQ8= MIME-Version: 1.0 X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: f4e305fb-8209-4fcc-634b-08d6a1fd4354 X-MS-Exchange-CrossTenant-originalarrivaltime: 06 Mar 2019 06:30:45.1768 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM6PR04MB4373 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add dma mode support for LPSPI. Any frame longer than half txfifosize will be sent by dma mode. For now, there are some limits: 1. The maximum transfer speed in master mode depends on the slave device, at least 40MHz(tested by spi-nor on 8qm-lpddr4-arm2 base board); 2. The maximum transfer speed in slave mode is 15MHz(imx7ulp), 22MHz(8qm/qxp). In order to reach the maximum speed which is mentioned in datasheet, the load of connect wires between master and slave should be less than 15pF. Signed-off-by: Clark Wang Acked-by: Fugang Duan --- V2: - replace a if judgement by switch statement - seperate a Debug print fix into next patch --- drivers/spi/spi-fsl-lpspi.c | 312 ++++++++++++++++++++++++++++++++++-- 1 file changed, 301 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index a25e0e03f058..9ff32fb67a29 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +34,9 @@ #define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */ +/* The maximum bytes that edma can transfer once.*/ +#define FSL_LPSPI_MAX_EDMA_BYTES ((1 << 15) - 1) + /* i.MX7ULP LPSPI registers */ #define IMX7ULP_VERID 0x0 #define IMX7ULP_PARAM 0x4 @@ -64,6 +70,8 @@ #define IER_FCIE BIT(9) #define IER_RDIE BIT(1) #define IER_TDIE BIT(0) +#define DER_RDDE BIT(1) +#define DER_TDDE BIT(0) #define CFGR1_PCSCFG BIT(27) #define CFGR1_PINCFG (BIT(24)|BIT(25)) #define CFGR1_PCSPOL BIT(8) @@ -91,6 +99,7 @@ struct lpspi_config { struct fsl_lpspi_data { struct device *dev; void __iomem *base; + unsigned long base_phys; struct clk *clk_ipg; struct clk *clk_per; bool is_slave; @@ -111,6 +120,11 @@ struct fsl_lpspi_data { bool slave_aborted; + /* DMA */ + bool usedma; + struct completion dma_rx_completion; + struct completion dma_tx_completion; + int chipselect[0]; }; @@ -158,6 +172,35 @@ static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi, writel(enable, fsl_lpspi->base + IMX7ULP_IER); } +static int fsl_lpspi_bytes_per_word(const int bpw) +{ + return DIV_ROUND_UP(bpw, BITS_PER_BYTE); +} + +static bool fsl_lpspi_can_dma(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + unsigned int bytes_per_word; + + if (!controller->dma_rx) + return false; + + bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word); + + switch (bytes_per_word) + { + case 1: + case 2: + case 4: + break; + default: + return false; + } + + return true; +} + static int lpspi_prepare_xfer_hardware(struct spi_controller *controller) { struct fsl_lpspi_data *fsl_lpspi = @@ -245,11 +288,13 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi) * For the first transfer, clear TCR_CONTC to assert SS. * For subsequent transfer, set TCR_CONTC to keep SS asserted. */ - temp |= TCR_CONT; - if (fsl_lpspi->is_first_byte) - temp &= ~TCR_CONTC; - else - temp |= TCR_CONTC; + if (!fsl_lpspi->usedma) { + temp |= TCR_CONT; + if (fsl_lpspi->is_first_byte) + temp &= ~TCR_CONTC; + else + temp |= TCR_CONTC; + } } writel(temp, fsl_lpspi->base + IMX7ULP_TCR); @@ -260,7 +305,11 @@ static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; - temp = fsl_lpspi->watermark >> 1 | (fsl_lpspi->watermark >> 1) << 16; + if (!fsl_lpspi->usedma) + temp = fsl_lpspi->watermark >> 1 | + (fsl_lpspi->watermark >> 1) << 16; + else + temp = fsl_lpspi->watermark >> 1; writel(temp, fsl_lpspi->base + IMX7ULP_FCR); @@ -302,6 +351,53 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) return 0; } +static int fsl_lpspi_dma_configure(struct spi_controller *controller) +{ + int ret; + enum dma_slave_buswidth buswidth; + struct dma_slave_config rx = {}, tx = {}; + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); + + switch (fsl_lpspi_bytes_per_word(fsl_lpspi->config.bpw)) { + case 4: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + case 2: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 1: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + default: + return -EINVAL; + } + + tx.direction = DMA_MEM_TO_DEV; + tx.dst_addr = fsl_lpspi->base_phys + IMX7ULP_TDR; + tx.dst_addr_width = buswidth; + tx.dst_maxburst = 1; + ret = dmaengine_slave_config(controller->dma_tx, &tx); + if (ret) { + dev_err(fsl_lpspi->dev, "TX dma configuration failed with %d\n", + ret); + return ret; + } + + rx.direction = DMA_DEV_TO_MEM; + rx.src_addr = fsl_lpspi->base_phys + IMX7ULP_RDR; + rx.src_addr_width = buswidth; + rx.src_maxburst = 1; + ret = dmaengine_slave_config(controller->dma_rx, &rx); + if (ret) { + dev_err(fsl_lpspi->dev, "RX dma configuration failed with %d\n", + ret); + return ret; + } + + return 0; +} + static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; @@ -327,10 +423,16 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) temp |= CR_RRF | CR_RTF | CR_MEN; writel(temp, fsl_lpspi->base + IMX7ULP_CR); + temp = 0; + if (fsl_lpspi->usedma) + temp = DER_TDDE | DER_RDDE; + writel(temp, fsl_lpspi->base + IMX7ULP_DER); + return 0; } -static int fsl_lpspi_setup_transfer(struct spi_device *spi, +static int fsl_lpspi_setup_transfer(struct spi_controller *controller, + struct spi_device *spi, struct spi_transfer *t) { struct fsl_lpspi_data *fsl_lpspi = @@ -363,6 +465,11 @@ static int fsl_lpspi_setup_transfer(struct spi_device *spi, else fsl_lpspi->watermark = fsl_lpspi->txfifosize; + if (fsl_lpspi_can_dma(controller, spi, t)) + fsl_lpspi->usedma = 1; + else + fsl_lpspi->usedma = 0; + return fsl_lpspi_config(fsl_lpspi); } @@ -401,8 +508,10 @@ static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; - /* Disable all interrupt */ - fsl_lpspi_intctrl(fsl_lpspi, 0); + if (!fsl_lpspi->usedma) { + /* Disable all interrupt */ + fsl_lpspi_intctrl(fsl_lpspi, 0); + } /* W1C for all flags in SR */ temp = 0x3F << 8; @@ -415,6 +524,176 @@ static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi) return 0; } +static void fsl_lpspi_dma_rx_callback(void *cookie) +{ + struct fsl_lpspi_data *fsl_lpspi = (struct fsl_lpspi_data *)cookie; + + complete(&fsl_lpspi->dma_rx_completion); +} + +static void fsl_lpspi_dma_tx_callback(void *cookie) +{ + struct fsl_lpspi_data *fsl_lpspi = (struct fsl_lpspi_data *)cookie; + + complete(&fsl_lpspi->dma_tx_completion); +} + +static int fsl_lpspi_calculate_timeout(struct fsl_lpspi_data *fsl_lpspi, + int size) +{ + unsigned long timeout = 0; + + /* Time with actual data transfer and CS change delay related to HW */ + timeout = (8 + 4) * size / fsl_lpspi->config.speed_hz; + + /* Add extra second for scheduler related activities */ + timeout += 1; + + /* Double calculated timeout */ + return msecs_to_jiffies(2 * timeout * MSEC_PER_SEC); +} + +static int fsl_lpspi_dma_transfer(struct spi_controller *controller, + struct fsl_lpspi_data *fsl_lpspi, + struct spi_transfer *transfer) +{ + struct dma_async_tx_descriptor *desc_tx, *desc_rx; + unsigned long transfer_timeout; + unsigned long timeout; + struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg; + int ret; + + ret = fsl_lpspi_dma_configure(controller); + if (ret) + return ret; + + desc_rx = dmaengine_prep_slave_sg(controller->dma_rx, + rx->sgl, rx->nents, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) + return -EINVAL; + + desc_rx->callback = fsl_lpspi_dma_rx_callback; + desc_rx->callback_param = (void *)fsl_lpspi; + dmaengine_submit(desc_rx); + reinit_completion(&fsl_lpspi->dma_rx_completion); + dma_async_issue_pending(controller->dma_rx); + + desc_tx = dmaengine_prep_slave_sg(controller->dma_tx, + tx->sgl, tx->nents, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) { + dmaengine_terminate_all(controller->dma_tx); + return -EINVAL; + } + + desc_tx->callback = fsl_lpspi_dma_tx_callback; + desc_tx->callback_param = (void *)fsl_lpspi; + dmaengine_submit(desc_tx); + reinit_completion(&fsl_lpspi->dma_tx_completion); + dma_async_issue_pending(controller->dma_tx); + + fsl_lpspi->slave_aborted = false; + + if (!fsl_lpspi->is_slave) { + transfer_timeout = fsl_lpspi_calculate_timeout(fsl_lpspi, + transfer->len); + + /* Wait eDMA to finish the data transfer.*/ + timeout = wait_for_completion_timeout(&fsl_lpspi->dma_tx_completion, + transfer_timeout); + if (!timeout) { + dev_err(fsl_lpspi->dev, "I/O Error in DMA TX\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -ETIMEDOUT; + } + + timeout = wait_for_completion_timeout(&fsl_lpspi->dma_rx_completion, + transfer_timeout); + if (!timeout) { + dev_err(fsl_lpspi->dev, "I/O Error in DMA RX\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -ETIMEDOUT; + } + } else { + if (wait_for_completion_interruptible(&fsl_lpspi->dma_tx_completion) || + fsl_lpspi->slave_aborted) { + dev_dbg(fsl_lpspi->dev, + "I/O Error in DMA TX interrupted\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -EINTR; + } + + if (wait_for_completion_interruptible(&fsl_lpspi->dma_rx_completion) || + fsl_lpspi->slave_aborted) { + dev_dbg(fsl_lpspi->dev, + "I/O Error in DMA RX interrupted\n"); + dmaengine_terminate_all(controller->dma_tx); + dmaengine_terminate_all(controller->dma_rx); + fsl_lpspi_reset(fsl_lpspi); + return -EINTR; + } + } + + fsl_lpspi_reset(fsl_lpspi); + + return 0; +} + +static void fsl_lpspi_dma_exit(struct spi_controller *controller) +{ + if (controller->dma_rx) { + dma_release_channel(controller->dma_rx); + controller->dma_rx = NULL; + } + + if (controller->dma_tx) { + dma_release_channel(controller->dma_tx); + controller->dma_tx = NULL; + } +} + +static int fsl_lpspi_dma_init(struct device *dev, + struct fsl_lpspi_data *fsl_lpspi, + struct spi_controller *controller) +{ + int ret; + + /* Prepare for TX DMA: */ + controller->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(controller->dma_tx)) { + ret = PTR_ERR(controller->dma_tx); + dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret); + controller->dma_tx = NULL; + goto err; + } + + /* Prepare for RX DMA: */ + controller->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(controller->dma_rx)) { + ret = PTR_ERR(controller->dma_rx); + dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret); + controller->dma_rx = NULL; + goto err; + } + + init_completion(&fsl_lpspi->dma_rx_completion); + init_completion(&fsl_lpspi->dma_tx_completion); + controller->can_dma = fsl_lpspi_can_dma; + controller->max_dma_len = FSL_LPSPI_MAX_EDMA_BYTES; + + return 0; +err: + fsl_lpspi_dma_exit(controller); + return ret; +} + static int fsl_lpspi_pio_transfer(struct spi_controller *controller, struct spi_transfer *t) { @@ -449,14 +728,17 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller, int ret; fsl_lpspi->is_first_byte = true; - ret = fsl_lpspi_setup_transfer(spi, t); + ret = fsl_lpspi_setup_transfer(controller, spi, t); if (ret < 0) return ret; fsl_lpspi_set_cmd(fsl_lpspi); fsl_lpspi->is_first_byte = false; - ret = fsl_lpspi_pio_transfer(controller, t); + if (fsl_lpspi->usedma) + ret = fsl_lpspi_dma_transfer(controller, fsl_lpspi, t); + else + ret = fsl_lpspi_pio_transfer(controller, t); if (ret < 0) return ret; @@ -606,6 +888,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = PTR_ERR(fsl_lpspi->base); goto out_controller_put; } + fsl_lpspi->base_phys = res->start; irq = platform_get_irq(pdev, 0); if (irq < 0) { @@ -647,6 +930,13 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi->txfifosize = 1 << (temp & 0x0f); fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f); + ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); + if (ret == -EPROBE_DEFER) + goto out_controller_put; + + if (ret < 0) + dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); + ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { dev_err(&pdev->dev, "spi_register_controller error.\n");