From patchwork Tue Nov 6 16:05:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670747 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 9CED71803 for ; Tue, 6 Nov 2018 16:05:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8D85829301 for ; Tue, 6 Nov 2018 16:05:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 817A8287EA; Tue, 6 Nov 2018 16:05:51 +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=-7.9 required=2.0 tests=BAYES_00,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 1858829301 for ; Tue, 6 Nov 2018 16:05:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388527AbeKGBbn (ORCPT ); Tue, 6 Nov 2018 20:31:43 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55077 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388927AbeKGBbn (ORCPT ); Tue, 6 Nov 2018 20:31:43 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id D4D3F2085D; Tue, 6 Nov 2018 17:05:48 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id 79059206A1; Tue, 6 Nov 2018 17:05:38 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 1/7] spi: spi-mem: Add missing word in the SPI_MEM_DATA_OUT description Date: Tue, 6 Nov 2018 17:05:30 +0100 Message-Id: <20181106160536.13415-2-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 Missing 'to' in the SPI_MEM_DATA_OUT description. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- Changes in v3: - Add Miquel's R-b Changes in v2: - New patch --- include/linux/spi/spi-mem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 69ee30456864..867839cc69a7 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -58,7 +58,7 @@ * enum spi_mem_data_dir - describes the direction of a SPI memory data * transfer from the controller perspective * @SPI_MEM_DATA_IN: data coming from the SPI memory - * @SPI_MEM_DATA_OUT: data sent the SPI memory + * @SPI_MEM_DATA_OUT: data sent to the SPI memory */ enum spi_mem_data_dir { SPI_MEM_DATA_IN, From patchwork Tue Nov 6 16:05:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670751 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 774F314E2 for ; Tue, 6 Nov 2018 16:05:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6689E287EA for ; Tue, 6 Nov 2018 16:05:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5A4342A4A4; Tue, 6 Nov 2018 16:05:52 +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=-7.9 required=2.0 tests=BAYES_00,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 E5B7429301 for ; Tue, 6 Nov 2018 16:05:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389030AbeKGBbo (ORCPT ); Tue, 6 Nov 2018 20:31:44 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55088 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388959AbeKGBbo (ORCPT ); Tue, 6 Nov 2018 20:31:44 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id 38097206A1; Tue, 6 Nov 2018 17:05:49 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id C7E3920726; Tue, 6 Nov 2018 17:05:38 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 2/7] spi: spi-mem: Add SPI_MEM_NO_DATA to the spi_mem_data_dir enum Date: Tue, 6 Nov 2018 17:05:31 +0100 Message-Id: <20181106160536.13415-3-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 When defining spi_mem_op templates we don't necessarily know the size that will be passed when the template is actually used, and basing the supports_op() check on op->data.nbytes to know whether there will be data transferred for a specific operation is this not possible. Add SPI_MEM_NO_DATA to the spi_mem_data_dir enum so that we can base our checks on op->data.dir instead of op->data.nbytes. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- Changes in v3: - Add Miquel's R-b Changes in v2: - New patch --- drivers/spi/spi-mem.c | 2 +- include/linux/spi/spi-mem.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 62a7b80801d2..967f581bca4f 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -142,7 +142,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem, spi_check_buswidth_req(mem, op->dummy.buswidth, true)) return false; - if (op->data.nbytes && + if (op->data.dir != SPI_MEM_NO_DATA && spi_check_buswidth_req(mem, op->data.buswidth, op->data.dir == SPI_MEM_DATA_OUT)) return false; diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 867839cc69a7..250b6f5c47c2 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -57,10 +57,12 @@ /** * enum spi_mem_data_dir - describes the direction of a SPI memory data * transfer from the controller perspective + * @SPI_MEM_NO_DATA: no data transferred * @SPI_MEM_DATA_IN: data coming from the SPI memory * @SPI_MEM_DATA_OUT: data sent to the SPI memory */ enum spi_mem_data_dir { + SPI_MEM_NO_DATA, SPI_MEM_DATA_IN, SPI_MEM_DATA_OUT, }; From patchwork Tue Nov 6 16:05:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670749 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 233E918FD for ; Tue, 6 Nov 2018 16:05:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1401E2A3F3 for ; Tue, 6 Nov 2018 16:05:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 082FE2A4A4; Tue, 6 Nov 2018 16:05:52 +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=-7.9 required=2.0 tests=BAYES_00,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 A22AD287EA for ; Tue, 6 Nov 2018 16:05:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389102AbeKGBbo (ORCPT ); Tue, 6 Nov 2018 20:31:44 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55102 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389030AbeKGBbo (ORCPT ); Tue, 6 Nov 2018 20:31:44 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id 9213820726; Tue, 6 Nov 2018 17:05:49 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id 2587420741; Tue, 6 Nov 2018 17:05:39 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 3/7] spi: spi-mem: Split spi_mem_exec_op() code Date: Tue, 6 Nov 2018 17:05:32 +0100 Message-Id: <20181106160536.13415-4-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 The logic surrounding the ->exec_op() call applies to direct mapping accessors. Move this code to separate functions to avoid duplicating code. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- Changes in v3: - Add Miquel's R-b Changes in v2: - New patch --- drivers/spi/spi-mem.c | 63 ++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 967f581bca4f..7916e655afc8 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -213,6 +213,44 @@ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) } EXPORT_SYMBOL_GPL(spi_mem_supports_op); +static int spi_mem_access_start(struct spi_mem *mem) +{ + struct spi_controller *ctlr = mem->spi->controller; + + /* + * Flush the message queue before executing our SPI memory + * operation to prevent preemption of regular SPI transfers. + */ + spi_flush_queue(ctlr); + + if (ctlr->auto_runtime_pm) { + int ret; + + ret = pm_runtime_get_sync(ctlr->dev.parent); + if (ret < 0) { + dev_err(&ctlr->dev, "Failed to power device: %d\n", + ret); + return ret; + } + } + + mutex_lock(&ctlr->bus_lock_mutex); + mutex_lock(&ctlr->io_mutex); + + return 0; +} + +static void spi_mem_access_end(struct spi_mem *mem) +{ + struct spi_controller *ctlr = mem->spi->controller; + + mutex_unlock(&ctlr->io_mutex); + mutex_unlock(&ctlr->bus_lock_mutex); + + if (ctlr->auto_runtime_pm) + pm_runtime_put(ctlr->dev.parent); +} + /** * spi_mem_exec_op() - Execute a memory operation * @mem: the SPI memory @@ -242,30 +280,13 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) return -ENOTSUPP; if (ctlr->mem_ops) { - /* - * Flush the message queue before executing our SPI memory - * operation to prevent preemption of regular SPI transfers. - */ - spi_flush_queue(ctlr); - - if (ctlr->auto_runtime_pm) { - ret = pm_runtime_get_sync(ctlr->dev.parent); - if (ret < 0) { - dev_err(&ctlr->dev, - "Failed to power device: %d\n", - ret); - return ret; - } - } + ret = spi_mem_access_start(mem); + if (ret) + return ret; - mutex_lock(&ctlr->bus_lock_mutex); - mutex_lock(&ctlr->io_mutex); ret = ctlr->mem_ops->exec_op(mem, op); - mutex_unlock(&ctlr->io_mutex); - mutex_unlock(&ctlr->bus_lock_mutex); - if (ctlr->auto_runtime_pm) - pm_runtime_put(ctlr->dev.parent); + spi_mem_access_end(mem); /* * Some controllers only optimize specific paths (typically the From patchwork Tue Nov 6 16:05:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670753 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 98C7414E2 for ; Tue, 6 Nov 2018 16:05:53 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 87076287EA for ; Tue, 6 Nov 2018 16:05:53 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7AB542A4A4; Tue, 6 Nov 2018 16:05:53 +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=-7.9 required=2.0 tests=BAYES_00,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 8DE25287EA for ; Tue, 6 Nov 2018 16:05:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388959AbeKGBbp (ORCPT ); Tue, 6 Nov 2018 20:31:45 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55114 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388927AbeKGBbp (ORCPT ); Tue, 6 Nov 2018 20:31:45 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id EBDA020741; Tue, 6 Nov 2018 17:05:49 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id 7799320790; Tue, 6 Nov 2018 17:05:39 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 4/7] spi: spi-mem: Add a new API to support direct mapping Date: Tue, 6 Nov 2018 17:05:33 +0100 Message-Id: <20181106160536.13415-5-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 Most modern SPI controllers can directly map a SPI memory (or a portion of the SPI memory) in the CPU address space. Most of the time this brings significant performance improvements as it automates the whole process of sending SPI memory operations every time a new region is accessed. This new API allows SPI memory drivers to create direct mappings and then use them to access the memory instead of using spi_mem_exec_op(). Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- Changes in v3: - Add Miquel's R-b Changes in v2: - Clearly state that spi_mem_dirmap_read/write() can return less than the requested len --- drivers/spi/spi-mem.c | 204 ++++++++++++++++++++++++++++++++++++ include/linux/spi/spi-mem.h | 80 ++++++++++++++ 2 files changed, 284 insertions(+) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 7916e655afc8..b12a7974b665 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -432,6 +432,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) } EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); +static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct spi_mem_op op = desc->info.op_tmpl; + int ret; + + op.addr.val = desc->info.offset + offs; + op.data.buf.in = buf; + op.data.nbytes = len; + ret = spi_mem_adjust_op_size(desc->mem, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(desc->mem, &op); + if (ret) + return ret; + + return op.data.nbytes; +} + +static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct spi_mem_op op = desc->info.op_tmpl; + int ret; + + op.addr.val = desc->info.offset + offs; + op.data.buf.out = buf; + op.data.nbytes = len; + ret = spi_mem_adjust_op_size(desc->mem, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(desc->mem, &op); + if (ret) + return ret; + + return op.data.nbytes; +} + +/** + * spi_mem_dirmap_create() - Create a direct mapping descriptor + * @mem: SPI mem device this direct mapping should be created for + * @info: direct mapping information + * + * This function is creating a direct mapping descriptor which can then be used + * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write(). + * If the SPI controller driver does not support direct mapping, this function + * fallback to an implementation using spi_mem_exec_op(), so that the caller + * doesn't have to bother implementing a fallback on his own. + * + * Return: a valid pointer in case of success, and ERR_PTR() otherwise. + */ +struct spi_mem_dirmap_desc * +spi_mem_dirmap_create(struct spi_mem *mem, + const struct spi_mem_dirmap_info *info) +{ + struct spi_controller *ctlr = mem->spi->controller; + struct spi_mem_dirmap_desc *desc; + int ret = -ENOTSUPP; + + /* Make sure the number of address cycles is between 1 and 8 bytes. */ + if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8) + return ERR_PTR(-EINVAL); + + /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */ + if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA) + return ERR_PTR(-EINVAL); + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + desc->mem = mem; + desc->info = *info; + if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) + ret = ctlr->mem_ops->dirmap_create(desc); + + if (ret) { + desc->nodirmap = true; + if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + ret = -ENOTSUPP; + else + ret = 0; + } + + if (ret) { + kfree(desc); + return ERR_PTR(ret); + } + + return desc; +} +EXPORT_SYMBOL_GPL(spi_mem_dirmap_create); + +/** + * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor + * @desc: the direct mapping descriptor to destroy + * @info: direct mapping information + * + * This function destroys a direct mapping descriptor previously created by + * spi_mem_dirmap_create(). + */ +void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc) +{ + struct spi_controller *ctlr = desc->mem->spi->controller; + + if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy) + ctlr->mem_ops->dirmap_destroy(desc); +} +EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy); + +/** + * spi_mem_dirmap_dirmap_read() - Read data through a direct mapping + * @desc: direct mapping descriptor + * @offs: offset to start reading from. Note that this is not an absolute + * offset, but the offset within the direct mapping which already has + * its own offset + * @len: length in bytes + * @buf: destination buffer. This buffer must be DMA-able + * + * This function reads data from a memory device using a direct mapping + * previously instantiated with spi_mem_dirmap_create(). + * + * Return: the amount of data read from the memory device or a negative error + * code. Note that the returned size might be smaller than @len, and the caller + * is responsible for calling spi_mem_dirmap_read() again when that happens. + */ +ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct spi_controller *ctlr = desc->mem->spi->controller; + ssize_t ret; + + if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + return -EINVAL; + + if (!len) + return 0; + + if (desc->nodirmap) { + ret = spi_mem_no_dirmap_read(desc, offs, len, buf); + } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) { + ret = spi_mem_access_start(desc->mem); + if (ret) + return ret; + + ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf); + + spi_mem_access_end(desc->mem); + } else { + ret = -ENOTSUPP; + } + + return ret; +} +EXPORT_SYMBOL_GPL(spi_mem_dirmap_read); + +/** + * spi_mem_dirmap_dirmap_write() - Write data through a direct mapping + * @desc: direct mapping descriptor + * @offs: offset to start writing from. Note that this is not an absolute + * offset, but the offset within the direct mapping which already has + * its own offset + * @len: length in bytes + * @buf: source buffer. This buffer must be DMA-able + * + * This function writes data to a memory device using a direct mapping + * previously instantiated with spi_mem_dirmap_create(). + * + * Return: the amount of data written to the memory device or a negative error + * code. Note that the returned size might be smaller than @len, and the caller + * is responsible for calling spi_mem_dirmap_write() again when that happens. + */ +ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct spi_controller *ctlr = desc->mem->spi->controller; + ssize_t ret; + + if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT) + return -EINVAL; + + if (!len) + return 0; + + if (desc->nodirmap) { + ret = spi_mem_no_dirmap_write(desc, offs, len, buf); + } else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) { + ret = spi_mem_access_start(desc->mem); + if (ret) + return ret; + + ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf); + + spi_mem_access_end(desc->mem); + } else { + ret = -ENOTSUPP; + } + + return ret; +} +EXPORT_SYMBOL_GPL(spi_mem_dirmap_write); + static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) { return container_of(drv, struct spi_mem_driver, spidrv.driver); diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 250b6f5c47c2..3fe24500c5ee 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -124,6 +124,49 @@ struct spi_mem_op { .data = __data, \ } +/** + * struct spi_mem_dirmap_info - Direct mapping information + * @op_tmpl: operation template that should be used by the direct mapping when + * the memory device is accessed + * @offset: absolute offset this direct mapping is pointing to + * @length: length in byte of this direct mapping + * + * These information are used by the controller specific implementation to know + * the portion of memory that is directly mapped and the spi_mem_op that should + * be used to access the device. + * A direct mapping is only valid for one direction (read or write) and this + * direction is directly encoded in the ->op_tmpl.data.dir field. + */ +struct spi_mem_dirmap_info { + struct spi_mem_op op_tmpl; + u64 offset; + u64 length; +}; + +/** + * struct spi_mem_dirmap_desc - Direct mapping descriptor + * @mem: the SPI memory device this direct mapping is attached to + * @info: information passed at direct mapping creation time + * @nodirmap: set to 1 if the SPI controller does not implement + * ->mem_ops->dirmap_create() or when this function returned an + * error. If @nodirmap is true, all spi_mem_dirmap_{read,write}() + * calls will use spi_mem_exec_op() to access the memory. This is a + * degraded mode that allows spi_mem drivers to use the same code + * no matter whether the controller supports direct mapping or not + * @priv: field pointing to controller specific data + * + * Common part of a direct mapping descriptor. This object is created by + * spi_mem_dirmap_create() and controller implementation of ->create_dirmap() + * can create/attach direct mapping resources to the descriptor in the ->priv + * field. + */ +struct spi_mem_dirmap_desc { + struct spi_mem *mem; + struct spi_mem_dirmap_info info; + unsigned int nodirmap; + void *priv; +}; + /** * struct spi_mem - describes a SPI memory device * @spi: the underlying SPI device @@ -179,10 +222,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) * Note that if the implementation of this function allocates memory * dynamically, then it should do so with devm_xxx(), as we don't * have a ->free_name() function. + * @dirmap_create: create a direct mapping descriptor that can later be used to + * access the memory device. This method is optional + * @dirmap_destroy: destroy a memory descriptor previous created by + * ->dirmap_create() + * @dirmap_read: read data from the memory device using the direct mapping + * created by ->dirmap_create(). The function can return less + * data than requested (for example when the request is crossing + * the currently mapped area), and the caller of + * spi_mem_dirmap_read() is responsible for calling it again in + * this case. + * @dirmap_write: write data to the memory device using the direct mapping + * created by ->dirmap_create(). The function can return less + * data than requested (for example when the request is crossing + * the currently mapped area), and the caller of + * spi_mem_dirmap_write() is responsible for calling it again in + * this case. * * This interface should be implemented by SPI controllers providing an * high-level interface to execute SPI memory operation, which is usually the * case for QSPI controllers. + * + * Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct + * mapping from the CPU because doing that can stall the CPU waiting for the + * SPI mem transaction to finish, and this will make real-time maintainers + * unhappy and might make your system less reactive. Instead, drivers should + * use DMA to access this direct mapping. */ struct spi_controller_mem_ops { int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op); @@ -191,6 +256,12 @@ struct spi_controller_mem_ops { int (*exec_op)(struct spi_mem *mem, const struct spi_mem_op *op); const char *(*get_name)(struct spi_mem *mem); + int (*dirmap_create)(struct spi_mem_dirmap_desc *desc); + void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc); + ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf); + ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf); }; /** @@ -251,6 +322,15 @@ int spi_mem_exec_op(struct spi_mem *mem, const char *spi_mem_get_name(struct spi_mem *mem); +struct spi_mem_dirmap_desc * +spi_mem_dirmap_create(struct spi_mem *mem, + const struct spi_mem_dirmap_info *info); +void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc); +ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf); +ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf); + int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, struct module *owner); From patchwork Tue Nov 6 16:05:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670755 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 6C6D81751 for ; Tue, 6 Nov 2018 16:06:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5C8E3287EA for ; Tue, 6 Nov 2018 16:06:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4F3362A4C0; Tue, 6 Nov 2018 16:06:02 +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=-7.9 required=2.0 tests=BAYES_00,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 1A727287EA for ; Tue, 6 Nov 2018 16:06:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389117AbeKGBbx (ORCPT ); Tue, 6 Nov 2018 20:31:53 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55124 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388927AbeKGBbx (ORCPT ); Tue, 6 Nov 2018 20:31:53 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id AB59E20889; Tue, 6 Nov 2018 17:05:58 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id C9D9320799; Tue, 6 Nov 2018 17:05:39 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 5/7] mtd: devices: m25p80: Use the spi-mem dirmap API Date: Tue, 6 Nov 2018 17:05:34 +0100 Message-Id: <20181106160536.13415-6-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 Make use of the spi-mem direct mapping API to let advanced controllers optimize read/write operations when they support direct mapping. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal Tested-by: Yogesh Narayan Gaur Tested-by: Yogesh Narayan Gaur Tested-by: Yogesh Narayan Gaur --- Changes in v3: - Make nor->read/write() functional before the direct mappings have been created - Add Miquel's R-b Changes in v2: - Rename the dirmap fields - Return directly after calling dirmap_read/write() and let the spi-nor framework call us again if those functions returned less than the requested length --- drivers/mtd/devices/m25p80.c | 102 +++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index c4a1d04b8c80..847188e8a99e 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -31,8 +31,70 @@ struct m25p { struct spi_mem *spimem; struct spi_nor spi_nor; + struct { + struct spi_mem_dirmap_desc *rdesc; + struct spi_mem_dirmap_desc *wdesc; + } dirmap; }; +static int m25p_create_write_dirmap(struct m25p *flash) +{ + struct spi_nor *nor = &flash->spi_nor; + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)), + .offset = 0, + .length = flash->spi_nor.mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + op->addr.nbytes = 0; + + flash->dirmap.wdesc = spi_mem_dirmap_create(flash->spimem, &info); + if (IS_ERR(flash->dirmap.wdesc)) + return PTR_ERR(flash->dirmap.wdesc); + + return 0; +} + +static int m25p_create_read_dirmap(struct m25p *flash) +{ + struct spi_nor *nor = &flash->spi_nor; + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_DUMMY(nor->read_dummy, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)), + .offset = 0, + .length = flash->spi_nor.mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; + + flash->dirmap.rdesc = spi_mem_dirmap_create(flash->spimem, &info); + if (IS_ERR(flash->dirmap.rdesc)) + return PTR_ERR(flash->dirmap.rdesc); + + return 0; +} + static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) { struct m25p *flash = nor->priv; @@ -92,6 +154,9 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len, SPI_MEM_OP_DATA_OUT(len, buf, 1)); int ret; + if (flash->dirmap.wdesc) + return spi_mem_dirmap_write(flash->dirmap.wdesc, to, len, buf); + /* get transfer protocols. */ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); @@ -128,6 +193,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, size_t remaining = len; int ret; + if (flash->dirmap.rdesc) + return spi_mem_dirmap_read(flash->dirmap.rdesc, from, len, buf); + /* get transfer protocols. */ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); @@ -231,19 +299,47 @@ static int m25p_probe(struct spi_mem *spimem) if (ret) return ret; - return mtd_device_register(&nor->mtd, data ? data->parts : NULL, - data ? data->nr_parts : 0); + ret = m25p_create_write_dirmap(flash); + if (ret) + return ret; + + ret = m25p_create_read_dirmap(flash); + if (ret) + goto err_destroy_write_dirmap; + + ret = mtd_device_register(&nor->mtd, data ? data->parts : NULL, + data ? data->nr_parts : 0); + if (ret) + goto err_destroy_read_dirmap; + + return 0; + +err_destroy_read_dirmap: + spi_mem_dirmap_destroy(flash->dirmap.rdesc); + +err_destroy_write_dirmap: + spi_mem_dirmap_destroy(flash->dirmap.wdesc); + + return ret; } static int m25p_remove(struct spi_mem *spimem) { struct m25p *flash = spi_mem_get_drvdata(spimem); + int ret; spi_nor_restore(&flash->spi_nor); /* Clean up MTD stuff. */ - return mtd_device_unregister(&flash->spi_nor.mtd); + ret = mtd_device_unregister(&flash->spi_nor.mtd); + if (ret) + return ret; + + spi_mem_dirmap_destroy(flash->dirmap.rdesc); + spi_mem_dirmap_destroy(flash->dirmap.wdesc); + + return 0; } static void m25p_shutdown(struct spi_mem *spimem) From patchwork Tue Nov 6 16:05:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670757 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 2A39B1751 for ; Tue, 6 Nov 2018 16:06:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 18ED329301 for ; Tue, 6 Nov 2018 16:06:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 094E82A4C0; Tue, 6 Nov 2018 16:06:03 +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=-7.9 required=2.0 tests=BAYES_00,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 385D229301 for ; Tue, 6 Nov 2018 16:06:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388927AbeKGBbz (ORCPT ); Tue, 6 Nov 2018 20:31:55 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55133 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389106AbeKGBby (ORCPT ); Tue, 6 Nov 2018 20:31:54 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id B442B20887; Tue, 6 Nov 2018 17:05:58 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id 2549D207A3; Tue, 6 Nov 2018 17:05:40 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 6/7] mtd: spinand: Use the spi-mem dirmap API Date: Tue, 6 Nov 2018 17:05:35 +0100 Message-Id: <20181106160536.13415-7-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 Make use of the spi-mem direct mapping API to let advanced controllers optimize read/write operations when they support direct mapping. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal --- Changes in v3: - Add Miquel's R-b Changes in v2: - New patch --- drivers/mtd/nand/spi/core.c | 231 ++++++++++++++++++++---------------- include/linux/mtd/spinand.h | 7 ++ 2 files changed, 138 insertions(+), 100 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 30f83649c481..0fe3fa4867a8 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -19,21 +19,6 @@ #include #include -static void spinand_cache_op_adjust_colum(struct spinand_device *spinand, - const struct nand_page_io_req *req, - u16 *column) -{ - struct nand_device *nand = spinand_to_nand(spinand); - unsigned int shift; - - if (nand->memorg.planes_per_lun < 2) - return; - - /* The plane number is passed in MSB just above the column address */ - shift = fls(nand->memorg.pagesize); - *column |= req->pos.plane << shift; -} - static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg, @@ -227,27 +212,21 @@ static int spinand_load_page_op(struct spinand_device *spinand, static int spinand_read_from_cache_op(struct spinand_device *spinand, const struct nand_page_io_req *req) { - struct spi_mem_op op = *spinand->op_templates.read_cache; struct nand_device *nand = spinand_to_nand(spinand); struct mtd_info *mtd = nanddev_to_mtd(nand); - struct nand_page_io_req adjreq = *req; + struct spi_mem_dirmap_desc *rdesc; unsigned int nbytes = 0; void *buf = NULL; u16 column = 0; - int ret; + ssize_t ret; if (req->datalen) { - adjreq.datalen = nanddev_page_size(nand); - adjreq.dataoffs = 0; - adjreq.databuf.in = spinand->databuf; buf = spinand->databuf; - nbytes = adjreq.datalen; + nbytes = nanddev_page_size(nand); + column = 0; } if (req->ooblen) { - adjreq.ooblen = nanddev_per_page_oobsize(nand); - adjreq.ooboffs = 0; - adjreq.oobbuf.in = spinand->oobbuf; nbytes += nanddev_per_page_oobsize(nand); if (!buf) { buf = spinand->oobbuf; @@ -255,28 +234,18 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, } } - spinand_cache_op_adjust_colum(spinand, &adjreq, &column); - op.addr.val = column; + rdesc = spinand->dirmaps[req->pos.plane].rdesc; - /* - * Some controllers are limited in term of max RX data size. In this - * case, just repeat the READ_CACHE operation after updating the - * column. - */ while (nbytes) { - op.data.buf.in = buf; - op.data.nbytes = nbytes; - ret = spi_mem_adjust_op_size(spinand->spimem, &op); - if (ret) - return ret; + ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf); + if (!ret || ret > nbytes) + ret = -EIO; - ret = spi_mem_exec_op(spinand->spimem, &op); - if (ret) + if (ret < 0) return ret; - buf += op.data.nbytes; - nbytes -= op.data.nbytes; - op.addr.val += op.data.nbytes; + nbytes -= ret; + column += ret; } if (req->datalen) @@ -300,28 +269,18 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, static int spinand_write_to_cache_op(struct spinand_device *spinand, const struct nand_page_io_req *req) { - struct spi_mem_op op = *spinand->op_templates.write_cache; struct nand_device *nand = spinand_to_nand(spinand); struct mtd_info *mtd = nanddev_to_mtd(nand); - struct nand_page_io_req adjreq = *req; - unsigned int nbytes = 0; - void *buf = NULL; - u16 column = 0; - int ret; + struct spi_mem_dirmap_desc *wdesc; + unsigned int nbytes, column = 0; + ssize_t ret; - memset(spinand->databuf, 0xff, - nanddev_page_size(nand) + - nanddev_per_page_oobsize(nand)); + nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); + memset(spinand->databuf, 0xff, nbytes); - if (req->datalen) { + if (req->datalen) memcpy(spinand->databuf + req->dataoffs, req->databuf.out, req->datalen); - adjreq.dataoffs = 0; - adjreq.datalen = nanddev_page_size(nand); - adjreq.databuf.out = spinand->databuf; - nbytes = adjreq.datalen; - buf = spinand->databuf; - } if (req->ooblen) { if (req->mode == MTD_OPS_AUTO_OOB) @@ -332,52 +291,21 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, else memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out, req->ooblen); - - adjreq.ooblen = nanddev_per_page_oobsize(nand); - adjreq.ooboffs = 0; - nbytes += nanddev_per_page_oobsize(nand); - if (!buf) { - buf = spinand->oobbuf; - column = nanddev_page_size(nand); - } } - spinand_cache_op_adjust_colum(spinand, &adjreq, &column); - - op = *spinand->op_templates.write_cache; - op.addr.val = column; + wdesc = spinand->dirmaps[req->pos.plane].wdesc; - /* - * Some controllers are limited in term of max TX data size. In this - * case, split the operation into one LOAD CACHE and one or more - * LOAD RANDOM CACHE. - */ while (nbytes) { - op.data.buf.out = buf; - op.data.nbytes = nbytes; + ret = spi_mem_dirmap_read(wdesc, column, nbytes, + spinand->databuf + column); + if (!ret || ret > nbytes) + ret = -EIO; - ret = spi_mem_adjust_op_size(spinand->spimem, &op); - if (ret) - return ret; - - ret = spi_mem_exec_op(spinand->spimem, &op); - if (ret) + if (ret < 0) return ret; - buf += op.data.nbytes; - nbytes -= op.data.nbytes; - op.addr.val += op.data.nbytes; - - /* - * We need to use the RANDOM LOAD CACHE operation if there's - * more than one iteration, because the LOAD operation resets - * the cache to 0xff. - */ - if (nbytes) { - column = op.addr.val; - op = *spinand->op_templates.update_cache; - op.addr.val = column; - } + nbytes -= ret; + column += ret; } return 0; @@ -757,6 +685,97 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) return ret; } +static int spinand_create_dirmap(struct spinand_device *spinand, + unsigned int plane) +{ + struct nand_device *nand = spinand_to_nand(spinand); + struct spi_mem_dirmap_info info = { + .length = nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand), + }; + struct spi_mem_dirmap_desc *desc; + + /* The plane number is passed in MSB just above the column address */ + info.offset = plane << fls(nand->memorg.pagesize); + + info.op_tmpl = *spinand->op_templates.update_cache; + desc = spi_mem_dirmap_create(spinand->spimem, &info); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + spinand->dirmaps[plane].wdesc = desc; + + info.op_tmpl = *spinand->op_templates.read_cache; + desc = spi_mem_dirmap_create(spinand->spimem, &info); + if (IS_ERR(desc)) { + spi_mem_dirmap_destroy(spinand->dirmaps[plane].wdesc); + spinand->dirmaps[plane].wdesc = NULL; + return PTR_ERR(desc); + } + + spinand->dirmaps[plane].rdesc = desc; + + return 0; +} + +static void spinand_destroy_dirmap(struct spinand_device *spinand, + unsigned int plane) +{ + spi_mem_dirmap_destroy(spinand->dirmaps[plane].rdesc); + spi_mem_dirmap_destroy(spinand->dirmaps[plane].wdesc); +} + +static int spinand_create_dirmaps(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int i, ret; + + spinand->dirmaps = devm_kzalloc(&spinand->spimem->spi->dev, + sizeof(*spinand->dirmaps) * + nand->memorg.planes_per_lun, + GFP_KERNEL); + if (!spinand->dirmaps) + return -ENOMEM; + + for (i = 0; i < nand->memorg.planes_per_lun; i++) { + ret = spinand_create_dirmap(spinand, i); + if (ret) + goto err_destroy_dirmaps; + } + + return 0; + +err_destroy_dirmaps: + for (i--; i >= 0; i--) + spinand_destroy_dirmap(spinand, i); + + return ret; +} + +static void spinand_destroy_dirmaps(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int i; + + for (i = 0; i < nand->memorg.planes_per_lun; i++) + spinand_destroy_dirmap(spinand, i); +} + +const struct spi_mem_op * +spinand_find_supported_op(struct spinand_device *spinand, + const struct spi_mem_op *ops, + unsigned int nops) +{ + unsigned int i; + + for (i = 0; i < nops; i++) { + if (spi_mem_supports_op(spinand->spimem, &ops[i])) + return &ops[i]; + } + + return NULL; +} + static const struct nand_ops spinand_ops = { .erase = spinand_erase, .markbad = spinand_markbad, @@ -1012,20 +1031,28 @@ static int spinand_init(struct spinand_device *spinand) goto err_free_bufs; } + ret = spinand_create_dirmaps(spinand); + if (ret) { + dev_err(dev, + "Failed to create direct mappings for read/write operations (err = %d)\n", + ret); + goto err_manuf_cleanup; + } + /* After power up, all blocks are locked, so unlock them here. */ for (i = 0; i < nand->memorg.ntargets; i++) { ret = spinand_select_target(spinand, i); if (ret) - goto err_free_bufs; + goto err_destroy_dirmaps; ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); if (ret) - goto err_free_bufs; + goto err_destroy_dirmaps; } ret = nanddev_init(nand, &spinand_ops, THIS_MODULE); if (ret) - goto err_manuf_cleanup; + goto err_destroy_dirmaps; /* * Right now, we don't support ECC, so let the whole oob @@ -1054,6 +1081,9 @@ static int spinand_init(struct spinand_device *spinand) err_cleanup_nanddev: nanddev_cleanup(nand); +err_destroy_dirmaps: + spinand_destroy_dirmaps(spinand); + err_manuf_cleanup: spinand_manufacturer_cleanup(spinand); @@ -1068,6 +1098,7 @@ static void spinand_cleanup(struct spinand_device *spinand) struct nand_device *nand = spinand_to_nand(spinand); nanddev_cleanup(nand); + spinand_destroy_dirmaps(spinand); spinand_manufacturer_cleanup(spinand); kfree(spinand->databuf); kfree(spinand->scratchbuf); diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 088ff96c3eb6..1f6a9e8b4a37 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -300,6 +300,11 @@ struct spinand_info { __VA_ARGS__ \ } +struct spinand_dirmap { + struct spi_mem_dirmap_desc *wdesc; + struct spi_mem_dirmap_desc *rdesc; +}; + /** * struct spinand_device - SPI NAND device instance * @base: NAND device instance @@ -339,6 +344,8 @@ struct spinand_device { const struct spi_mem_op *update_cache; } op_templates; + struct spinand_dirmap *dirmaps; + int (*select_target)(struct spinand_device *spinand, unsigned int target); unsigned int cur_target; From patchwork Tue Nov 6 16:05:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 10670759 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 6E23414E2 for ; Tue, 6 Nov 2018 16:06:03 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5D1C5287EA for ; Tue, 6 Nov 2018 16:06:03 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 482002A4A4; Tue, 6 Nov 2018 16:06:03 +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=-7.9 required=2.0 tests=BAYES_00,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 B8D02287EA for ; Tue, 6 Nov 2018 16:06:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389106AbeKGBbz (ORCPT ); Tue, 6 Nov 2018 20:31:55 -0500 Received: from mail.bootlin.com ([62.4.15.54]:55138 "EHLO mail.bootlin.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389112AbeKGBbz (ORCPT ); Tue, 6 Nov 2018 20:31:55 -0500 Received: by mail.bootlin.com (Postfix, from userid 110) id F1FC3207A3; Tue, 6 Nov 2018 17:05:58 +0100 (CET) Received: from localhost.localdomain (aaubervilliers-681-1-93-44.w90-88.abo.wanadoo.fr [90.88.34.44]) by mail.bootlin.com (Postfix) with ESMTPSA id 7B021207D0; Tue, 6 Nov 2018 17:05:40 +0100 (CET) From: Boris Brezillon To: David Woodhouse , Brian Norris , Boris Brezillon , Marek Vasut , Richard Weinberger , linux-mtd@lists.infradead.org, Mark Brown , linux-spi@vger.kernel.org Cc: Vignesh R , Cyrille Pitchen , Tudor Ambarus , Yogesh Narayan Gaur , Frieder Schrempf , Miquel Raynal , Piotr Bugalski Subject: [PATCH v3 7/7] spi: mxic: Add support for direct mapping Date: Tue, 6 Nov 2018 17:05:36 +0100 Message-Id: <20181106160536.13415-8-boris.brezillon@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181106160536.13415-1-boris.brezillon@bootlin.com> References: <20181106160536.13415-1-boris.brezillon@bootlin.com> 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 Implement the ->dirmap_create() and ->dirmap_read() hooks to provide a fast path for read accesses. Write dirmap is not supported yet. Signed-off-by: Boris Brezillon --- Changes in v3: - Relax the limitation on the linear iomem size Changes in v2: - New patch --- drivers/spi/spi-mxic.c | 137 +++++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index e41ae6ef0f8a..d32c65d3700a 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -172,6 +172,11 @@ struct mxic_spi { struct clk *send_dly_clk; void __iomem *regs; u32 cur_speed_hz; + struct { + void __iomem *map; + dma_addr_t dma; + size_t size; + } linear; }; static int mxic_spi_clk_enable(struct mxic_spi *mxic) @@ -280,6 +285,42 @@ static void mxic_spi_hw_init(struct mxic_spi *mxic) mxic->regs + HC_CFG); } +static u32 mxic_spi_mem_prep_op_cfg(const struct spi_mem_op *op) +{ + u32 cfg = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1); + + if (op->addr.nbytes) + cfg |= OP_ADDR_BYTES(op->addr.nbytes) | + OP_ADDR_BUSW(fls(op->addr.buswidth) - 1); + + if (op->dummy.nbytes) + cfg |= OP_DUMMY_CYC(op->dummy.nbytes); + + if (op->data.dir != SPI_MEM_NO_DATA) { + cfg |= OP_DATA_BUSW(fls(op->data.buswidth) - 1); + if (op->data.dir == SPI_MEM_DATA_IN) + cfg |= OP_READ; + } + + return cfg; +} + +static void mxic_spi_set_hc_cfg(struct spi_device *spi, u32 flags) +{ + struct mxic_spi *mxic = spi_master_get_devdata(spi->master); + int nio = 1; + + if (spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) + nio = 4; + else if (spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) + nio = 2; + + writel(flags | HC_CFG_NIO(nio) | + HC_CFG_TYPE(spi->chip_select, HC_CFG_TYPE_SPI_NOR) | + HC_CFG_SLV_ACT(spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1), + mxic->regs + HC_CFG); +} + static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, void *rxbuf, unsigned int len) { @@ -332,6 +373,41 @@ static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, return 0; } +static ssize_t mxic_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct mxic_spi *mxic = spi_master_get_devdata(desc->mem->spi->master); + int ret; + u32 sts; + + if (WARN_ON(offs + desc->info.offset + len > U32_MAX)) + return -EINVAL; + + mxic_spi_set_hc_cfg(desc->mem->spi, 0); + + writel(LMODE_CMD0(desc->info.op_tmpl.cmd.opcode) | + LMODE_SLV_ACT(desc->mem->spi->chip_select) | + LMODE_EN, + mxic->regs + LRD_CTRL); + writel(mxic_spi_mem_prep_op_cfg(&desc->info.op_tmpl), + mxic->regs + LRD_CFG); + writel(desc->info.offset + offs, mxic->regs + LRD_ADDR); + len = min_t(size_t, len, mxic->linear.size); + writel(len, mxic->regs + LRD_RANGE); + + memcpy_fromio(buf, mxic->linear.map, len); + + writel(INT_LRD_DIS, mxic->regs + INT_STS); + writel(0, mxic->regs + LRD_CTRL); + + ret = readl_poll_timeout(mxic->regs + INT_STS, sts, + sts & INT_LRD_DIS, 0, USEC_PER_SEC); + if (ret) + return ret; + + return len; +} + static bool mxic_spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { @@ -349,46 +425,42 @@ static bool mxic_spi_mem_supports_op(struct spi_mem *mem, return true; } +static int mxic_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct mxic_spi *mxic = spi_master_get_devdata(desc->mem->spi->master); + + if (!mxic->linear.map) + return -ENOTSUPP; + + if (desc->info.offset + desc->info.length > U32_MAX) + return -ENOTSUPP; + + if (!mxic_spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) + return -ENOTSUPP; + + if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) + return -ENOTSUPP; + + return 0; +} + static int mxic_spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master); - int nio = 1, i, ret; - u32 ss_ctrl; + int i, ret; u8 addr[8]; ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz); if (ret) return ret; - if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) - nio = 4; - else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) - nio = 2; + mxic_spi_set_hc_cfg(mem->spi, HC_CFG_MAN_CS_EN); - writel(HC_CFG_NIO(nio) | - HC_CFG_TYPE(mem->spi->chip_select, HC_CFG_TYPE_SPI_NOR) | - HC_CFG_SLV_ACT(mem->spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1) | - HC_CFG_MAN_CS_EN, - mxic->regs + HC_CFG); writel(HC_EN_BIT, mxic->regs + HC_EN); - ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1); - - if (op->addr.nbytes) - ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) | - OP_ADDR_BUSW(fls(op->addr.buswidth) - 1); - - if (op->dummy.nbytes) - ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes); - - if (op->data.nbytes) { - ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1); - if (op->data.dir == SPI_MEM_DATA_IN) - ss_ctrl |= OP_READ; - } - - writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select)); + writel(mxic_spi_mem_prep_op_cfg(op), + mxic->regs + SS_CTRL(mem->spi->chip_select)); writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, mxic->regs + HC_CFG); @@ -426,6 +498,8 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem, static const struct spi_controller_mem_ops mxic_spi_mem_ops = { .supports_op = mxic_spi_mem_supports_op, .exec_op = mxic_spi_mem_exec_op, + .dirmap_create = mxic_spi_mem_dirmap_create, + .dirmap_read = mxic_spi_mem_dirmap_read, }; static void mxic_spi_set_cs(struct spi_device *spi, bool lvl) @@ -557,6 +631,15 @@ static int mxic_spi_probe(struct platform_device *pdev) if (IS_ERR(mxic->regs)) return PTR_ERR(mxic->regs); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap"); + mxic->linear.map = devm_ioremap_resource(&pdev->dev, res); + if (!IS_ERR(mxic->linear.map)) { + mxic->linear.dma = res->start; + mxic->linear.size = resource_size(res); + } else { + mxic->linear.map = NULL; + } + pm_runtime_enable(&pdev->dev); master->auto_runtime_pm = true;