From patchwork Thu Aug 16 15:29:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958394 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr3M35dVz9s4Z for ; Fri, 17 Aug 2018 01:34:07 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 1EC5EC21D83; Thu, 16 Aug 2018 15:32:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 9BB6FC21E2F; Thu, 16 Aug 2018 15:30:48 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 25992C21C38; Thu, 16 Aug 2018 15:30:43 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id BCD2BC21D8A for ; Thu, 16 Aug 2018 15:30:42 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id A4596215D2; Thu, 16 Aug 2018 17:30:40 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 3EFCF206ED; Thu, 16 Aug 2018 17:30:30 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:29:59 +0200 Message-Id: <20180816153029.15521-2-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Boris Brezillon , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 01/31] mtd: Fallback to ->_read/write_oob() when ->_read/write() is missing X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Some MTD sublayers/drivers are implementing ->_read/write_oob() and provide dummy wrappers for their ->_read/write() implementations. Let the core handle this case instead of duplicating the logic. Signed-off-by: Boris Brezillon Acked-by: Robert Jarzmik Acked-by: Brian Norris Reviewed-by: Miquel Raynal Tested-by: Ladislav Michl Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- drivers/mtd/mtdcore.c | 31 +++++++++++++++-- drivers/mtd/mtdpart.c | 6 ++-- drivers/mtd/nand/nand_base.c | 56 ------------------------------ drivers/mtd/onenand/onenand_base.c | 2 -- 4 files changed, 33 insertions(+), 62 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6217be2352..60ad28efd4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -937,7 +937,20 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, * representing the maximum number of bitflips that were corrected on * any one ecc region (if applicable; zero otherwise). */ - ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (mtd->_read) { + ret_code = mtd->_read(mtd, from, len, retlen, buf); + } else if (mtd->_read_oob) { + struct mtd_oob_ops ops = { + .len = len, + .datbuf = buf, + }; + + ret_code = mtd->_read_oob(mtd, from, &ops); + *retlen = ops.retlen; + } else { + return -ENOTSUPP; + } + if (unlikely(ret_code < 0)) return ret_code; if (mtd->ecc_strength == 0) @@ -952,10 +965,24 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, *retlen = 0; if (to < 0 || to > mtd->size || len > mtd->size - to) return -EINVAL; - if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) + if ((!mtd->_write && !mtd->_write_oob) || + !(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (!len) return 0; + + if (!mtd->_write) { + struct mtd_oob_ops ops = { + .len = len, + .datbuf = (u8 *)buf, + }; + int ret; + + ret = mtd->_write_oob(mtd, to, &ops); + *retlen = ops.retlen; + return ret; + } + return mtd->_write(mtd, to, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_write); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f87c962205..ccbb1757ea 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -417,8 +417,10 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.dev.parent = master->dev.parent; #endif - slave->mtd._read = part_read; - slave->mtd._write = part_write; + if (master->_read) + slave->mtd._read = part_read; + if (master->_write) + slave->mtd._write = part_write; if (master->_panic_write) slave->mtd._panic_write = part_panic_write; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9094f857c1..92daebe120 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1863,33 +1863,6 @@ read_retry: return max_bitflips; } -/** - * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * - * Get hold of the chip and call nand_do_read. - */ -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) -{ - struct mtd_oob_ops ops; - int ret; - - nand_get_device(mtd, FL_READING); - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = buf; - ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_read_ops(mtd, from, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; -} - /** * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function * @mtd: mtd info structure @@ -2674,33 +2647,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } -/** - * nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * NAND write with ECC. - */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) -{ - struct mtd_oob_ops ops; - int ret; - - nand_get_device(mtd, FL_WRITING); - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = (uint8_t *)buf; - ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_write_ops(mtd, to, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; -} - /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure @@ -4620,8 +4566,6 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; mtd->_erase = nand_erase; - mtd->_read = nand_read; - mtd->_write = nand_write; mtd->_panic_write = panic_nand_write; mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 86b1640357..371e2ecaa7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2656,8 +2656,6 @@ int onenand_probe(struct mtd_info *mtd) mtd->flags = MTD_CAP_NANDFLASH; mtd->_erase = onenand_erase; - mtd->_read = onenand_read; - mtd->_write = onenand_write; mtd->_read_oob = onenand_read_oob; mtd->_write_oob = onenand_write_oob; mtd->_sync = onenand_sync; From patchwork Thu Aug 16 15:30:00 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958392 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr1d1pXHz9s4Z for ; Fri, 17 Aug 2018 01:32:37 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id D0E98C21DCA; Thu, 16 Aug 2018 15:32:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id D034BC21DFF; Thu, 16 Aug 2018 15:30:45 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id CEA6AC21C38; Thu, 16 Aug 2018 15:30:43 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id ECA3BC21C29 for ; Thu, 16 Aug 2018 15:30:41 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id F41F5206ED; Thu, 16 Aug 2018 17:30:40 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 8973720794; Thu, 16 Aug 2018 17:30:30 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:00 +0200 Message-Id: <20180816153029.15521-3-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Jacek Anaszewski , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 02/31] mtd: Uninline mtd_write_oob and move it to mtdcore.c X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Ezequiel Garcia There's no reason for having mtd_write_oob inlined in mtd.h header. Move it to mtdcore.c where it belongs. Signed-off-by: Ezequiel Garcia Acked-by: Boris Brezillon Signed-off-by: Jacek Anaszewski Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 12 ++++++++++++ include/linux/mtd/mtd.h | 12 +----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 60ad28efd4..ad61406dac 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1031,6 +1031,18 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) } EXPORT_SYMBOL_GPL(mtd_read_oob); +int mtd_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + ops->retlen = ops->oobretlen = 0; + if (!mtd->_write_oob) + return -EOPNOTSUPP; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + return mtd->_write_oob(mtd, to, ops); +} +EXPORT_SYMBOL_GPL(mtd_write_oob); + /** * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section * @mtd: MTD device structure diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 823e535b82..eb39f38887 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -351,17 +351,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); - -static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - return mtd->_write_oob(mtd, to, ops); -} +int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf); From patchwork Thu Aug 16 15:30:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958404 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr7x3nvkz9s4Z for ; Fri, 17 Aug 2018 01:38:05 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 33C69C21E16; Thu, 16 Aug 2018 15:33:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 6C940C21E44; Thu, 16 Aug 2018 15:30:49 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 94F16C21DA6; Thu, 16 Aug 2018 15:30:46 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 3BBF3C21CB6 for ; Thu, 16 Aug 2018 15:30:42 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 5879020794; Thu, 16 Aug 2018 17:30:41 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id DC37320828; Thu, 16 Aug 2018 17:30:30 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:01 +0200 Message-Id: <20180816153029.15521-4-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Boris Brezillon , Miquel Raynal , Allan Nielsen , u-boot@lists.denx.de, Richard Weinberger , Stefan Roese , Jagan Teki , Peter Pan Subject: [U-Boot] [PATCH v6 03/31] mtd: Add sanity checks in mtd_write/read_oob() X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Unlike what's done in mtd_read/write(), there are no checks to make sure the parameters passed to mtd_read/write_oob() are consistent, which forces implementers of ->_read/write_oob() to do it, which in turn leads to code duplication and possibly errors in the logic. Do general sanity checks, like ops fields consistency and range checking. Signed-off-by: Boris Brezillon Cc: Peter Pan Signed-off-by: Richard Weinberger [Miquel: squashed the fix about the chip's size check] Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ad61406dac..9a3efe95df 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1010,12 +1010,50 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, } EXPORT_SYMBOL_GPL(mtd_panic_write); +static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, + struct mtd_oob_ops *ops) +{ + /* + * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving + * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in + * this case. + */ + if (!ops->datbuf) + ops->len = 0; + + if (!ops->oobbuf) + ops->ooblen = 0; + + if (offs < 0 || offs + ops->len > mtd->size) + return -EINVAL; + + if (ops->ooblen) { + u64 maxooblen; + + if (ops->ooboffs >= mtd_oobavail(mtd, ops)) + return -EINVAL; + + maxooblen = ((mtd_div_by_ws(mtd->size, mtd) - + mtd_div_by_ws(offs, mtd)) * + mtd_oobavail(mtd, ops)) - ops->ooboffs; + if (ops->ooblen > maxooblen) + return -EINVAL; + } + + return 0; +} + int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int ret_code; ops->retlen = ops->oobretlen = 0; if (!mtd->_read_oob) return -EOPNOTSUPP; + + ret_code = mtd_check_oob_ops(mtd, from, ops); + if (ret_code) + return ret_code; + /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics * similar to mtd->_read(), returning a non-negative integer @@ -1034,11 +1072,18 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + int ret; + ops->retlen = ops->oobretlen = 0; if (!mtd->_write_oob) return -EOPNOTSUPP; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + + ret = mtd_check_oob_ops(mtd, to, ops); + if (ret) + return ret; + return mtd->_write_oob(mtd, to, ops); } EXPORT_SYMBOL_GPL(mtd_write_oob); From patchwork Thu Aug 16 15:30:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958389 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr0r1MBxz9s4Z for ; Fri, 17 Aug 2018 01:31:56 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id D7E2AC21F80; Thu, 16 Aug 2018 15:31:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 006D4C21DB6; Thu, 16 Aug 2018 15:30:44 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 4CBB9C21C38; Thu, 16 Aug 2018 15:30:43 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 72E73C21D83 for ; Thu, 16 Aug 2018 15:30:42 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 97AD820828; Thu, 16 Aug 2018 17:30:41 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 3D4592083D; Thu, 16 Aug 2018 17:30:31 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:02 +0200 Message-Id: <20180816153029.15521-5-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 04/31] mtd: Fallback to ->_read/write() when ->_read/write_oob() is missing X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Some MTD sublayers/drivers are implementing ->_read/write() and not ->_read/write_oob(). While for NAND devices both are usually valid, for NOR devices, using the _oob variant has no real meaning. But, as the MTD layer is supposed to hide as much as possible the flash complexity to the user, there is no reason to error out while it is just a matter of rewritting things internally. Add a fallback on mtd->_read() (resp. mtd->_write()) when the user calls mtd_read_oob() (resp. mtd_write_oob()) while mtd->_read_oob() (resp. mtd->_write_oob) is not implemented. There is already a fallback on the _oob variant if the former is used. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 9a3efe95df..fb1d68d5e2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1047,20 +1047,27 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int ret_code; ops->retlen = ops->oobretlen = 0; - if (!mtd->_read_oob) - return -EOPNOTSUPP; ret_code = mtd_check_oob_ops(mtd, from, ops); if (ret_code) return ret_code; + /* Check the validity of a potential fallback on mtd->_read */ + if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf)) + return -EOPNOTSUPP; + + if (mtd->_read_oob) + ret_code = mtd->_read_oob(mtd, from, ops); + else + ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen, + ops->datbuf); + /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics * similar to mtd->_read(), returning a non-negative integer * representing max bitflips. In other cases, mtd->_read_oob() may * return -EUCLEAN. In all cases, perform similar logic to mtd_read(). */ - ret_code = mtd->_read_oob(mtd, from, ops); if (unlikely(ret_code < 0)) return ret_code; if (mtd->ecc_strength == 0) @@ -1075,8 +1082,7 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, int ret; ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; @@ -1084,7 +1090,15 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, if (ret) return ret; - return mtd->_write_oob(mtd, to, ops); + /* Check the validity of a potential fallback on mtd->_write */ + if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf)) + return -EOPNOTSUPP; + + if (mtd->_write_oob) + return mtd->_write_oob(mtd, to, ops); + else + return mtd->_write(mtd, to, ops->len, &ops->retlen, + ops->datbuf); } EXPORT_SYMBOL_GPL(mtd_write_oob); From patchwork Thu Aug 16 15:30:03 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958396 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr4J0z3tz9s4Z for ; Fri, 17 Aug 2018 01:34:56 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id B9D69C21E1A; Thu, 16 Aug 2018 15:34:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 40E49C21E3B; Thu, 16 Aug 2018 15:31:05 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 4F3BDC21E56; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 57D6CC21E6C for ; Thu, 16 Aug 2018 15:30:51 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 7428620794; Thu, 16 Aug 2018 17:30:50 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 86FB120890; Thu, 16 Aug 2018 17:30:31 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:03 +0200 Message-Id: <20180816153029.15521-6-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Brian Norris , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 05/31] mtd: add get/set of_node/flash_node helpers X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Brian Norris We are going to begin using the mtd->dev.of_node field for MTD device nodes, so let's add helpers for it. Also, we'll be making some conversions on spi_nor (and nand_chip eventually) too, so get that ready with their own helpers. Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/linux/mtd/mtd.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index eb39f38887..73b19b57b1 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -306,6 +306,31 @@ struct mtd_info { int usecount; }; +#if IS_ENABLED(CONFIG_DM) +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ + mtd->dev->node.np = np; +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return mtd->dev->node.np; +} +#else +struct device_node; + +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return NULL; +} +#endif + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, From patchwork Thu Aug 16 15:30:04 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958399 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr5V2JvLz9s4Z for ; Fri, 17 Aug 2018 01:35:58 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 069B3C21E1A; Thu, 16 Aug 2018 15:33:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 47CA3C21EAE; Thu, 16 Aug 2018 15:31:03 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 5ACC9C21E7E; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 4FB20C21E6A for ; Thu, 16 Aug 2018 15:30:51 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 6CE79206ED; Thu, 16 Aug 2018 17:30:50 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id D6960208C2; Thu, 16 Aug 2018 17:30:31 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:04 +0200 Message-Id: <20180816153029.15521-7-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 06/31] mtd: fix build issue with includes X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Fix build errors produced by mtd.h and dm/device.h if not included in the right order. Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/linux/mtd/mtd.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 73b19b57b1..272c646f9d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -21,6 +21,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_DM) +#include +#endif #define MAX_MTD_DEVICES 32 #endif From patchwork Thu Aug 16 15:30:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958412 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrB92yrLz9s4Z for ; Fri, 17 Aug 2018 01:40:01 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 0BE71C21E34; Thu, 16 Aug 2018 15:34:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 17EBFC21DEC; Thu, 16 Aug 2018 15:31:08 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 2ED92C21E6C; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 9E921C21E76 for ; Thu, 16 Aug 2018 15:30:51 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id BC62B2083D; Thu, 16 Aug 2018 17:30:50 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 356B82090A; Thu, 16 Aug 2018 17:30:32 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:05 +0200 Message-Id: <20180816153029.15521-8-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 07/31] mtd: move definitions to enlarge their range X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Some helpers might be useful in a future 'mtd' U-Boot command to parse MTD device list. Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.h | 6 ------ include/linux/mtd/mtd.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index 7b0353399a..1d181a1045 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -5,7 +5,6 @@ extern struct mutex mtd_table_mutex; -struct mtd_info *__mtd_next_device(int i); int add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); @@ -16,8 +15,3 @@ int parse_mtd_partitions(struct mtd_info *master, const char * const *types, int __init init_mtdchar(void); void __exit cleanup_mtdchar(void); - -#define mtd_for_each_device(mtd) \ - for ((mtd) = __mtd_next_device(0); \ - (mtd) != NULL; \ - (mtd) = __mtd_next_device(mtd->index + 1)) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 272c646f9d..b8c2c3fd59 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -533,6 +533,12 @@ int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +struct mtd_info *__mtd_next_device(int i); +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) + int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, loff_t *maxsize, int devtype, uint64_t chipsize); int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, From patchwork Thu Aug 16 15:30:06 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958402 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr62663tz9s4Z for ; Fri, 17 Aug 2018 01:36:26 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 5BD11C21E31; Thu, 16 Aug 2018 15:33:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 1D960C21EBB; Thu, 16 Aug 2018 15:31:04 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 32071C21E68; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 9F725C21E77 for ; Thu, 16 Aug 2018 15:30:51 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id BDF6620828; Thu, 16 Aug 2018 17:30:50 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 7DF5020DBD; Thu, 16 Aug 2018 17:30:32 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:06 +0200 Message-Id: <20180816153029.15521-9-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 08/31] mtd: move all flash categories inside MTD submenu X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" There is no reason to have NAND, SPI flashes and UBI sections outside of the MTD submenu in Kconfig. Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- drivers/mtd/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 41f8883ec2..9341d518f3 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -59,10 +59,10 @@ config RENESAS_RPC_HF This enables access to Hyperflash memory through the Renesas RCar Gen3 RPC controller. -endmenu - source "drivers/mtd/nand/Kconfig" source "drivers/mtd/spi/Kconfig" source "drivers/mtd/ubi/Kconfig" + +endmenu From patchwork Thu Aug 16 15:30:07 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958438 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrRN3dckz9s3Z for ; Fri, 17 Aug 2018 01:51:28 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 31AD9C21E2B; Thu, 16 Aug 2018 15:40:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 7698FC21EEB; Thu, 16 Aug 2018 15:31:25 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 37079C21E70; Thu, 16 Aug 2018 15:31:09 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 18852C21E79 for ; Thu, 16 Aug 2018 15:30:52 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 35A25208C2; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id CA1AC20EB9; Thu, 16 Aug 2018 17:30:32 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:07 +0200 Message-Id: <20180816153029.15521-10-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> MIME-Version: 1.0 Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 09/31] mtd: move NAND files into a raw/ subdirectory X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" NAND flavors, like serial and parallel, have a lot in common and would benefit to share code. Let's move raw (parallel) NAND specific code in a raw/ subdirectory, to ease the addition of a core file in nand/ and the introduction of a spi/ subdirectory specific to SPI NANDs. Signed-off-by: Miquel Raynal --- MAINTAINERS | 6 +- Makefile | 2 +- README | 6 +- arch/arm/mach-uniphier/board_late_init.c | 2 +- common/spl/Kconfig | 2 +- common/spl/spl_spi.c | 2 +- doc/README.SPL | 4 +- doc/README.arm-relocation | 2 +- doc/README.nand | 6 +- doc/README.zynq | 2 +- drivers/Makefile | 2 +- drivers/mtd/Makefile | 2 + drivers/mtd/nand/Kconfig | 298 +----------------- drivers/mtd/nand/Makefile | 75 ----- drivers/mtd/nand/raw/Kconfig | 297 +++++++++++++++++ drivers/mtd/nand/raw/Makefile | 77 +++++ drivers/mtd/nand/{ => raw}/am335x_spl_bch.c | 0 drivers/mtd/nand/{ => raw}/arasan_nfc.c | 0 drivers/mtd/nand/{ => raw}/atmel_nand.c | 0 drivers/mtd/nand/{ => raw}/atmel_nand_ecc.h | 0 drivers/mtd/nand/{ => raw}/davinci_nand.c | 2 +- drivers/mtd/nand/{ => raw}/denali.c | 0 drivers/mtd/nand/{ => raw}/denali.h | 0 drivers/mtd/nand/{ => raw}/denali_dt.c | 0 drivers/mtd/nand/{ => raw}/denali_spl.c | 0 drivers/mtd/nand/{ => raw}/fsl_elbc_nand.c | 0 drivers/mtd/nand/{ => raw}/fsl_elbc_spl.c | 0 drivers/mtd/nand/{ => raw}/fsl_ifc_nand.c | 0 drivers/mtd/nand/{ => raw}/fsl_ifc_spl.c | 0 drivers/mtd/nand/{ => raw}/fsl_upm.c | 0 drivers/mtd/nand/{ => raw}/fsmc_nand.c | 0 drivers/mtd/nand/{ => raw}/kb9202_nand.c | 0 drivers/mtd/nand/{ => raw}/kirkwood_nand.c | 0 drivers/mtd/nand/{ => raw}/kmeter1_nand.c | 0 drivers/mtd/nand/{ => raw}/lpc32xx_nand_mlc.c | 0 drivers/mtd/nand/{ => raw}/lpc32xx_nand_slc.c | 0 drivers/mtd/nand/{ => raw}/mxc_nand.c | 0 drivers/mtd/nand/{ => raw}/mxc_nand.h | 0 drivers/mtd/nand/{ => raw}/mxc_nand_spl.c | 0 drivers/mtd/nand/{ => raw}/mxs_nand.c | 0 drivers/mtd/nand/{ => raw}/mxs_nand.h | 0 drivers/mtd/nand/{ => raw}/mxs_nand_dt.c | 0 drivers/mtd/nand/{ => raw}/mxs_nand_spl.c | 0 drivers/mtd/nand/{ => raw}/nand.c | 0 drivers/mtd/nand/{ => raw}/nand_base.c | 0 drivers/mtd/nand/{ => raw}/nand_bbt.c | 0 drivers/mtd/nand/{ => raw}/nand_bch.c | 0 drivers/mtd/nand/{ => raw}/nand_ecc.c | 2 +- drivers/mtd/nand/{ => raw}/nand_ids.c | 0 drivers/mtd/nand/{ => raw}/nand_plat.c | 0 drivers/mtd/nand/{ => raw}/nand_spl_load.c | 0 drivers/mtd/nand/{ => raw}/nand_spl_loaders.c | 0 drivers/mtd/nand/{ => raw}/nand_spl_simple.c | 0 drivers/mtd/nand/{ => raw}/nand_timings.c | 0 drivers/mtd/nand/{ => raw}/nand_util.c | 2 +- drivers/mtd/nand/{ => raw}/omap_elm.c | 0 drivers/mtd/nand/{ => raw}/omap_gpmc.c | 0 drivers/mtd/nand/{ => raw}/pxa3xx_nand.c | 2 +- drivers/mtd/nand/{ => raw}/pxa3xx_nand.h | 0 drivers/mtd/nand/{ => raw}/sunxi_nand.c | 0 drivers/mtd/nand/{ => raw}/sunxi_nand_spl.c | 0 drivers/mtd/nand/{ => raw}/tegra_nand.c | 0 drivers/mtd/nand/{ => raw}/tegra_nand.h | 0 drivers/mtd/nand/{ => raw}/vf610_nfc.c | 0 drivers/mtd/nand/{ => raw}/zynq_nand.c | 0 include/configs/MPC8313ERDB.h | 2 +- 66 files changed, 400 insertions(+), 395 deletions(-) create mode 100644 drivers/mtd/nand/raw/Kconfig create mode 100644 drivers/mtd/nand/raw/Makefile rename drivers/mtd/nand/{ => raw}/am335x_spl_bch.c (100%) rename drivers/mtd/nand/{ => raw}/arasan_nfc.c (100%) rename drivers/mtd/nand/{ => raw}/atmel_nand.c (100%) rename drivers/mtd/nand/{ => raw}/atmel_nand_ecc.h (100%) rename drivers/mtd/nand/{ => raw}/davinci_nand.c (99%) rename drivers/mtd/nand/{ => raw}/denali.c (100%) rename drivers/mtd/nand/{ => raw}/denali.h (100%) rename drivers/mtd/nand/{ => raw}/denali_dt.c (100%) rename drivers/mtd/nand/{ => raw}/denali_spl.c (100%) rename drivers/mtd/nand/{ => raw}/fsl_elbc_nand.c (100%) rename drivers/mtd/nand/{ => raw}/fsl_elbc_spl.c (100%) rename drivers/mtd/nand/{ => raw}/fsl_ifc_nand.c (100%) rename drivers/mtd/nand/{ => raw}/fsl_ifc_spl.c (100%) rename drivers/mtd/nand/{ => raw}/fsl_upm.c (100%) rename drivers/mtd/nand/{ => raw}/fsmc_nand.c (100%) rename drivers/mtd/nand/{ => raw}/kb9202_nand.c (100%) rename drivers/mtd/nand/{ => raw}/kirkwood_nand.c (100%) rename drivers/mtd/nand/{ => raw}/kmeter1_nand.c (100%) rename drivers/mtd/nand/{ => raw}/lpc32xx_nand_mlc.c (100%) rename drivers/mtd/nand/{ => raw}/lpc32xx_nand_slc.c (100%) rename drivers/mtd/nand/{ => raw}/mxc_nand.c (100%) rename drivers/mtd/nand/{ => raw}/mxc_nand.h (100%) rename drivers/mtd/nand/{ => raw}/mxc_nand_spl.c (100%) rename drivers/mtd/nand/{ => raw}/mxs_nand.c (100%) rename drivers/mtd/nand/{ => raw}/mxs_nand.h (100%) rename drivers/mtd/nand/{ => raw}/mxs_nand_dt.c (100%) rename drivers/mtd/nand/{ => raw}/mxs_nand_spl.c (100%) rename drivers/mtd/nand/{ => raw}/nand.c (100%) rename drivers/mtd/nand/{ => raw}/nand_base.c (100%) rename drivers/mtd/nand/{ => raw}/nand_bbt.c (100%) rename drivers/mtd/nand/{ => raw}/nand_bch.c (100%) rename drivers/mtd/nand/{ => raw}/nand_ecc.c (99%) rename drivers/mtd/nand/{ => raw}/nand_ids.c (100%) rename drivers/mtd/nand/{ => raw}/nand_plat.c (100%) rename drivers/mtd/nand/{ => raw}/nand_spl_load.c (100%) rename drivers/mtd/nand/{ => raw}/nand_spl_loaders.c (100%) rename drivers/mtd/nand/{ => raw}/nand_spl_simple.c (100%) rename drivers/mtd/nand/{ => raw}/nand_timings.c (100%) rename drivers/mtd/nand/{ => raw}/nand_util.c (99%) rename drivers/mtd/nand/{ => raw}/omap_elm.c (100%) rename drivers/mtd/nand/{ => raw}/omap_gpmc.c (100%) rename drivers/mtd/nand/{ => raw}/pxa3xx_nand.c (99%) rename drivers/mtd/nand/{ => raw}/pxa3xx_nand.h (100%) rename drivers/mtd/nand/{ => raw}/sunxi_nand.c (100%) rename drivers/mtd/nand/{ => raw}/sunxi_nand_spl.c (100%) rename drivers/mtd/nand/{ => raw}/tegra_nand.c (100%) rename drivers/mtd/nand/{ => raw}/tegra_nand.h (100%) rename drivers/mtd/nand/{ => raw}/vf610_nfc.c (100%) rename drivers/mtd/nand/{ => raw}/zynq_nand.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 8a2f0a730c..b8803ee754 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -282,7 +282,7 @@ F: drivers/i2c/i2c-cdns.c F: drivers/i2c/muxes/pca954x.c F: drivers/i2c/zynq_i2c.c F: drivers/mmc/zynq_sdhci.c -F: drivers/mtd/nand/zynq_nand.c +F: drivers/mtd/nand/raw/zynq_nand.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c F: drivers/serial/serial_zynq.c @@ -306,7 +306,7 @@ F: drivers/i2c/i2c-cdns.c F: drivers/i2c/muxes/pca954x.c F: drivers/i2c/zynq_i2c.c F: drivers/mmc/zynq_sdhci.c -F: drivers/mtd/nand/zynq_nand.c +F: drivers/mtd/nand/raw/zynq_nand.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c F: drivers/serial/serial_zynq.c @@ -443,7 +443,7 @@ NAND FLASH #M: Scott Wood S: Orphaned (Since 2018-07) T: git git://git.denx.de/u-boot-nand-flash.git -F: drivers/mtd/nand/ +F: drivers/mtd/nand/raw/ NDS32 M: Macpaul Lin diff --git a/Makefile b/Makefile index 884b7d943c..a38146003d 100644 --- a/Makefile +++ b/Makefile @@ -685,7 +685,7 @@ libs-y += drivers/dma/ libs-y += drivers/gpio/ libs-y += drivers/i2c/ libs-y += drivers/mtd/ -libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ +libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/raw/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ diff --git a/README b/README index a91af2a189..c8732cc33e 100644 --- a/README +++ b/README @@ -3264,8 +3264,8 @@ Low Level (hardware related) configuration options: a 16 bit bus. Not all NAND drivers use this symbol. Example of drivers that use it: - - drivers/mtd/nand/ndfc.c - - drivers/mtd/nand/mxc_nand.c + - drivers/mtd/nand/raw/ndfc.c + - drivers/mtd/nand/raw/mxc_nand.c - CONFIG_SYS_NDFC_EBC0_CFG Sets the EBC0_CFG register for the NDFC. If not defined @@ -3382,7 +3382,7 @@ Low Level (hardware related) configuration options: - CONFIG_SYS_NAND_NO_SUBPAGE_WRITE Option to disable subpage write in NAND driver driver that uses this: - drivers/mtd/nand/davinci_nand.c + drivers/mtd/nand/raw/davinci_nand.c Freescale QE/FMAN Firmware Support: ----------------------------------- diff --git a/arch/arm/mach-uniphier/board_late_init.c b/arch/arm/mach-uniphier/board_late_init.c index 8ffb9a8d3c..1b871c62ce 100644 --- a/arch/arm/mach-uniphier/board_late_init.c +++ b/arch/arm/mach-uniphier/board_late_init.c @@ -12,7 +12,7 @@ #include #include #include -#include <../drivers/mtd/nand/denali.h> +#include <../drivers/mtd/nand/raw/denali.h> #include "init.h" diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 0ad1e049a9..412c36025c 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -479,7 +479,7 @@ config SPL_NAND_SUPPORT help Enable support for NAND (Negative AND) flash in SPL. NAND flash can be used to allow SPL to load U-Boot from supported devices. - This enables the drivers in drivers/mtd/nand as part of an SPL + This enables the drivers in drivers/mtd/nand/raw as part of an SPL build. config SPL_NET_SUPPORT diff --git a/common/spl/spl_spi.c b/common/spl/spl_spi.c index ba60a3a3c5..0016d49739 100644 --- a/common/spl/spl_spi.c +++ b/common/spl/spl_spi.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2011 OMICRON electronics GmbH * - * based on drivers/mtd/nand/nand_spl_load.c + * based on drivers/mtd/nand/raw/nand_spl_load.c * * Copyright (C) 2011 * Heiko Schocher, DENX Software Engineering, hs@denx.de. diff --git a/doc/README.SPL b/doc/README.SPL index 3ba313caa8..fc1ca1ad4f 100644 --- a/doc/README.SPL +++ b/doc/README.SPL @@ -57,11 +57,11 @@ CONFIG_SPL_FAT_SUPPORT (fs/fat/libfat.o) CONFIG_SPL_EXT_SUPPORT CONFIG_SPL_LIBGENERIC_SUPPORT (lib/libgeneric.o) CONFIG_SPL_POWER_SUPPORT (drivers/power/libpower.o) -CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/libnand.o) +CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/raw/libnand.o) CONFIG_SPL_DRIVERS_MISC_SUPPORT (drivers/misc) CONFIG_SPL_DMA_SUPPORT (drivers/dma/libdma.o) CONFIG_SPL_POST_MEM_SUPPORT (post/drivers/memory.o) -CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/nand_spl_load.o) +CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/raw/nand_spl_load.o) CONFIG_SPL_SPI_LOAD (drivers/mtd/spi/spi_spl_load.o) CONFIG_SPL_RAM_DEVICE (common/spl/spl.c) CONFIG_SPL_WATCHDOG_SUPPORT (drivers/watchdog/libwatchdog.o) diff --git a/doc/README.arm-relocation b/doc/README.arm-relocation index 645b3746c8..d2a7e8122e 100644 --- a/doc/README.arm-relocation +++ b/doc/README.arm-relocation @@ -84,7 +84,7 @@ Relocation with SPL (example for the tx25 booting from NAND Flash): - cpu copies the first page from NAND to 0xbb000000 (IMX_NFC_BASE) and start with code execution on this address. -- The First page contains u-boot code from drivers/mtd/nand/mxc_nand_spl.c +- The First page contains u-boot code from drivers/mtd/nand/raw/mxc_nand_spl.c which inits the dram, cpu registers, reloacte itself to CONFIG_SPL_TEXT_BASE and loads the "real" u-boot to CONFIG_SYS_NAND_U_BOOT_DST and starts execution @CONFIG_SYS_NAND_U_BOOT_START diff --git a/doc/README.nand b/doc/README.nand index cda11b43ff..ec461b2dc9 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -116,7 +116,7 @@ Configuration Options: The maximum number of NAND chips per device to be supported. CONFIG_SYS_NAND_SELF_INIT - Traditionally, glue code in drivers/mtd/nand/nand.c has driven + Traditionally, glue code in drivers/mtd/nand/raw/nand.c has driven the initialization process -- it provides the mtd and nand structs, calls a board init function for a specific device, calls nand_scan(), and registers with mtd. @@ -125,7 +125,7 @@ Configuration Options: run code between nand_scan_ident() and nand_scan_tail(), or other deviations from the "normal" flow. - If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/nand.c + If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/raw/nand.c will make one call to board_nand_init(), with no arguments. That function is responsible for calling a driver init function for each NAND device on the board, that performs all initialization @@ -280,7 +280,7 @@ NOTE: ===== The Disk On Chip driver is currently broken and has been for some time. -There is a driver in drivers/mtd/nand, taken from Linux, that works with +There is a driver in drivers/mtd/nand/raw, taken from Linux, that works with the current NAND system but has not yet been adapted to the u-boot environment. diff --git a/doc/README.zynq b/doc/README.zynq index 5b0672c503..da977b2016 100644 --- a/doc/README.zynq +++ b/doc/README.zynq @@ -63,7 +63,7 @@ bootmode strings at runtime. spi - drivers/spi/zynq_spi.c qspi - drivers/spi/zynq_qspi.c i2c - drivers/i2c/zynq_i2c.c - nand - drivers/mtd/nand/zynq_nand.c + nand - drivers/mtd/nand/raw/zynq_nand.c - Done proper cleanups on board configurations - Added basic FDT support for zynq boards - d-cache support for zynq_gem.c diff --git a/drivers/Makefile b/drivers/Makefile index d53208540e..3d1a763b95 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ obj-$(CONFIG_$(SPL_TPL_)I2C_SUPPORT) += i2c/ obj-$(CONFIG_$(SPL_TPL_)LED) += led/ obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/ -obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/ +obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/raw/ obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/ obj-$(CONFIG_$(SPL_TPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_TPL_)RAM) += ram/ diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 9cec06510c..cdf515256a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -18,3 +18,5 @@ obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o + +obj-y += nand/ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1e4ea7bdd4..6d53734718 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,297 +1 @@ - -menuconfig NAND - bool "NAND Device Support" -if NAND - -config SYS_NAND_SELF_INIT - bool - help - This option, if enabled, provides more flexible and linux-like - NAND initialization process. - -config NAND_ATMEL - bool "Support Atmel NAND controller" - imply SYS_NAND_USE_FLASH_BBT - help - Enable this driver for NAND flash platforms using an Atmel NAND - controller. - -config NAND_DAVINCI - bool "Support TI Davinci NAND controller" - help - Enable this driver for NAND flash controllers available in TI Davinci - and Keystone2 platforms - -config NAND_DENALI - bool - select SYS_NAND_SELF_INIT - imply CMD_NAND - -config NAND_DENALI_DT - bool "Support Denali NAND controller as a DT device" - select NAND_DENALI - depends on OF_CONTROL && DM - help - Enable the driver for NAND flash on platforms using a Denali NAND - controller as a DT device. - -config NAND_DENALI_SPARE_AREA_SKIP_BYTES - int "Number of bytes skipped in OOB area" - depends on NAND_DENALI - range 0 63 - help - This option specifies the number of bytes to skip from the beginning - of OOB area before last ECC sector data starts. This is potentially - used to preserve the bad block marker in the OOB area. - -config NAND_LPC32XX_SLC - bool "Support LPC32XX_SLC controller" - help - Enable the LPC32XX SLC NAND controller. - -config NAND_OMAP_GPMC - bool "Support OMAP GPMC NAND controller" - depends on ARCH_OMAP2PLUS - help - Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms. - GPMC controller is used for parallel NAND flash devices, and can - do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8 - and BCH16 ECC algorithms. - -config NAND_OMAP_GPMC_PREFETCH - bool "Enable GPMC Prefetch" - depends on NAND_OMAP_GPMC - default y - help - On OMAP platforms that use the GPMC controller - (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that - uses the prefetch mode to speed up read operations. - -config NAND_OMAP_ELM - bool "Enable ELM driver for OMAPxx and AMxx platforms." - depends on NAND_OMAP_GPMC && !OMAP34XX - help - ELM controller is used for ECC error detection (not ECC calculation) - of BCH4, BCH8 and BCH16 ECC algorithms. - Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, - thus such SoC platforms need to depend on software library for ECC error - detection. However ECC calculation on such plaforms would still be - done by GPMC controller. - -config NAND_VF610_NFC - bool "Support for Freescale NFC for VF610" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - Enables support for NAND Flash Controller on some Freescale - processors like the VF610, MCF54418 or Kinetis K70. - The driver supports a maximum 2k page size. The driver - currently does not support hardware ECC. - -choice - prompt "Hardware ECC strength" - depends on NAND_VF610_NFC - default SYS_NAND_VF610_NFC_45_ECC_BYTES - help - Select the ECC strength used in the hardware BCH ECC block. - -config SYS_NAND_VF610_NFC_45_ECC_BYTES - bool "24-error correction (45 ECC bytes)" - -config SYS_NAND_VF610_NFC_60_ECC_BYTES - bool "32-error correction (60 ECC bytes)" - -endchoice - -config NAND_PXA3XX - bool "Support for NAND on PXA3xx and Armada 370/XP/38x" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables the driver for the NAND flash device found on - PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). - -config NAND_SUNXI - bool "Support for NAND on Allwinner SoCs" - default ARCH_SUNXI - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I - select SYS_NAND_SELF_INIT - select SYS_NAND_U_BOOT_LOCATIONS - select SPL_NAND_SUPPORT - imply CMD_NAND - ---help--- - Enable support for NAND. This option enables the standard and - SPL drivers. - The SPL driver only supports reading from the NAND using DMA - transfers. - -if NAND_SUNXI - -config NAND_SUNXI_SPL_ECC_STRENGTH - int "Allwinner NAND SPL ECC Strength" - default 64 - -config NAND_SUNXI_SPL_ECC_SIZE - int "Allwinner NAND SPL ECC Step Size" - default 1024 - -config NAND_SUNXI_SPL_USABLE_PAGE_SIZE - int "Allwinner NAND SPL Usable Page Size" - default 1024 - -endif - -config NAND_ARASAN - bool "Configure Arasan Nand" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables Nand driver support for Arasan nand flash - controller. This uses the hardware ECC for read and - write operations. - -config NAND_MXC - bool "MXC NAND support" - depends on CPU_ARM926EJS || CPU_ARM1136 || MX5 - imply CMD_NAND - help - This enables the NAND driver for the NAND flash controller on the - i.MX27 / i.MX31 / i.MX5 rocessors. - -config NAND_MXS - bool "MXS NAND support" - depends on MX23 || MX28 || MX6 || MX7 - select SYS_NAND_SELF_INIT - imply CMD_NAND - select APBH_DMA - select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 - select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 - help - This enables NAND driver for the NAND flash controller on the - MXS processors. - -if NAND_MXS - -config NAND_MXS_DT - bool "Support MXS NAND controller as a DT device" - depends on OF_CONTROL && MTD - help - Enable the driver for MXS NAND flash on platforms using - device tree. - -config NAND_MXS_USE_MINIMUM_ECC - bool "Use minimum ECC strength supported by the controller" - default false - -endif - -config NAND_ZYNQ - bool "Support for Zynq Nand controller" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables Nand driver support for Nand flash controller - found on Zynq SoC. - -config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS - bool "Enable use of 1st stage bootloader timing for NAND" - depends on NAND_ZYNQ - help - This flag prevent U-boot reconfigure NAND flash controller and reuse - the NAND timing from 1st stage bootloader. - -comment "Generic NAND options" - -config SYS_NAND_BLOCK_SIZE - hex "NAND chip eraseblock size" - depends on ARCH_SUNXI - help - Number of data bytes in one eraseblock for the NAND chip on the - board. This is the multiple of NAND_PAGE_SIZE and the number of - pages. - -config SYS_NAND_PAGE_SIZE - hex "NAND chip page size" - depends on ARCH_SUNXI - help - Number of data bytes in one page for the NAND chip on the - board, not including the OOB area. - -config SYS_NAND_OOBSIZE - hex "NAND chip OOB size" - depends on ARCH_SUNXI - help - Number of bytes in the Out-Of-Band area for the NAND chip on - the board. - -# Enhance depends when converting drivers to Kconfig which use this config -# option (mxc_nand, ndfc, omap_gpmc). -config SYS_NAND_BUSWIDTH_16BIT - bool "Use 16-bit NAND interface" - depends on NAND_VF610_NFC || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI - help - Indicates that NAND device has 16-bit wide data-bus. In absence of this - config, bus-width of NAND device is assumed to be either 8-bit and later - determined by reading ONFI params. - Above config is useful when NAND device's bus-width information cannot - be determined from on-chip ONFI params, like in following scenarios: - - SPL boot does not support reading of ONFI parameters. This is done to - keep SPL code foot-print small. - - In current U-Boot flow using nand_init(), driver initialization - happens in board_nand_init() which is called before any device probe - (nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are - not available while configuring controller. So a static CONFIG_NAND_xx - is needed to know the device's bus-width in advance. - -if SPL - -config SYS_NAND_U_BOOT_LOCATIONS - bool "Define U-boot binaries locations in NAND" - help - Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. - This option should not be enabled when compiling U-boot for boards - defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/.h - file. - -config SYS_NAND_U_BOOT_OFFS - hex "Location in NAND to read U-Boot from" - default 0x800000 if NAND_SUNXI - depends on SYS_NAND_U_BOOT_LOCATIONS - help - Set the offset from the start of the nand where u-boot should be - loaded from. - -config SYS_NAND_U_BOOT_OFFS_REDUND - hex "Location in NAND to read U-Boot from" - default SYS_NAND_U_BOOT_OFFS - depends on SYS_NAND_U_BOOT_LOCATIONS - help - Set the offset from the start of the nand where the redundant u-boot - should be loaded from. - -config SPL_NAND_AM33XX_BCH - bool "Enables SPL-NAND driver which supports ELM based" - depends on NAND_OMAP_GPMC && !OMAP34XX - default y - help - Hardware ECC correction. This is useful for platforms which have ELM - hardware engine and use NAND boot mode. - Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, - so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling - SPL-NAND driver with software ECC correction support. - -config SPL_NAND_DENALI - bool "Support Denali NAND controller for SPL" - help - This is a small implementation of the Denali NAND controller - for use on SPL. - -config SPL_NAND_SIMPLE - bool "Use simple SPL NAND driver" - depends on !SPL_NAND_AM33XX_BCH - help - Support for NAND boot using simple NAND drivers that - expose the cmd_ctrl() interface. -endif - -endif # if NAND +source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index c61e3f3839..69f40d1563 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,77 +1,2 @@ # SPDX-License-Identifier: GPL-2.0+ -# -# (C) Copyright 2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifdef CONFIG_SPL_BUILD - -ifdef CONFIG_SPL_NAND_DRIVERS -NORMAL_DRIVERS=y -endif - -obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o -obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o -obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o -obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o -obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o -obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o -obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o -obj-$(CONFIG_SPL_NAND_INIT) += nand.o -ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) -obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o -endif - -else # not spl - -NORMAL_DRIVERS=y - -obj-y += nand.o -obj-y += nand_bbt.o -obj-y += nand_ids.o -obj-y += nand_util.o -obj-y += nand_ecc.o -obj-y += nand_base.o -obj-y += nand_timings.o - -endif # not spl - -ifdef NORMAL_DRIVERS - -obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o - -obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o -obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o -obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o -obj-$(CONFIG_NAND_DENALI) += denali.o -obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o -obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o -obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o -obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o -obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o -obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o -obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o -obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o -obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o -obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o -obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o -obj-$(CONFIG_NAND_MXC) += mxc_nand.o -obj-$(CONFIG_NAND_MXS) += mxs_nand.o -obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o -obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o -obj-$(CONFIG_NAND_SPEAR) += spr_nand.o -obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o -obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o -obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o -obj-$(CONFIG_NAND_PLAT) += nand_plat.o -obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o -obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o - -else # minimal SPL drivers - -obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o -obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o -obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o -obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o -obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o - -endif # drivers diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig new file mode 100644 index 0000000000..1e4ea7bdd4 --- /dev/null +++ b/drivers/mtd/nand/raw/Kconfig @@ -0,0 +1,297 @@ + +menuconfig NAND + bool "NAND Device Support" +if NAND + +config SYS_NAND_SELF_INIT + bool + help + This option, if enabled, provides more flexible and linux-like + NAND initialization process. + +config NAND_ATMEL + bool "Support Atmel NAND controller" + imply SYS_NAND_USE_FLASH_BBT + help + Enable this driver for NAND flash platforms using an Atmel NAND + controller. + +config NAND_DAVINCI + bool "Support TI Davinci NAND controller" + help + Enable this driver for NAND flash controllers available in TI Davinci + and Keystone2 platforms + +config NAND_DENALI + bool + select SYS_NAND_SELF_INIT + imply CMD_NAND + +config NAND_DENALI_DT + bool "Support Denali NAND controller as a DT device" + select NAND_DENALI + depends on OF_CONTROL && DM + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + +config NAND_DENALI_SPARE_AREA_SKIP_BYTES + int "Number of bytes skipped in OOB area" + depends on NAND_DENALI + range 0 63 + help + This option specifies the number of bytes to skip from the beginning + of OOB area before last ECC sector data starts. This is potentially + used to preserve the bad block marker in the OOB area. + +config NAND_LPC32XX_SLC + bool "Support LPC32XX_SLC controller" + help + Enable the LPC32XX SLC NAND controller. + +config NAND_OMAP_GPMC + bool "Support OMAP GPMC NAND controller" + depends on ARCH_OMAP2PLUS + help + Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms. + GPMC controller is used for parallel NAND flash devices, and can + do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8 + and BCH16 ECC algorithms. + +config NAND_OMAP_GPMC_PREFETCH + bool "Enable GPMC Prefetch" + depends on NAND_OMAP_GPMC + default y + help + On OMAP platforms that use the GPMC controller + (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that + uses the prefetch mode to speed up read operations. + +config NAND_OMAP_ELM + bool "Enable ELM driver for OMAPxx and AMxx platforms." + depends on NAND_OMAP_GPMC && !OMAP34XX + help + ELM controller is used for ECC error detection (not ECC calculation) + of BCH4, BCH8 and BCH16 ECC algorithms. + Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, + thus such SoC platforms need to depend on software library for ECC error + detection. However ECC calculation on such plaforms would still be + done by GPMC controller. + +config NAND_VF610_NFC + bool "Support for Freescale NFC for VF610" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + Enables support for NAND Flash Controller on some Freescale + processors like the VF610, MCF54418 or Kinetis K70. + The driver supports a maximum 2k page size. The driver + currently does not support hardware ECC. + +choice + prompt "Hardware ECC strength" + depends on NAND_VF610_NFC + default SYS_NAND_VF610_NFC_45_ECC_BYTES + help + Select the ECC strength used in the hardware BCH ECC block. + +config SYS_NAND_VF610_NFC_45_ECC_BYTES + bool "24-error correction (45 ECC bytes)" + +config SYS_NAND_VF610_NFC_60_ECC_BYTES + bool "32-error correction (60 ECC bytes)" + +endchoice + +config NAND_PXA3XX + bool "Support for NAND on PXA3xx and Armada 370/XP/38x" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables the driver for the NAND flash device found on + PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). + +config NAND_SUNXI + bool "Support for NAND on Allwinner SoCs" + default ARCH_SUNXI + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I + select SYS_NAND_SELF_INIT + select SYS_NAND_U_BOOT_LOCATIONS + select SPL_NAND_SUPPORT + imply CMD_NAND + ---help--- + Enable support for NAND. This option enables the standard and + SPL drivers. + The SPL driver only supports reading from the NAND using DMA + transfers. + +if NAND_SUNXI + +config NAND_SUNXI_SPL_ECC_STRENGTH + int "Allwinner NAND SPL ECC Strength" + default 64 + +config NAND_SUNXI_SPL_ECC_SIZE + int "Allwinner NAND SPL ECC Step Size" + default 1024 + +config NAND_SUNXI_SPL_USABLE_PAGE_SIZE + int "Allwinner NAND SPL Usable Page Size" + default 1024 + +endif + +config NAND_ARASAN + bool "Configure Arasan Nand" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables Nand driver support for Arasan nand flash + controller. This uses the hardware ECC for read and + write operations. + +config NAND_MXC + bool "MXC NAND support" + depends on CPU_ARM926EJS || CPU_ARM1136 || MX5 + imply CMD_NAND + help + This enables the NAND driver for the NAND flash controller on the + i.MX27 / i.MX31 / i.MX5 rocessors. + +config NAND_MXS + bool "MXS NAND support" + depends on MX23 || MX28 || MX6 || MX7 + select SYS_NAND_SELF_INIT + imply CMD_NAND + select APBH_DMA + select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 + select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 + help + This enables NAND driver for the NAND flash controller on the + MXS processors. + +if NAND_MXS + +config NAND_MXS_DT + bool "Support MXS NAND controller as a DT device" + depends on OF_CONTROL && MTD + help + Enable the driver for MXS NAND flash on platforms using + device tree. + +config NAND_MXS_USE_MINIMUM_ECC + bool "Use minimum ECC strength supported by the controller" + default false + +endif + +config NAND_ZYNQ + bool "Support for Zynq Nand controller" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables Nand driver support for Nand flash controller + found on Zynq SoC. + +config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS + bool "Enable use of 1st stage bootloader timing for NAND" + depends on NAND_ZYNQ + help + This flag prevent U-boot reconfigure NAND flash controller and reuse + the NAND timing from 1st stage bootloader. + +comment "Generic NAND options" + +config SYS_NAND_BLOCK_SIZE + hex "NAND chip eraseblock size" + depends on ARCH_SUNXI + help + Number of data bytes in one eraseblock for the NAND chip on the + board. This is the multiple of NAND_PAGE_SIZE and the number of + pages. + +config SYS_NAND_PAGE_SIZE + hex "NAND chip page size" + depends on ARCH_SUNXI + help + Number of data bytes in one page for the NAND chip on the + board, not including the OOB area. + +config SYS_NAND_OOBSIZE + hex "NAND chip OOB size" + depends on ARCH_SUNXI + help + Number of bytes in the Out-Of-Band area for the NAND chip on + the board. + +# Enhance depends when converting drivers to Kconfig which use this config +# option (mxc_nand, ndfc, omap_gpmc). +config SYS_NAND_BUSWIDTH_16BIT + bool "Use 16-bit NAND interface" + depends on NAND_VF610_NFC || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI + help + Indicates that NAND device has 16-bit wide data-bus. In absence of this + config, bus-width of NAND device is assumed to be either 8-bit and later + determined by reading ONFI params. + Above config is useful when NAND device's bus-width information cannot + be determined from on-chip ONFI params, like in following scenarios: + - SPL boot does not support reading of ONFI parameters. This is done to + keep SPL code foot-print small. + - In current U-Boot flow using nand_init(), driver initialization + happens in board_nand_init() which is called before any device probe + (nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are + not available while configuring controller. So a static CONFIG_NAND_xx + is needed to know the device's bus-width in advance. + +if SPL + +config SYS_NAND_U_BOOT_LOCATIONS + bool "Define U-boot binaries locations in NAND" + help + Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. + This option should not be enabled when compiling U-boot for boards + defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/.h + file. + +config SYS_NAND_U_BOOT_OFFS + hex "Location in NAND to read U-Boot from" + default 0x800000 if NAND_SUNXI + depends on SYS_NAND_U_BOOT_LOCATIONS + help + Set the offset from the start of the nand where u-boot should be + loaded from. + +config SYS_NAND_U_BOOT_OFFS_REDUND + hex "Location in NAND to read U-Boot from" + default SYS_NAND_U_BOOT_OFFS + depends on SYS_NAND_U_BOOT_LOCATIONS + help + Set the offset from the start of the nand where the redundant u-boot + should be loaded from. + +config SPL_NAND_AM33XX_BCH + bool "Enables SPL-NAND driver which supports ELM based" + depends on NAND_OMAP_GPMC && !OMAP34XX + default y + help + Hardware ECC correction. This is useful for platforms which have ELM + hardware engine and use NAND boot mode. + Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, + so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling + SPL-NAND driver with software ECC correction support. + +config SPL_NAND_DENALI + bool "Support Denali NAND controller for SPL" + help + This is a small implementation of the Denali NAND controller + for use on SPL. + +config SPL_NAND_SIMPLE + bool "Use simple SPL NAND driver" + depends on !SPL_NAND_AM33XX_BCH + help + Support for NAND boot using simple NAND drivers that + expose the cmd_ctrl() interface. +endif + +endif # if NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile new file mode 100644 index 0000000000..c61e3f3839 --- /dev/null +++ b/drivers/mtd/nand/raw/Makefile @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +ifdef CONFIG_SPL_BUILD + +ifdef CONFIG_SPL_NAND_DRIVERS +NORMAL_DRIVERS=y +endif + +obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o +obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o +obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o +obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o +obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o +obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o +obj-$(CONFIG_SPL_NAND_INIT) += nand.o +ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) +obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o +endif + +else # not spl + +NORMAL_DRIVERS=y + +obj-y += nand.o +obj-y += nand_bbt.o +obj-y += nand_ids.o +obj-y += nand_util.o +obj-y += nand_ecc.o +obj-y += nand_base.o +obj-y += nand_timings.o + +endif # not spl + +ifdef NORMAL_DRIVERS + +obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o + +obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o +obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o +obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o +obj-$(CONFIG_NAND_DENALI) += denali.o +obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o +obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o +obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o +obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o +obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o +obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o +obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o +obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o +obj-$(CONFIG_NAND_MXC) += mxc_nand.o +obj-$(CONFIG_NAND_MXS) += mxs_nand.o +obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o +obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o +obj-$(CONFIG_NAND_SPEAR) += spr_nand.o +obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o +obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o +obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o +obj-$(CONFIG_NAND_PLAT) += nand_plat.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o + +else # minimal SPL drivers + +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o +obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o +obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o + +endif # drivers diff --git a/drivers/mtd/nand/am335x_spl_bch.c b/drivers/mtd/nand/raw/am335x_spl_bch.c similarity index 100% rename from drivers/mtd/nand/am335x_spl_bch.c rename to drivers/mtd/nand/raw/am335x_spl_bch.c diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/raw/arasan_nfc.c similarity index 100% rename from drivers/mtd/nand/arasan_nfc.c rename to drivers/mtd/nand/raw/arasan_nfc.c diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/raw/atmel_nand.c similarity index 100% rename from drivers/mtd/nand/atmel_nand.c rename to drivers/mtd/nand/raw/atmel_nand.c diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/raw/atmel_nand_ecc.h similarity index 100% rename from drivers/mtd/nand/atmel_nand_ecc.h rename to drivers/mtd/nand/raw/atmel_nand_ecc.h diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c similarity index 99% rename from drivers/mtd/nand/davinci_nand.c rename to drivers/mtd/nand/raw/davinci_nand.c index 305e68ad49..e6a84a52b4 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -9,7 +9,7 @@ /* * - * linux/drivers/mtd/nand/nand_davinci.c + * linux/drivers/mtd/nand/raw/nand_davinci.c * * NAND Flash Driver * diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/raw/denali.c similarity index 100% rename from drivers/mtd/nand/denali.c rename to drivers/mtd/nand/raw/denali.c diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h similarity index 100% rename from drivers/mtd/nand/denali.h rename to drivers/mtd/nand/raw/denali.h diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c similarity index 100% rename from drivers/mtd/nand/denali_dt.c rename to drivers/mtd/nand/raw/denali_dt.c diff --git a/drivers/mtd/nand/denali_spl.c b/drivers/mtd/nand/raw/denali_spl.c similarity index 100% rename from drivers/mtd/nand/denali_spl.c rename to drivers/mtd/nand/raw/denali_spl.c diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c similarity index 100% rename from drivers/mtd/nand/fsl_elbc_nand.c rename to drivers/mtd/nand/raw/fsl_elbc_nand.c diff --git a/drivers/mtd/nand/fsl_elbc_spl.c b/drivers/mtd/nand/raw/fsl_elbc_spl.c similarity index 100% rename from drivers/mtd/nand/fsl_elbc_spl.c rename to drivers/mtd/nand/raw/fsl_elbc_spl.c diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c similarity index 100% rename from drivers/mtd/nand/fsl_ifc_nand.c rename to drivers/mtd/nand/raw/fsl_ifc_nand.c diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/raw/fsl_ifc_spl.c similarity index 100% rename from drivers/mtd/nand/fsl_ifc_spl.c rename to drivers/mtd/nand/raw/fsl_ifc_spl.c diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c similarity index 100% rename from drivers/mtd/nand/fsl_upm.c rename to drivers/mtd/nand/raw/fsl_upm.c diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c similarity index 100% rename from drivers/mtd/nand/fsmc_nand.c rename to drivers/mtd/nand/raw/fsmc_nand.c diff --git a/drivers/mtd/nand/kb9202_nand.c b/drivers/mtd/nand/raw/kb9202_nand.c similarity index 100% rename from drivers/mtd/nand/kb9202_nand.c rename to drivers/mtd/nand/raw/kb9202_nand.c diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/raw/kirkwood_nand.c similarity index 100% rename from drivers/mtd/nand/kirkwood_nand.c rename to drivers/mtd/nand/raw/kirkwood_nand.c diff --git a/drivers/mtd/nand/kmeter1_nand.c b/drivers/mtd/nand/raw/kmeter1_nand.c similarity index 100% rename from drivers/mtd/nand/kmeter1_nand.c rename to drivers/mtd/nand/raw/kmeter1_nand.c diff --git a/drivers/mtd/nand/lpc32xx_nand_mlc.c b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c similarity index 100% rename from drivers/mtd/nand/lpc32xx_nand_mlc.c rename to drivers/mtd/nand/raw/lpc32xx_nand_mlc.c diff --git a/drivers/mtd/nand/lpc32xx_nand_slc.c b/drivers/mtd/nand/raw/lpc32xx_nand_slc.c similarity index 100% rename from drivers/mtd/nand/lpc32xx_nand_slc.c rename to drivers/mtd/nand/raw/lpc32xx_nand_slc.c diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c similarity index 100% rename from drivers/mtd/nand/mxc_nand.c rename to drivers/mtd/nand/raw/mxc_nand.c diff --git a/drivers/mtd/nand/mxc_nand.h b/drivers/mtd/nand/raw/mxc_nand.h similarity index 100% rename from drivers/mtd/nand/mxc_nand.h rename to drivers/mtd/nand/raw/mxc_nand.h diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/raw/mxc_nand_spl.c similarity index 100% rename from drivers/mtd/nand/mxc_nand_spl.c rename to drivers/mtd/nand/raw/mxc_nand_spl.c diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c similarity index 100% rename from drivers/mtd/nand/mxs_nand.c rename to drivers/mtd/nand/raw/mxs_nand.c diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/raw/mxs_nand.h similarity index 100% rename from drivers/mtd/nand/mxs_nand.h rename to drivers/mtd/nand/raw/mxs_nand.h diff --git a/drivers/mtd/nand/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c similarity index 100% rename from drivers/mtd/nand/mxs_nand_dt.c rename to drivers/mtd/nand/raw/mxs_nand_dt.c diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c similarity index 100% rename from drivers/mtd/nand/mxs_nand_spl.c rename to drivers/mtd/nand/raw/mxs_nand_spl.c diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/raw/nand.c similarity index 100% rename from drivers/mtd/nand/nand.c rename to drivers/mtd/nand/raw/nand.c diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/raw/nand_base.c similarity index 100% rename from drivers/mtd/nand/nand_base.c rename to drivers/mtd/nand/raw/nand_base.c diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c similarity index 100% rename from drivers/mtd/nand/nand_bbt.c rename to drivers/mtd/nand/raw/nand_bbt.c diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c similarity index 100% rename from drivers/mtd/nand/nand_bch.c rename to drivers/mtd/nand/raw/nand_bch.c diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c similarity index 99% rename from drivers/mtd/nand/nand_ecc.c rename to drivers/mtd/nand/raw/nand_ecc.c index 05e55fce9a..2bc329be1a 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/raw/nand_ecc.c @@ -3,7 +3,7 @@ * This file contains an ECC algorithm from Toshiba that detects and * corrects 1 bit errors in a 256 byte block of data. * - * drivers/mtd/nand/nand_ecc.c + * drivers/mtd/nand/raw/nand_ecc.c * * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) * Toshiba America Electronics Components, Inc. diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c similarity index 100% rename from drivers/mtd/nand/nand_ids.c rename to drivers/mtd/nand/raw/nand_ids.c diff --git a/drivers/mtd/nand/nand_plat.c b/drivers/mtd/nand/raw/nand_plat.c similarity index 100% rename from drivers/mtd/nand/nand_plat.c rename to drivers/mtd/nand/raw/nand_plat.c diff --git a/drivers/mtd/nand/nand_spl_load.c b/drivers/mtd/nand/raw/nand_spl_load.c similarity index 100% rename from drivers/mtd/nand/nand_spl_load.c rename to drivers/mtd/nand/raw/nand_spl_load.c diff --git a/drivers/mtd/nand/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c similarity index 100% rename from drivers/mtd/nand/nand_spl_loaders.c rename to drivers/mtd/nand/raw/nand_spl_loaders.c diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/raw/nand_spl_simple.c similarity index 100% rename from drivers/mtd/nand/nand_spl_simple.c rename to drivers/mtd/nand/raw/nand_spl_simple.c diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c similarity index 100% rename from drivers/mtd/nand/nand_timings.c rename to drivers/mtd/nand/raw/nand_timings.c diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/raw/nand_util.c similarity index 99% rename from drivers/mtd/nand/nand_util.c rename to drivers/mtd/nand/raw/nand_util.c index 1ded7aa920..fc2235c1a0 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/raw/nand_util.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/mtd/nand/nand_util.c + * drivers/mtd/nand/raw/nand_util.c * * Copyright (C) 2006 by Weiss-Electronic GmbH. * All rights reserved. diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c similarity index 100% rename from drivers/mtd/nand/omap_elm.c rename to drivers/mtd/nand/raw/omap_elm.c diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c similarity index 100% rename from drivers/mtd/nand/omap_gpmc.c rename to drivers/mtd/nand/raw/omap_gpmc.c diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c similarity index 99% rename from drivers/mtd/nand/pxa3xx_nand.c rename to drivers/mtd/nand/raw/pxa3xx_nand.c index 9d02fd8eb1..67b2b62f76 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * drivers/mtd/nand/pxa3xx_nand.c + * drivers/mtd/nand/raw/pxa3xx_nand.c * * Copyright © 2005 Intel Corporation * Copyright © 2006 Marvell International Ltd. diff --git a/drivers/mtd/nand/pxa3xx_nand.h b/drivers/mtd/nand/raw/pxa3xx_nand.h similarity index 100% rename from drivers/mtd/nand/pxa3xx_nand.h rename to drivers/mtd/nand/raw/pxa3xx_nand.h diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c similarity index 100% rename from drivers/mtd/nand/sunxi_nand.c rename to drivers/mtd/nand/raw/sunxi_nand.c diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c similarity index 100% rename from drivers/mtd/nand/sunxi_nand_spl.c rename to drivers/mtd/nand/raw/sunxi_nand_spl.c diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c similarity index 100% rename from drivers/mtd/nand/tegra_nand.c rename to drivers/mtd/nand/raw/tegra_nand.c diff --git a/drivers/mtd/nand/tegra_nand.h b/drivers/mtd/nand/raw/tegra_nand.h similarity index 100% rename from drivers/mtd/nand/tegra_nand.h rename to drivers/mtd/nand/raw/tegra_nand.h diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c similarity index 100% rename from drivers/mtd/nand/vf610_nfc.c rename to drivers/mtd/nand/raw/vf610_nfc.c diff --git a/drivers/mtd/nand/zynq_nand.c b/drivers/mtd/nand/raw/zynq_nand.c similarity index 100% rename from drivers/mtd/nand/zynq_nand.c rename to drivers/mtd/nand/raw/zynq_nand.c diff --git a/include/configs/MPC8313ERDB.h b/include/configs/MPC8313ERDB.h index 1f56deb151..2a58058ebb 100644 --- a/include/configs/MPC8313ERDB.h +++ b/include/configs/MPC8313ERDB.h @@ -242,7 +242,7 @@ /* LB refresh timer prescal, 266MHz/32 */ #define CONFIG_SYS_LBC_MRTPR 0x20000000 /*TODO */ -/* drivers/mtd/nand/nand.c */ +/* drivers/mtd/nand/raw/nand.c */ #if defined(CONFIG_NAND) && defined(CONFIG_SPL_BUILD) #define CONFIG_SYS_NAND_BASE 0xFFF00000 #else From patchwork Thu Aug 16 15:30:08 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958400 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr5k2WNWz9s4Z for ; Fri, 17 Aug 2018 01:36:10 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 126A9C21DEC; Thu, 16 Aug 2018 15:34:53 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id CFEFEC21DC1; Thu, 16 Aug 2018 15:31:08 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 5AA67C21C29; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id EE4E3C21E75 for ; Thu, 16 Aug 2018 15:30:51 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 1764020890; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 2746720F25; Thu, 16 Aug 2018 17:30:33 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:08 +0200 Message-Id: <20180816153029.15521-11-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 10/31] mtd: rename nand into rawnand in Kconfig prompt X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Sync the Kconfig raw NAND entry title with the code architecture. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 1e4ea7bdd4..008f7b4b4b 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -1,6 +1,6 @@ menuconfig NAND - bool "NAND Device Support" + bool "Raw NAND Device Support" if NAND config SYS_NAND_SELF_INIT From patchwork Thu Aug 16 15:30:09 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958424 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrJD5PD5z9s3Z for ; Fri, 17 Aug 2018 01:45:16 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 67169C21E13; Thu, 16 Aug 2018 15:39:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 3DD79C21E2C; Thu, 16 Aug 2018 15:31:23 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 3928BC21EB9; Thu, 16 Aug 2018 15:31:09 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 71963C21E29 for ; Thu, 16 Aug 2018 15:30:52 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 8B5E720DBD; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 7991920F71; Thu, 16 Aug 2018 17:30:33 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:09 +0200 Message-Id: <20180816153029.15521-12-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 11/31] mtd: nand: Add core infrastructure to deal with NAND devices X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Add an intermediate layer to abstract NAND device interface so that some logic can be shared between SPI NANDs, parallel/raw NANDs, OneNANDs, ... Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/Kconfig | 3 + drivers/mtd/nand/Makefile | 2 + drivers/mtd/nand/bbt.c | 132 +++++++ drivers/mtd/nand/core.c | 243 +++++++++++++ include/linux/mtd/nand.h | 731 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1111 insertions(+) create mode 100644 drivers/mtd/nand/bbt.c create mode 100644 drivers/mtd/nand/core.c create mode 100644 include/linux/mtd/nand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6d53734718..1c1a1f487e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1 +1,4 @@ +config MTD_NAND_CORE + tristate + source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 69f40d1563..cd492dbc14 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ +nandcore-objs := core.o bbt.o +obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c new file mode 100644 index 0000000000..7e0ad3190c --- /dev/null +++ b/drivers/mtd/nand/bbt.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand-bbt: " fmt + +#include +#ifndef __UBOOT__ +#include +#endif + +/** + * nanddev_bbt_init() - Initialize the BBT (Bad Block Table) + * @nand: NAND device + * + * Initialize the in-memory BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_init(struct nand_device *nand) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned int nblocks = nanddev_neraseblocks(nand); + unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block, + BITS_PER_LONG); + + nand->bbt.cache = kzalloc(nwords, GFP_KERNEL); + if (!nand->bbt.cache) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_init); + +/** + * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) + * @nand: NAND device + * + * Undoes what has been done in nanddev_bbt_init() + */ +void nanddev_bbt_cleanup(struct nand_device *nand) +{ + kfree(nand->bbt.cache); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); + +/** + * nanddev_bbt_update() - Update a BBT + * @nand: nand device + * + * Update the BBT. Currently a NOP function since on-flash bbt is not yet + * supported. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_update(struct nand_device *nand) +{ + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_update); + +/** + * nanddev_bbt_get_block_status() - Return the status of an eraseblock + * @nand: nand device + * @entry: the BBT entry + * + * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry + * is bigger than the BBT size. + */ +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long status; + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + status = pos[0] >> offs; + if (bits_per_block + offs > BITS_PER_LONG) + status |= pos[1] << (BITS_PER_LONG - offs); + + return status & GENMASK(bits_per_block - 1, 0); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); + +/** + * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the + * in-memory BBT + * @nand: nand device + * @entry: the BBT entry to update + * @status: the new status + * + * Update an entry of the in-memory BBT. If you want to push the updated BBT + * the NAND you should call nanddev_bbt_update(). + * + * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT + * size. + */ +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long val = status & GENMASK(bits_per_block - 1, 0); + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); + pos[0] |= val << offs; + + if (bits_per_block + offs > BITS_PER_LONG) { + unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; + + pos[1] &= ~GENMASK(rbits - 1, 0); + pos[1] |= val >> rbits; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c new file mode 100644 index 0000000000..0b793695cc --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand: " fmt + +#ifndef __UBOOT__ +#include +#endif +#include + +/** + * nanddev_isbad() - Check if a block is bad + * @nand: NAND device + * @pos: position pointing to the block we want to check + * + * Return: true if the block is bad, false otherwise. + */ +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_bbt_is_initialized(nand)) { + unsigned int entry; + int status; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + /* Lazy block status retrieval */ + if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { + if (nand->ops->isbad(nand, pos)) + status = NAND_BBT_BLOCK_FACTORY_BAD; + else + status = NAND_BBT_BLOCK_GOOD; + + nanddev_bbt_set_block_status(nand, entry, status); + } + + if (status == NAND_BBT_BLOCK_WORN || + status == NAND_BBT_BLOCK_FACTORY_BAD) + return true; + + return false; + } + + return nand->ops->isbad(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_isbad); + +/** + * nanddev_markbad() - Mark a block as bad + * @nand: NAND device + * @pos: position of the block to mark bad + * + * Mark a block bad. This function is updating the BBT if available and + * calls the low-level markbad hook (nand->ops->markbad()). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + unsigned int entry; + int ret = 0; + + if (nanddev_isbad(nand, pos)) + return 0; + + ret = nand->ops->markbad(nand, pos); + if (ret) + pr_warn("failed to write BBM to block @%llx (err = %d)\n", + nanddev_pos_to_offs(nand, pos), ret); + + if (!nanddev_bbt_is_initialized(nand)) + goto out; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); + if (ret) + goto out; + + ret = nanddev_bbt_update(nand); + +out: + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} +EXPORT_SYMBOL_GPL(nanddev_markbad); + +/** + * nanddev_isreserved() - Check whether an eraseblock is reserved or not + * @nand: NAND device + * @pos: NAND position to test + * + * Checks whether the eraseblock pointed by @pos is reserved or not. + * + * Return: true if the eraseblock is reserved, false otherwise. + */ +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) +{ + unsigned int entry; + int status; + + if (!nanddev_bbt_is_initialized(nand)) + return false; + + /* Return info from the table */ + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + return status == NAND_BBT_BLOCK_RESERVED; +} +EXPORT_SYMBOL_GPL(nanddev_isreserved); + +/** + * nanddev_erase() - Erase a NAND portion + * @nand: NAND device + * @pos: position of the block to erase + * + * Erases the block if it's not bad. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { + pr_warn("attempt to erase a bad/reserved block @%llx\n", + nanddev_pos_to_offs(nand, pos)); + return -EIO; + } + + return nand->ops->erase(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_erase); + +/** + * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices + * @mtd: MTD device + * @einfo: erase request + * + * This is a simple mtd->_erase() implementation iterating over all blocks + * concerned by @einfo and calling nand->ops->erase() on each of them. + * + * Note that mtd->_erase should not be directly assigned to this helper, + * because there's no locking here. NAND specialized layers should instead + * implement there own wrapper around nanddev_mtd_erase() taking the + * appropriate lock before calling nanddev_mtd_erase(). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos, last; + int ret; + + nanddev_offs_to_pos(nand, einfo->addr, &pos); + nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); + while (nanddev_pos_cmp(&pos, &last) <= 0) { + ret = nanddev_erase(nand, &pos); + if (ret) { + einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); + + return ret; + } + + nanddev_pos_next_eraseblock(nand, &pos); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_mtd_erase); + +/** + * nanddev_init() - Initialize a NAND device + * @nand: NAND device + * @ops: NAND device operations + * @owner: NAND device owner + * + * Initializes a NAND device object. Consistency checks are done on @ops and + * @nand->memorg. Also takes care of initializing the BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_memory_organization *memorg = nanddev_get_memorg(nand); + + if (!nand || !ops) + return -EINVAL; + + if (!ops->erase || !ops->markbad || !ops->isbad) + return -EINVAL; + + if (!memorg->bits_per_cell || !memorg->pagesize || + !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || + !memorg->planes_per_lun || !memorg->luns_per_target || + !memorg->ntargets) + return -EINVAL; + + nand->rowconv.eraseblock_addr_shift = + fls(memorg->pages_per_eraseblock - 1); + nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + + nand->rowconv.eraseblock_addr_shift; + + nand->ops = ops; + + mtd->type = memorg->bits_per_cell == 1 ? + MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; + mtd->writesize = memorg->pagesize; + mtd->writebufsize = memorg->pagesize; + mtd->oobsize = memorg->oobsize; + mtd->size = nanddev_size(nand); + mtd->owner = owner; + + return nanddev_bbt_init(nand); +} +EXPORT_SYMBOL_GPL(nanddev_init); + +/** + * nanddev_cleanup() - Release resources allocated in nanddev_init() + * @nand: NAND device + * + * Basically undoes what has been done in nanddev_init(). + */ +void nanddev_cleanup(struct nand_device *nand) +{ + if (nanddev_bbt_is_initialized(nand)) + nanddev_bbt_cleanup(nand); +} +EXPORT_SYMBOL_GPL(nanddev_cleanup); + +MODULE_DESCRIPTION("Generic NAND framework"); +MODULE_AUTHOR("Boris Brezillon "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h new file mode 100644 index 0000000000..ada7af4a41 --- /dev/null +++ b/include/linux/mtd/nand.h @@ -0,0 +1,731 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2017 - Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H + +#include + +/** + * struct nand_memory_organization - Memory organization structure + * @bits_per_cell: number of bits per NAND cell + * @pagesize: page size + * @oobsize: OOB area size + * @pages_per_eraseblock: number of pages per eraseblock + * @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number) + * @planes_per_lun: number of planes per LUN + * @luns_per_target: number of LUN per target (target is a synonym for die) + * @ntargets: total number of targets exposed by the NAND device + */ +struct nand_memory_organization { + unsigned int bits_per_cell; + unsigned int pagesize; + unsigned int oobsize; + unsigned int pages_per_eraseblock; + unsigned int eraseblocks_per_lun; + unsigned int planes_per_lun; + unsigned int luns_per_target; + unsigned int ntargets; +}; + +#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \ + { \ + .bits_per_cell = (bpc), \ + .pagesize = (ps), \ + .oobsize = (os), \ + .pages_per_eraseblock = (ppe), \ + .eraseblocks_per_lun = (epl), \ + .planes_per_lun = (ppl), \ + .luns_per_target = (lpt), \ + .ntargets = (nt), \ + } + +/** + * struct nand_row_converter - Information needed to convert an absolute offset + * into a row address + * @lun_addr_shift: position of the LUN identifier in the row address + * @eraseblock_addr_shift: position of the eraseblock identifier in the row + * address + */ +struct nand_row_converter { + unsigned int lun_addr_shift; + unsigned int eraseblock_addr_shift; +}; + +/** + * struct nand_pos - NAND position object + * @target: the NAND target/die + * @lun: the LUN identifier + * @plane: the plane within the LUN + * @eraseblock: the eraseblock within the LUN + * @page: the page within the LUN + * + * These information are usually used by specific sub-layers to select the + * appropriate target/die and generate a row address to pass to the device. + */ +struct nand_pos { + unsigned int target; + unsigned int lun; + unsigned int plane; + unsigned int eraseblock; + unsigned int page; +}; + +/** + * struct nand_page_io_req - NAND I/O request object + * @pos: the position this I/O request is targeting + * @dataoffs: the offset within the page + * @datalen: number of data bytes to read from/write to this page + * @databuf: buffer to store data in or get data from + * @ooboffs: the OOB offset within the page + * @ooblen: the number of OOB bytes to read from/write to this page + * @oobbuf: buffer to store OOB data in or get OOB data from + * + * This object is used to pass per-page I/O requests to NAND sub-layers. This + * way all useful information are already formatted in a useful way and + * specific NAND layers can focus on translating these information into + * specific commands/operations. + */ +struct nand_page_io_req { + struct nand_pos pos; + unsigned int dataoffs; + unsigned int datalen; + union { + const void *out; + void *in; + } databuf; + unsigned int ooboffs; + unsigned int ooblen; + union { + const void *out; + void *in; + } oobbuf; +}; + +/** + * struct nand_ecc_req - NAND ECC requirements + * @strength: ECC strength + * @step_size: ECC step/block size + */ +struct nand_ecc_req { + unsigned int strength; + unsigned int step_size; +}; + +#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) } + +/** + * struct nand_bbt - bad block table object + * @cache: in memory BBT cache + */ +struct nand_bbt { + unsigned long *cache; +}; + +struct nand_device; + +/** + * struct nand_ops - NAND operations + * @erase: erase a specific block. No need to check if the block is bad before + * erasing, this has been taken care of by the generic NAND layer + * @markbad: mark a specific block bad. No need to check if the block is + * already marked bad, this has been taken care of by the generic + * NAND layer. This method should just write the BBM (Bad Block + * Marker) so that future call to struct_nand_ops->isbad() return + * true + * @isbad: check whether a block is bad or not. This method should just read + * the BBM and return whether the block is bad or not based on what it + * reads + * + * These are all low level operations that should be implemented by specialized + * NAND layers (SPI NAND, raw NAND, ...). + */ +struct nand_ops { + int (*erase)(struct nand_device *nand, const struct nand_pos *pos); + int (*markbad)(struct nand_device *nand, const struct nand_pos *pos); + bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos); +}; + +/** + * struct nand_device - NAND device + * @mtd: MTD instance attached to the NAND device + * @memorg: memory layout + * @eccreq: ECC requirements + * @rowconv: position to row address converter + * @bbt: bad block table info + * @ops: NAND operations attached to the NAND device + * + * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND) + * should declare their own NAND object embedding a nand_device struct (that's + * how inheritance is done). + * struct_nand_device->memorg and struct_nand_device->eccreq should be filled + * at device detection time to reflect the NAND device + * capabilities/requirements. Once this is done nanddev_init() can be called. + * It will take care of converting NAND information into MTD ones, which means + * the specialized NAND layers should never manually tweak + * struct_nand_device->mtd except for the ->_read/write() hooks. + */ +struct nand_device { + struct mtd_info *mtd; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct nand_row_converter rowconv; + struct nand_bbt bbt; + const struct nand_ops *ops; +}; + +/** + * struct nand_io_iter - NAND I/O iterator + * @req: current I/O request + * @oobbytes_per_page: maximum number of OOB bytes per page + * @dataleft: remaining number of data bytes to read/write + * @oobleft: remaining number of OOB bytes to read/write + * + * Can be used by specialized NAND layers to iterate over all pages covered + * by an MTD I/O request, which should greatly simplifies the boiler-plate + * code needed to read/write data from/to a NAND device. + */ +struct nand_io_iter { + struct nand_page_io_req req; + unsigned int oobbytes_per_page; + unsigned int dataleft; + unsigned int oobleft; +}; + +/** + * mtd_to_nanddev() - Get the NAND device attached to the MTD instance + * @mtd: MTD instance + * + * Return: the NAND device embedding @mtd. + */ +static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/** + * nanddev_to_mtd() - Get the MTD device attached to a NAND device + * @nand: NAND device + * + * Return: the MTD device embedded in @nand. + */ +static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand) +{ + return nand->mtd; +} + +/* + * nanddev_bits_per_cell() - Get the number of bits per cell + * @nand: NAND device + * + * Return: the number of bits per cell. + */ +static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand) +{ + return nand->memorg.bits_per_cell; +} + +/** + * nanddev_page_size() - Get NAND page size + * @nand: NAND device + * + * Return: the page size. + */ +static inline size_t nanddev_page_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize; +} + +/** + * nanddev_per_page_oobsize() - Get NAND OOB size + * @nand: NAND device + * + * Return: the OOB size. + */ +static inline unsigned int +nanddev_per_page_oobsize(const struct nand_device *nand) +{ + return nand->memorg.oobsize; +} + +/** + * nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock + * @nand: NAND device + * + * Return: the number of pages per eraseblock. + */ +static inline unsigned int +nanddev_pages_per_eraseblock(const struct nand_device *nand) +{ + return nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_per_page_oobsize() - Get NAND erase block size + * @nand: NAND device + * + * Return: the eraseblock size. + */ +static inline size_t nanddev_eraseblock_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN + * @nand: NAND device + * + * Return: the number of eraseblocks per LUN. + */ +static inline unsigned int +nanddev_eraseblocks_per_lun(const struct nand_device *nand) +{ + return nand->memorg.eraseblocks_per_lun; +} + +/** + * nanddev_target_size() - Get the total size provided by a single target/die + * @nand: NAND device + * + * Return: the total size exposed by a single target/die in bytes. + */ +static inline u64 nanddev_target_size(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock * + nand->memorg.pagesize; +} + +/** + * nanddev_ntarget() - Get the total of targets + * @nand: NAND device + * + * Return: the number of targets/dies exposed by @nand. + */ +static inline unsigned int nanddev_ntargets(const struct nand_device *nand) +{ + return nand->memorg.ntargets; +} + +/** + * nanddev_neraseblocks() - Get the total number of erasablocks + * @nand: NAND device + * + * Return: the total number of eraseblocks exposed by @nand. + */ +static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_size() - Get NAND size + * @nand: NAND device + * + * Return: the total size (in bytes) exposed by @nand. + */ +static inline u64 nanddev_size(const struct nand_device *nand) +{ + return nanddev_target_size(nand) * nanddev_ntargets(nand); +} + +/** + * nanddev_get_memorg() - Extract memory organization info from a NAND device + * @nand: NAND device + * + * This can be used by the upper layer to fill the memorg info before calling + * nanddev_init(). + * + * Return: the memorg object embedded in the NAND device. + */ +static inline struct nand_memory_organization * +nanddev_get_memorg(struct nand_device *nand) +{ + return &nand->memorg; +} + +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner); +void nanddev_cleanup(struct nand_device *nand); + +/** + * nanddev_register() - Register a NAND device + * @nand: NAND device + * + * Register a NAND device. + * This function is just a wrapper around mtd_device_register() + * registering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_register(struct nand_device *nand) +{ + return mtd_device_register(nand->mtd, NULL, 0); +} + +/** + * nanddev_unregister() - Unregister a NAND device + * @nand: NAND device + * + * Unregister a NAND device. + * This function is just a wrapper around mtd_device_unregister() + * unregistering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_unregister(struct nand_device *nand) +{ + return mtd_device_unregister(nand->mtd); +} + +/** + * nanddev_set_of_node() - Attach a DT node to a NAND device + * @nand: NAND device + * @np: DT node + * + * Attach a DT node to a NAND device. + */ +static inline void nanddev_set_of_node(struct nand_device *nand, + const struct device_node *np) +{ + mtd_set_of_node(nand->mtd, np); +} + +/** + * nanddev_get_of_node() - Retrieve the DT node attached to a NAND device + * @nand: NAND device + * + * Return: the DT node attached to @nand. + */ +static inline const struct device_node *nanddev_get_of_node(struct nand_device *nand) +{ + return mtd_get_of_node(nand->mtd); +} + +/** + * nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position + * @nand: NAND device + * @offs: absolute NAND offset (usually passed by the MTD layer) + * @pos: a NAND position object to fill in + * + * Converts @offs into a nand_pos representation. + * + * Return: the offset within the NAND page pointed by @pos. + */ +static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand, + loff_t offs, + struct nand_pos *pos) +{ + unsigned int pageoffs; + u64 tmp = offs; + + pageoffs = do_div(tmp, nand->memorg.pagesize); + pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock); + pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun); + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; + pos->lun = do_div(tmp, nand->memorg.luns_per_target); + pos->target = tmp; + + return pageoffs; +} + +/** + * nanddev_pos_cmp() - Compare two NAND positions + * @a: First NAND position + * @b: Second NAND position + * + * Compares two NAND positions. + * + * Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b. + */ +static inline int nanddev_pos_cmp(const struct nand_pos *a, + const struct nand_pos *b) +{ + if (a->target != b->target) + return a->target < b->target ? -1 : 1; + + if (a->lun != b->lun) + return a->lun < b->lun ? -1 : 1; + + if (a->eraseblock != b->eraseblock) + return a->eraseblock < b->eraseblock ? -1 : 1; + + if (a->page != b->page) + return a->page < b->page ? -1 : 1; + + return 0; +} + +/** + * nanddev_pos_to_offs() - Convert a NAND position into an absolute offset + * @nand: NAND device + * @pos: the NAND position to convert + * + * Converts @pos NAND position into an absolute offset. + * + * Return: the absolute offset. Note that @pos points to the beginning of a + * page, if one wants to point to a specific offset within this page + * the returned offset has to be adjusted manually. + */ +static inline loff_t nanddev_pos_to_offs(struct nand_device *nand, + const struct nand_pos *pos) +{ + unsigned int npages; + + npages = pos->page + + ((pos->eraseblock + + (pos->lun + + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun) * + nand->memorg.pages_per_eraseblock); + + return (loff_t)npages * nand->memorg.pagesize; +} + +/** + * nanddev_pos_to_row() - Extract a row address from a NAND position + * @nand: NAND device + * @pos: the position to convert + * + * Converts a NAND position into a row address that can then be passed to the + * device. + * + * Return: the row address extracted from @pos. + */ +static inline unsigned int nanddev_pos_to_row(struct nand_device *nand, + const struct nand_pos *pos) +{ + return (pos->lun << nand->rowconv.lun_addr_shift) | + (pos->eraseblock << nand->rowconv.eraseblock_addr_shift) | + pos->page; +} + +/** + * nanddev_pos_next_target() - Move a position to the next target/die + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next target/die. Useful when you + * want to iterate over all targets/dies of a NAND device. + */ +static inline void nanddev_pos_next_target(struct nand_device *nand, + struct nand_pos *pos) +{ + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; + pos->lun = 0; + pos->target++; +} + +/** + * nanddev_pos_next_lun() - Move a position to the next LUN + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next LUN. Useful when you want to + * iterate over all LUNs of a NAND device. + */ +static inline void nanddev_pos_next_lun(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->lun >= nand->memorg.luns_per_target - 1) + return nanddev_pos_next_target(nand, pos); + + pos->lun++; + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next eraseblock + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next eraseblock. Useful when you + * want to iterate over all eraseblocks of a NAND device. + */ +static inline void nanddev_pos_next_eraseblock(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1) + return nanddev_pos_next_lun(nand, pos); + + pos->eraseblock++; + pos->page = 0; + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next page + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next page. Useful when you want to + * iterate over all pages of a NAND device. + */ +static inline void nanddev_pos_next_page(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->page >= nand->memorg.pages_per_eraseblock - 1) + return nanddev_pos_next_eraseblock(nand, pos); + + pos->page++; +} + +/** + * nand_io_iter_init - Initialize a NAND I/O iterator + * @nand: NAND device + * @offs: absolute offset + * @req: MTD request + * @iter: NAND I/O iterator + * + * Initializes a NAND iterator based on the information passed by the MTD + * layer. + */ +static inline void nanddev_io_iter_init(struct nand_device *nand, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + + iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); + iter->req.ooboffs = req->ooboffs; + iter->oobbytes_per_page = mtd_oobavail(mtd, req); + iter->dataleft = req->len; + iter->oobleft = req->ooblen; + iter->req.databuf.in = req->datbuf; + iter->req.datalen = min_t(unsigned int, + nand->memorg.pagesize - iter->req.dataoffs, + iter->dataleft); + iter->req.oobbuf.in = req->oobbuf; + iter->req.ooblen = min_t(unsigned int, + iter->oobbytes_per_page - iter->req.ooboffs, + iter->oobleft); +} + +/** + * nand_io_iter_next_page - Move to the next page + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Updates the @iter to point to the next page. + */ +static inline void nanddev_io_iter_next_page(struct nand_device *nand, + struct nand_io_iter *iter) +{ + nanddev_pos_next_page(nand, &iter->req.pos); + iter->dataleft -= iter->req.datalen; + iter->req.databuf.in += iter->req.datalen; + iter->oobleft -= iter->req.ooblen; + iter->req.oobbuf.in += iter->req.ooblen; + iter->req.dataoffs = 0; + iter->req.ooboffs = 0; + iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize, + iter->dataleft); + iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page, + iter->oobleft); +} + +/** + * nand_io_iter_end - Should end iteration or not + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Check whether @iter has reached the end of the NAND portion it was asked to + * iterate on or not. + * + * Return: true if @iter has reached the end of the iteration request, false + * otherwise. + */ +static inline bool nanddev_io_iter_end(struct nand_device *nand, + const struct nand_io_iter *iter) +{ + if (iter->dataleft || iter->oobleft) + return false; + + return true; +} + +/** + * nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O + * request + * @nand: NAND device + * @start: start address to read/write from + * @req: MTD I/O request + * @iter: NAND I/O iterator + * + * Should be used for iterate over pages that are contained in an MTD request. + */ +#define nanddev_io_for_each_page(nand, start, req, iter) \ + for (nanddev_io_iter_init(nand, start, req, iter); \ + !nanddev_io_iter_end(nand, iter); \ + nanddev_io_iter_next_page(nand, iter)) + +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos); +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); + +/* BBT related functions */ +enum nand_bbt_block_status { + NAND_BBT_BLOCK_STATUS_UNKNOWN, + NAND_BBT_BLOCK_GOOD, + NAND_BBT_BLOCK_WORN, + NAND_BBT_BLOCK_RESERVED, + NAND_BBT_BLOCK_FACTORY_BAD, + NAND_BBT_BLOCK_NUM_STATUS, +}; + +int nanddev_bbt_init(struct nand_device *nand); +void nanddev_bbt_cleanup(struct nand_device *nand); +int nanddev_bbt_update(struct nand_device *nand); +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry); +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status); +int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block); + +/** + * nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry + * @nand: NAND device + * @pos: the NAND position we want to get BBT entry for + * + * Return the BBT entry used to store information about the eraseblock pointed + * by @pos. + * + * Return: the BBT entry storing information about eraseblock pointed by @pos. + */ +static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand, + const struct nand_pos *pos) +{ + return pos->eraseblock + + ((pos->lun + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun); +} + +/** + * nanddev_bbt_is_initialized() - Check if the BBT has been initialized + * @nand: NAND device + * + * Return: true if the BBT has been initialized, false otherwise. + */ +static inline bool nanddev_bbt_is_initialized(struct nand_device *nand) +{ + return !!nand->bbt.cache; +} + +/* MTD -> NAND helper functions. */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); + +#endif /* __LINUX_MTD_NAND_H */ From patchwork Thu Aug 16 15:30:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958415 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrC65tPHz9s4s for ; Fri, 17 Aug 2018 01:40:50 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 85E75C21DEC; Thu, 16 Aug 2018 15:35:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 65BE5C21E79; Thu, 16 Aug 2018 15:31:13 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 8635DC21DFA; Thu, 16 Aug 2018 15:31:07 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 5CCCEC21D8A for ; Thu, 16 Aug 2018 15:30:52 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 781112090A; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id CA5AF20F73; Thu, 16 Aug 2018 17:30:33 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:10 +0200 Message-Id: <20180816153029.15521-13-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 12/31] mtd: nand: Pass mode information to nand_page_io_req X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon The NAND sub-layers are likely to need the MTD_OPS_XXX mode information in order to decide if they should enable/disable ECC or how they should place the OOB bytes in the provided OOB buffer. Add a field to nand_page_io_req to pass this information. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- include/linux/mtd/nand.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ada7af4a41..13e8dd1103 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -86,6 +86,7 @@ struct nand_pos { * @ooboffs: the OOB offset within the page * @ooblen: the number of OOB bytes to read from/write to this page * @oobbuf: buffer to store OOB data in or get OOB data from + * @mode: one of the %MTD_OPS_XXX mode * * This object is used to pass per-page I/O requests to NAND sub-layers. This * way all useful information are already formatted in a useful way and @@ -106,6 +107,7 @@ struct nand_page_io_req { const void *out; void *in; } oobbuf; + int mode; }; /** @@ -599,6 +601,7 @@ static inline void nanddev_io_iter_init(struct nand_device *nand, { struct mtd_info *mtd = nanddev_to_mtd(nand); + iter->req.mode = req->mode; iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); iter->req.ooboffs = req->ooboffs; iter->oobbytes_per_page = mtd_oobavail(mtd, req); From patchwork Thu Aug 16 15:30:11 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958421 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrGR1lMxz9s8f for ; Fri, 17 Aug 2018 01:43:43 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id D0996C21DFD; Thu, 16 Aug 2018 15:37:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 487A3C21E50; Thu, 16 Aug 2018 15:31:18 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 1BE07C21E02; Thu, 16 Aug 2018 15:31:09 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id A4B99C21E49 for ; Thu, 16 Aug 2018 15:30:52 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id C14BA20EB9; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 2060920F77; Thu, 16 Aug 2018 17:30:34 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:11 +0200 Message-Id: <20180816153029.15521-14-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 13/31] spi: Extend the core to ease integration of SPI memory controllers X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Some controllers are exposing high-level interfaces to access various kind of SPI memories. Unfortunately they do not fit in the current spi_controller model and usually have drivers placed in drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI memories in general. This is an attempt at defining a SPI memory interface which works for all kinds of SPI memories (NORs, NANDs, SRAMs). Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-mem.c | 501 ++++++++++++++++++++++++++++++++++++++++++ include/spi-mem.h | 258 ++++++++++++++++++++++ include/spi.h | 11 + 5 files changed, 778 insertions(+) create mode 100644 drivers/spi/spi-mem.c create mode 100644 include/spi-mem.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index dcd719ff0a..9fbd26740d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -18,6 +18,13 @@ config DM_SPI if DM_SPI +config SPI_MEM + bool "SPI memory extension" + help + Enable this option if you want to enable the SPI memory extension. + This extension is meant to simplify interaction with SPI memories + by providing an high-level interface to send memory-like commands. + config ALTERA_SPI bool "Altera SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 728e30c538..bdb5b5a02f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DM_SPI obj-y += spi-uclass.o obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o obj-$(CONFIG_SOFT_SPI) += soft_spi.o +obj-$(CONFIG_SPI_MEM) += spi-mem.o else obj-y += spi.o obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c new file mode 100644 index 0000000000..af9aef009a --- /dev/null +++ b/drivers/spi/spi-mem.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Exceet Electronics GmbH + * Copyright (C) 2018 Bootlin + * + * Author: Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#include "internals.h" +#else +#include +#include +#endif + +#ifndef __UBOOT__ +/** + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a + * memory operation + * @ctlr: the SPI controller requesting this dma_map() + * @op: the memory operation containing the buffer to map + * @sgt: a pointer to a non-initialized sg_table that will be filled by this + * function + * + * Some controllers might want to do DMA on the data buffer embedded in @op. + * This helper prepares everything for you and provides a ready-to-use + * sg_table. This function is not intended to be called from spi drivers. + * Only SPI controller drivers should use it. + * Note that the caller must ensure the memory region pointed by + * op->data.buf.{in,out} is DMA-able before calling this function. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sgt) +{ + struct device *dmadev; + + if (!op->data.nbytes) + return -EINVAL; + + if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) + dmadev = ctlr->dma_tx->device->dev; + else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) + dmadev = ctlr->dma_rx->device->dev; + else + dmadev = ctlr->dev.parent; + + if (!dmadev) + return -EINVAL; + + return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes, + op->data.dir == SPI_MEM_DATA_IN ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +} +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data); + +/** + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a + * memory operation + * @ctlr: the SPI controller requesting this dma_unmap() + * @op: the memory operation containing the buffer to unmap + * @sgt: a pointer to an sg_table previously initialized by + * spi_controller_dma_map_mem_op_data() + * + * Some controllers might want to do DMA on the data buffer embedded in @op. + * This helper prepares things so that the CPU can access the + * op->data.buf.{in,out} buffer again. + * + * This function is not intended to be called from SPI drivers. Only SPI + * controller drivers should use it. + * + * This function should be called after the DMA operation has finished and is + * only valid if the previous spi_controller_dma_map_mem_op_data() call + * returned 0. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sgt) +{ + struct device *dmadev; + + if (!op->data.nbytes) + return; + + if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) + dmadev = ctlr->dma_tx->device->dev; + else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) + dmadev = ctlr->dma_rx->device->dev; + else + dmadev = ctlr->dev.parent; + + spi_unmap_buf(ctlr, dmadev, sgt, + op->data.dir == SPI_MEM_DATA_IN ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +} +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data); +#endif /* __UBOOT__ */ + +static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx) +{ + u32 mode = slave->mode; + + switch (buswidth) { + case 1: + return 0; + + case 2: + if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || + (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + return 0; + + break; + + case 4: + if ((tx && (mode & SPI_TX_QUAD)) || + (!tx && (mode & SPI_RX_QUAD))) + return 0; + + break; + + default: + break; + } + + return -ENOTSUPP; +} + +bool spi_mem_default_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (spi_check_buswidth_req(slave, op->cmd.buswidth, true)) + return false; + + if (op->addr.nbytes && + spi_check_buswidth_req(slave, op->addr.buswidth, true)) + return false; + + if (op->dummy.nbytes && + spi_check_buswidth_req(slave, op->dummy.buswidth, true)) + return false; + + if (op->data.nbytes && + spi_check_buswidth_req(slave, op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); + +/** + * spi_mem_supports_op() - Check if a memory device and the controller it is + * connected to support a specific memory operation + * @slave: the SPI device + * @op: the memory operation to check + * + * Some controllers are only supporting Single or Dual IOs, others might only + * support specific opcodes, or it can even be that the controller and device + * both support Quad IOs but the hardware prevents you from using it because + * only 2 IO lines are connected. + * + * This function checks whether a specific operation is supported. + * + * Return: true if @op is supported, false otherwise. + */ +bool spi_mem_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->mem_ops && ops->mem_ops->supports_op) + return ops->mem_ops->supports_op(slave, op); + + return spi_mem_default_supports_op(slave, op); +} +EXPORT_SYMBOL_GPL(spi_mem_supports_op); + +/** + * spi_mem_exec_op() - Execute a memory operation + * @slave: the SPI device + * @op: the memory operation to execute + * + * Executes a memory operation. + * + * This function first checks that @op is supported and then tries to execute + * it. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + unsigned int pos = 0; + const u8 *tx_buf = NULL; + u8 *rx_buf = NULL; + u8 *op_buf; + int op_len; + u32 flag; + int ret; + int i; + + if (!spi_mem_supports_op(slave, op)) + return -ENOTSUPP; + + if (ops->mem_ops) { +#ifndef __UBOOT__ + /* + * 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; + } + } + + mutex_lock(&ctlr->bus_lock_mutex); + mutex_lock(&ctlr->io_mutex); +#endif + ret = ops->mem_ops->exec_op(slave, op); +#ifndef __UBOOT__ + mutex_unlock(&ctlr->io_mutex); + mutex_unlock(&ctlr->bus_lock_mutex); + + if (ctlr->auto_runtime_pm) + pm_runtime_put(ctlr->dev.parent); +#endif + + /* + * Some controllers only optimize specific paths (typically the + * read path) and expect the core to use the regular SPI + * interface in other cases. + */ + if (!ret || ret != -ENOTSUPP) + return ret; + } + +#ifndef __UBOOT__ + tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes + + op->dummy.nbytes; + + /* + * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so + * we're guaranteed that this buffer is DMA-able, as required by the + * SPI layer. + */ + tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA); + if (!tmpbuf) + return -ENOMEM; + + spi_message_init(&msg); + + tmpbuf[0] = op->cmd.opcode; + xfers[xferpos].tx_buf = tmpbuf; + xfers[xferpos].len = sizeof(op->cmd.opcode); + xfers[xferpos].tx_nbits = op->cmd.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen++; + + if (op->addr.nbytes) { + int i; + + for (i = 0; i < op->addr.nbytes; i++) + tmpbuf[i + 1] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + xfers[xferpos].tx_buf = tmpbuf + 1; + xfers[xferpos].len = op->addr.nbytes; + xfers[xferpos].tx_nbits = op->addr.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->addr.nbytes; + } + + if (op->dummy.nbytes) { + memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes); + xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1; + xfers[xferpos].len = op->dummy.nbytes; + xfers[xferpos].tx_nbits = op->dummy.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->dummy.nbytes; + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) { + xfers[xferpos].rx_buf = op->data.buf.in; + xfers[xferpos].rx_nbits = op->data.buswidth; + } else { + xfers[xferpos].tx_buf = op->data.buf.out; + xfers[xferpos].tx_nbits = op->data.buswidth; + } + + xfers[xferpos].len = op->data.nbytes; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->data.nbytes; + } + + ret = spi_sync(slave, &msg); + + kfree(tmpbuf); + + if (ret) + return ret; + + if (msg.actual_length != totalxferlen) + return -EIO; +#else + + /* U-Boot does not support parallel SPI data lanes */ + if ((op->cmd.buswidth != 1) || + (op->addr.nbytes && op->addr.buswidth != 1) || + (op->dummy.nbytes && op->dummy.buswidth != 1) || + (op->data.nbytes && op->data.buswidth != 1)) { + printf("Dual/Quad raw SPI transfers not supported\n"); + return -ENOTSUPP; + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + rx_buf = op->data.buf.in; + else + tx_buf = op->data.buf.out; + } + + op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes; + op_buf = calloc(1, op_len); + + ret = spi_claim_bus(slave); + if (ret < 0) + return ret; + + op_buf[pos++] = op->cmd.opcode; + + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) + op_buf[pos + i] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + pos += op->addr.nbytes; + } + + if (op->dummy.nbytes) + memset(op_buf + pos, 0xff, op->dummy.nbytes); + + /* 1st transfer: opcode + address + dummy cycles */ + flag = SPI_XFER_BEGIN; + /* Make sure to set END bit if no tx or rx data messages follow */ + if (!tx_buf && !rx_buf) + flag |= SPI_XFER_END; + + ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag); + if (ret) + return ret; + + /* 2nd transfer: rx or tx data path */ + if (tx_buf || rx_buf) { + ret = spi_xfer(slave, op->data.nbytes * 8, tx_buf, + rx_buf, SPI_XFER_END); + if (ret) + return ret; + } + + spi_release_bus(slave); + + for (i = 0; i < pos; i++) + debug("%02x ", op_buf[i]); + debug("| [%dB %s] ", + tx_buf || rx_buf ? op->data.nbytes : 0, + tx_buf || rx_buf ? (tx_buf ? "out" : "in") : "-"); + for (i = 0; i < op->data.nbytes; i++) + debug("%02x ", tx_buf ? tx_buf[i] : rx_buf[i]); + debug("[ret %d]\n", ret); + + free(op_buf); + + if (ret < 0) + return ret; +#endif /* __UBOOT__ */ + + return 0; +} +EXPORT_SYMBOL_GPL(spi_mem_exec_op); + +/** + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to + * match controller limitations + * @slave: the SPI device + * @op: the operation to adjust + * + * Some controllers have FIFO limitations and must split a data transfer + * operation into multiple ones, others require a specific alignment for + * optimized accesses. This function allows SPI mem drivers to split a single + * operation into multiple sub-operations when required. + * + * Return: a negative error code if the controller can't properly adjust @op, + * 0 otherwise. Note that @op->data.nbytes will be updated if @op + * can't be handled in a single step. + */ +int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->mem_ops && ops->mem_ops->adjust_op_size) + return ops->mem_ops->adjust_op_size(slave, op); + + return 0; +} +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); + +#ifndef __UBOOT__ +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) +{ + return container_of(drv, struct spi_mem_driver, spidrv.driver); +} + +static int spi_mem_probe(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem; + + mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mem->spi = spi; + spi_set_drvdata(spi, mem); + + return memdrv->probe(mem); +} + +static int spi_mem_remove(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem = spi_get_drvdata(spi); + + if (memdrv->remove) + return memdrv->remove(mem); + + return 0; +} + +static void spi_mem_shutdown(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem = spi_get_drvdata(spi); + + if (memdrv->shutdown) + memdrv->shutdown(mem); +} + +/** + * spi_mem_driver_register_with_owner() - Register a SPI memory driver + * @memdrv: the SPI memory driver to register + * @owner: the owner of this driver + * + * Registers a SPI memory driver. + * + * Return: 0 in case of success, a negative error core otherwise. + */ + +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv, + struct module *owner) +{ + memdrv->spidrv.probe = spi_mem_probe; + memdrv->spidrv.remove = spi_mem_remove; + memdrv->spidrv.shutdown = spi_mem_shutdown; + + return __spi_register_driver(owner, &memdrv->spidrv); +} +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner); + +/** + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver + * @memdrv: the SPI memory driver to unregister + * + * Unregisters a SPI memory driver. + */ +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv) +{ + spi_unregister_driver(&memdrv->spidrv); +} +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister); +#endif /* __UBOOT__ */ diff --git a/include/spi-mem.h b/include/spi-mem.h new file mode 100644 index 0000000000..36814efa86 --- /dev/null +++ b/include/spi-mem.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Exceet Electronics GmbH + * Copyright (C) 2018 Bootlin + * + * Author: + * Peter Pan + * Boris Brezillon + */ + +#ifndef __UBOOT_SPI_MEM_H +#define __UBOOT_SPI_MEM_H + +#include +#include +#include +#include + +#define SPI_MEM_OP_CMD(__opcode, __buswidth) \ + { \ + .buswidth = __buswidth, \ + .opcode = __opcode, \ + } + +#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .val = __val, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_ADDR { } + +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DUMMY { } + +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .nbytes = __nbytes, \ + .buf.in = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_OUT, \ + .nbytes = __nbytes, \ + .buf.out = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DATA { } + +/** + * 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 + */ +enum spi_mem_data_dir { + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, +}; + +/** + * struct spi_mem_op - describes a SPI memory operation + * @cmd.buswidth: number of IO lines used to transmit the command + * @cmd.opcode: operation opcode + * @addr.nbytes: number of address bytes to send. Can be zero if the operation + * does not need to send an address + * @addr.buswidth: number of IO lines used to transmit the address cycles + * @addr.val: address value. This value is always sent MSB first on the bus. + * Note that only @addr.nbytes are taken into account in this + * address value, so users should make sure the value fits in the + * assigned number of bytes. + * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes + * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes + * @data.buswidth: number of IO lanes used to send/receive the data + * @data.dir: direction of the transfer + * @data.buf.in: input buffer + * @data.buf.out: output buffer + */ +struct spi_mem_op { + struct { + u8 buswidth; + u8 opcode; + } cmd; + + struct { + u8 nbytes; + u8 buswidth; + u64 val; + } addr; + + struct { + u8 nbytes; + u8 buswidth; + } dummy; + + struct { + u8 buswidth; + enum spi_mem_data_dir dir; + unsigned int nbytes; + /* buf.{in,out} must be DMA-able. */ + union { + void *in; + const void *out; + } buf; + } data; +}; + +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \ + { \ + .cmd = __cmd, \ + .addr = __addr, \ + .dummy = __dummy, \ + .data = __data, \ + } + +#ifndef __UBOOT__ +/** + * struct spi_mem - describes a SPI memory device + * @spi: the underlying SPI device + * @drvpriv: spi_mem_driver private data + * + * Extra information that describe the SPI memory device and may be needed by + * the controller to properly handle this device should be placed here. + * + * One example would be the device size since some controller expose their SPI + * mem devices through a io-mapped region. + */ +struct spi_mem { + struct udevice *dev; + void *drvpriv; +}; + +/** + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem + * device + * @mem: memory device + * @data: data to attach to the memory device + */ +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data) +{ + mem->drvpriv = data; +} + +/** + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem + * device + * @mem: memory device + * + * Return: the data attached to the mem device. + */ +static inline void *spi_mem_get_drvdata(struct spi_mem *mem) +{ + return mem->drvpriv; +} +#endif /* __UBOOT__ */ + +/** + * struct spi_controller_mem_ops - SPI memory operations + * @adjust_op_size: shrink the data xfer of an operation to match controller's + * limitations (can be alignment of max RX/TX size + * limitations) + * @supports_op: check if an operation is supported by the controller + * @exec_op: execute a SPI memory operation + * + * 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. + */ +struct spi_controller_mem_ops { + int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op); + bool (*supports_op)(struct spi_slave *slave, + const struct spi_mem_op *op); + int (*exec_op)(struct spi_slave *slave, + const struct spi_mem_op *op); +}; + +#ifndef __UBOOT__ +/** + * struct spi_mem_driver - SPI memory driver + * @spidrv: inherit from a SPI driver + * @probe: probe a SPI memory. Usually where detection/initialization takes + * place + * @remove: remove a SPI memory + * @shutdown: take appropriate action when the system is shutdown + * + * This is just a thin wrapper around a spi_driver. The core takes care of + * allocating the spi_mem object and forwarding the probe/remove/shutdown + * request to the spi_mem_driver. The reason we use this wrapper is because + * we might have to stuff more information into the spi_mem struct to let + * SPI controllers know more about the SPI memory they interact with, and + * having this intermediate layer allows us to do that without adding more + * useless fields to the spi_device object. + */ +struct spi_mem_driver { + struct spi_driver spidrv; + int (*probe)(struct spi_mem *mem); + int (*remove)(struct spi_mem *mem); + void (*shutdown)(struct spi_mem *mem); +}; + +#if IS_ENABLED(CONFIG_SPI_MEM) +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); + +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); +#else +static inline int +spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ + return -ENOTSUPP; +} + +static inline void +spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ +} +#endif /* CONFIG_SPI_MEM */ +#endif /* __UBOOT__ */ + +int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op); + +bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op); + +int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op); + +#ifndef __UBOOT__ +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, + struct module *owner); + +void spi_mem_driver_unregister(struct spi_mem_driver *drv); + +#define spi_mem_driver_register(__drv) \ + spi_mem_driver_register_with_owner(__drv, THIS_MODULE) + +#define module_spi_mem_driver(__drv) \ + module_driver(__drv, spi_mem_driver_register, \ + spi_mem_driver_unregister) +#endif + +#endif /* __LINUX_SPI_MEM_H */ diff --git a/include/spi.h b/include/spi.h index 9754c53aa1..938627bc01 100644 --- a/include/spi.h +++ b/include/spi.h @@ -9,6 +9,8 @@ #ifndef _SPI_H_ #define _SPI_H_ +#include + /* SPI mode flags */ #define SPI_CPHA BIT(0) /* clock phase */ #define SPI_CPOL BIT(1) /* clock polarity */ @@ -402,6 +404,15 @@ struct dm_spi_ops { int (*xfer)(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags); + /** + * Optimized handlers for SPI memory-like operations. + * + * Optimized/dedicated operations for interactions with SPI memory. This + * field is optional and should only be implemented if the controller + * has native support for memory like operations. + */ + const struct spi_controller_mem_ops *mem_ops; + /** * Set transfer speed. * This sets a new speed to be applied for next spi_xfer(). From patchwork Thu Aug 16 15:30:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958407 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr8V3vcSz9s8f for ; Fri, 17 Aug 2018 01:38:34 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 493E0C21DFD; Thu, 16 Aug 2018 15:36:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 45C72C21E1E; Thu, 16 Aug 2018 15:31:16 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 10306C21DFA; Thu, 16 Aug 2018 15:31:09 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id B17D0C21E62 for ; Thu, 16 Aug 2018 15:30:52 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id CD63B20F25; Thu, 16 Aug 2018 17:30:51 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 6E99A212C4; Thu, 16 Aug 2018 17:30:34 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:12 +0200 Message-Id: <20180816153029.15521-15-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki , Peter Pan Subject: [U-Boot] [PATCH v6 14/31] mtd: nand: Add core infrastructure to support SPI NANDs X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Peter Pan Add a SPI NAND framework based on the generic NAND framework and the spi-mem infrastructure. In its current state, this framework supports the following features: - single/dual/quad IO modes - on-die ECC Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/Kconfig | 2 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/spi/Kconfig | 7 + drivers/mtd/nand/spi/Makefile | 4 + drivers/mtd/nand/spi/core.c | 1235 +++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 427 ++++++++++++ 6 files changed, 1676 insertions(+) create mode 100644 drivers/mtd/nand/spi/Kconfig create mode 100644 drivers/mtd/nand/spi/Makefile create mode 100644 drivers/mtd/nand/spi/core.c create mode 100644 include/linux/mtd/spinand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1c1a1f487e..78ae04bdcb 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -2,3 +2,5 @@ config MTD_NAND_CORE tristate source "drivers/mtd/nand/raw/Kconfig" + +source "drivers/mtd/nand/spi/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index cd492dbc14..a358bc680e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,3 +2,4 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o +obj-$(CONFIG_MTD_SPI_NAND) += spi/ diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig new file mode 100644 index 0000000000..2197cb531f --- /dev/null +++ b/drivers/mtd/nand/spi/Kconfig @@ -0,0 +1,7 @@ +menuconfig MTD_SPI_NAND + bool "SPI NAND device Support" + depends on MTD && DM_SPI + select MTD_NAND_CORE + select SPI_MEM + help + This is the framework for the SPI NAND device drivers. diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile new file mode 100644 index 0000000000..f0c6e69d2e --- /dev/null +++ b/drivers/mtd/nand/spi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +spinand-objs := core.o +obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c new file mode 100644 index 0000000000..08f853ae11 --- /dev/null +++ b/drivers/mtd/nand/spi/core.c @@ -0,0 +1,1235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + * Boris Brezillon + */ + +#define pr_fmt(fmt) "spi-nand: " fmt + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +/* SPI NAND index visible in MTD names */ +static int spi_nand_idx; + +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, + spinand->scratchbuf); + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + *val = *spinand->scratchbuf; + return 0; +} + +static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) +{ + struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg, + spinand->scratchbuf); + + *spinand->scratchbuf = val; + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_read_status(struct spinand_device *spinand, u8 *status) +{ + return spinand_read_reg_op(spinand, REG_STATUS, status); +} + +static int spinand_get_cfg(struct spinand_device *spinand, u8 *cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + *cfg = spinand->cfg_cache[spinand->cur_target]; + return 0; +} + +static int spinand_set_cfg(struct spinand_device *spinand, u8 cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cfg_cache[spinand->cur_target] == cfg) + return 0; + + ret = spinand_write_reg_op(spinand, REG_CFG, cfg); + if (ret) + return ret; + + spinand->cfg_cache[spinand->cur_target] = cfg; + return 0; +} + +/** + * spinand_upd_cfg() - Update the configuration register + * @spinand: the spinand device + * @mask: the mask encoding the bits to update in the config reg + * @val: the new value to apply + * + * Update the configuration register. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val) +{ + int ret; + u8 cfg; + + ret = spinand_get_cfg(spinand, &cfg); + if (ret) + return ret; + + cfg &= ~mask; + cfg |= val; + + return spinand_set_cfg(spinand, cfg); +} + +/** + * spinand_select_target() - Select a specific NAND target/die + * @spinand: the spinand device + * @target: the target/die to select + * + * Select a new target/die. If chip only has one die, this function is a NOOP. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_select_target(struct spinand_device *spinand, unsigned int target) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cur_target == target) + return 0; + + if (nand->memorg.ntargets == 1) { + spinand->cur_target = target; + return 0; + } + + ret = spinand->select_target(spinand, target); + if (ret) + return ret; + + spinand->cur_target = target; + return 0; +} + +static int spinand_init_cfg_cache(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + struct udevice *dev = spinand->slave->dev; + unsigned int target; + int ret; + + spinand->cfg_cache = devm_kzalloc(dev, + sizeof(*spinand->cfg_cache) * + nand->memorg.ntargets, + GFP_KERNEL); + if (!spinand->cfg_cache) + return -ENOMEM; + + for (target = 0; target < nand->memorg.ntargets; target++) { + ret = spinand_select_target(spinand, target); + if (ret) + return ret; + + /* + * We use spinand_read_reg_op() instead of spinand_get_cfg() + * here to bypass the config cache. + */ + ret = spinand_read_reg_op(spinand, REG_CFG, + &spinand->cfg_cache[target]); + if (ret) + return ret; + } + + return 0; +} + +static int spinand_init_quad_enable(struct spinand_device *spinand) +{ + bool enable = false; + + if (!(spinand->flags & SPINAND_HAS_QE_BIT)) + return 0; + + if (spinand->op_templates.read_cache->data.buswidth == 4 || + spinand->op_templates.write_cache->data.buswidth == 4 || + spinand->op_templates.update_cache->data.buswidth == 4) + enable = true; + + return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, + enable ? CFG_QUAD_ENABLE : 0); +} + +static int spinand_ecc_enable(struct spinand_device *spinand, + bool enable) +{ + return spinand_upd_cfg(spinand, CFG_ECC_ENABLE, + enable ? CFG_ECC_ENABLE : 0); +} + +static int spinand_write_enable_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_load_page_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PAGE_READ_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +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; + unsigned int nbytes = 0; + void *buf = NULL; + u16 column = 0; + int ret; + + if (req->datalen) { + adjreq.datalen = nanddev_page_size(nand); + adjreq.dataoffs = 0; + adjreq.databuf.in = spinand->databuf; + buf = spinand->databuf; + nbytes = adjreq.datalen; + } + + 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; + column = nanddev_page_size(nand); + } + } + + spinand_cache_op_adjust_colum(spinand, &adjreq, &column); + op.addr.val = column; + + /* + * 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->slave, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + buf += op.data.nbytes; + nbytes -= op.data.nbytes; + op.addr.val += op.data.nbytes; + } + + if (req->datalen) + memcpy(req->databuf.in, spinand->databuf + req->dataoffs, + req->datalen); + + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_get_databytes(mtd, req->oobbuf.in, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs, + req->ooblen); + } + + return 0; +} + +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; + + memset(spinand->databuf, 0xff, + nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand)); + + 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) + mtd_ooblayout_set_databytes(mtd, req->oobbuf.out, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + 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; + + /* + * 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_adjust_op_size(spinand->slave, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + 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; + } + } + + return 0; +} + +static int spinand_program_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_erase_op(struct spinand_device *spinand, + const struct nand_pos *pos) +{ + struct nand_device *nand = &spinand->base; + unsigned int row = nanddev_pos_to_row(nand, pos); + struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_wait(struct spinand_device *spinand, u8 *s) +{ + unsigned long start, stop; + u8 status; + int ret; + + start = get_timer(0); + stop = 400; + do { + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + + if (!(status & STATUS_BUSY)) + goto out; + } while (get_timer(start) < stop); + + /* + * Extra read, just in case the STATUS_READY bit has changed + * since our last check + */ + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + +out: + if (s) + *s = status; + + return status & STATUS_BUSY ? -ETIMEDOUT : 0; +} + +static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +{ + struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, + SPINAND_MAX_ID_LEN); + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (!ret) + memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + + return ret; +} + +static int spinand_reset_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_RESET_OP; + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + return spinand_wait(spinand, NULL); +} + +static int spinand_lock_block(struct spinand_device *spinand, u8 lock) +{ + return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); +} + +static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (spinand->eccinfo.get_status) + return spinand->eccinfo.get_status(spinand, status); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * We have no way to know exactly how many bitflips have been + * fixed, so let's return the maximum possible value so that + * wear-leveling layers move the data immediately. + */ + return nand->eccreq.strength; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int spinand_read_page(struct spinand_device *spinand, + const struct nand_page_io_req *req, + bool ecc_enabled) +{ + u8 status; + int ret; + + ret = spinand_load_page_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (ret < 0) + return ret; + + ret = spinand_read_from_cache_op(spinand, req); + if (ret) + return ret; + + if (!ecc_enabled) + return 0; + + return spinand_check_ecc_status(spinand, status); +} + +static int spinand_write_page(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + u8 status; + int ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_write_to_cache_op(spinand, req); + if (ret) + return ret; + + ret = spinand_program_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_PROG_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int max_bitflips = 0; + struct nand_io_iter iter; + bool enable_ecc = false; + bool ecc_failed = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) + enable_ecc = true; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + + nanddev_io_for_each_page(nand, from, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_read_page(spinand, &iter.req, enable_ecc); + if (ret < 0 && ret != -EBADMSG) + break; + + if (ret == -EBADMSG) { + ecc_failed = true; + mtd->ecc_stats.failed++; + ret = 0; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max_t(unsigned int, max_bitflips, ret); + } + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + if (ecc_failed && !ret) + ret = -EBADMSG; + + return ret ? ret : max_bitflips; +} + +static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_io_iter iter; + bool enable_ecc = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) + enable_ecc = true; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + + nanddev_io_for_each_page(nand, to, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_write_page(spinand, &iter.req); + if (ret) + break; + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooblen = 2, + .ooboffs = 0, + .oobbuf.in = spinand->oobbuf, + .mode = MTD_OPS_RAW, + }; + int ret; + + memset(spinand->oobbuf, 0, 2); + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_read_page(spinand, &req, false); + if (ret) + return ret; + + if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff) + return true; + + return false; +} + +static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); +#ifndef __UBOOT__ + struct spinand_device *spinand = nand_to_spinand(nand); +#endif + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_isbad(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + return ret; +} + +static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooboffs = 0, + .ooblen = 2, + .oobbuf.out = spinand->oobbuf, + }; + int ret; + + /* Erase block before marking it bad. */ + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_erase_op(spinand, pos); + if (ret) + return ret; + + memset(spinand->oobbuf, 0, 2); + return spinand_write_page(spinand, &req); +} + +static int spinand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); +#ifndef __UBOOT__ + struct spinand_device *spinand = nand_to_spinand(nand); +#endif + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_markbad(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + return ret; +} + +static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + u8 status; + int ret; + + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_erase_op(spinand, pos); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_ERASE_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_erase(struct mtd_info *mtd, + struct erase_info *einfo) +{ +#ifndef __UBOOT__ + struct spinand_device *spinand = mtd_to_spinand(mtd); +#endif + int ret; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_mtd_erase(mtd, einfo); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) +{ +#ifndef __UBOOT__ + struct spinand_device *spinand = mtd_to_spinand(mtd); +#endif + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_isreserved(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +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->slave, &ops[i])) + return &ops[i]; + } + + return NULL; +} + +static const struct nand_ops spinand_ops = { + .erase = spinand_erase, + .markbad = spinand_markbad, + .isbad = spinand_isbad, +}; + +static int spinand_manufacturer_detect(struct spinand_device *spinand) +{ + return -ENOTSUPP; +} + +static int spinand_manufacturer_init(struct spinand_device *spinand) +{ + if (spinand->manufacturer->ops->init) + return spinand->manufacturer->ops->init(spinand); + + return 0; +} + +static void spinand_manufacturer_cleanup(struct spinand_device *spinand) +{ + /* Release manufacturer private data */ + if (spinand->manufacturer->ops->cleanup) + return spinand->manufacturer->ops->cleanup(spinand); +} + +static const struct spi_mem_op * +spinand_select_op_variant(struct spinand_device *spinand, + const struct spinand_op_variants *variants) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < variants->nops; i++) { + struct spi_mem_op op = variants->ops[i]; + unsigned int nbytes; + int ret; + + nbytes = nanddev_per_page_oobsize(nand) + + nanddev_page_size(nand); + + while (nbytes) { + op.data.nbytes = nbytes; + ret = spi_mem_adjust_op_size(spinand->slave, &op); + if (ret) + break; + + if (!spi_mem_supports_op(spinand->slave, &op)) + break; + + nbytes -= op.data.nbytes; + } + + if (!nbytes) + return &variants->ops[i]; + } + + return NULL; +} + +/** + * spinand_match_and_init() - Try to find a match between a device ID and an + * entry in a spinand_info table + * @spinand: SPI NAND object + * @table: SPI NAND device description table + * @table_size: size of the device description table + * + * Should be used by SPI NAND manufacturer drivers when they want to find a + * match between a device ID retrieved through the READ_ID command and an + * entry in the SPI NAND description table. If a match is found, the spinand + * object will be initialized with information provided by the matching + * spinand_info entry. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, + unsigned int table_size, u8 devid) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < table_size; i++) { + const struct spinand_info *info = &table[i]; + const struct spi_mem_op *op; + + if (devid != info->devid) + continue; + + nand->memorg = table[i].memorg; + nand->eccreq = table[i].eccreq; + spinand->eccinfo = table[i].eccinfo; + spinand->flags = table[i].flags; + spinand->select_target = table[i].select_target; + + op = spinand_select_op_variant(spinand, + info->op_variants.read_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.read_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.write_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.write_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.update_cache); + spinand->op_templates.update_cache = op; + + return 0; + } + + return -ENOTSUPP; +} + +static int spinand_detect(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + ret = spinand_reset_op(spinand); + if (ret) + return ret; + + ret = spinand_read_id_op(spinand, spinand->id.data); + if (ret) + return ret; + + spinand->id.len = SPINAND_MAX_ID_LEN; + + ret = spinand_manufacturer_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, + spinand->id.data); + return ret; + } + + if (nand->memorg.ntargets > 1 && !spinand->select_target) { + dev_err(dev, + "SPI NANDs with more than one die must implement ->select_target()\n"); + return -EINVAL; + } + + dev_info(spinand->slave->dev, + "%s SPI NAND was found.\n", spinand->manufacturer->name); + dev_info(spinand->slave->dev, + "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n", + nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10, + nanddev_page_size(nand), nanddev_per_page_oobsize(nand)); + + return 0; +} + +static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { + .ecc = spinand_noecc_ooblayout_ecc, + .free = spinand_noecc_ooblayout_free, +}; + +static int spinand_init(struct spinand_device *spinand) +{ + struct mtd_info *mtd = spinand_to_mtd(spinand); + struct nand_device *nand = mtd_to_nanddev(mtd); + int ret, i; + + /* + * We need a scratch buffer because the spi_mem interface requires that + * buf passed in spi_mem_op->data.buf be DMA-able. + */ + spinand->scratchbuf = kzalloc(SPINAND_MAX_ID_LEN, GFP_KERNEL); + if (!spinand->scratchbuf) + return -ENOMEM; + + ret = spinand_detect(spinand); + if (ret) + goto err_free_bufs; + + /* + * Use kzalloc() instead of devm_kzalloc() here, because some drivers + * may use this buffer for DMA access. + * Memory allocated by devm_ does not guarantee DMA-safe alignment. + */ + spinand->databuf = kzalloc(nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand), + GFP_KERNEL); + if (!spinand->databuf) { + ret = -ENOMEM; + goto err_free_bufs; + } + + spinand->oobbuf = spinand->databuf + nanddev_page_size(nand); + + ret = spinand_init_cfg_cache(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_init_quad_enable(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0); + if (ret) + goto err_free_bufs; + + ret = spinand_manufacturer_init(spinand); + if (ret) { + dev_err(dev, + "Failed to initialize the SPI NAND chip (err = %d)\n", + ret); + goto err_free_bufs; + } + + /* 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; + + ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); + if (ret) + goto err_free_bufs; + } + + ret = nanddev_init(nand, &spinand_ops, THIS_MODULE); + if (ret) + goto err_manuf_cleanup; + + /* + * Right now, we don't support ECC, so let the whole oob + * area is available for user. + */ + mtd->_read_oob = spinand_mtd_read; + mtd->_write_oob = spinand_mtd_write; + mtd->_block_isbad = spinand_mtd_block_isbad; + mtd->_block_markbad = spinand_mtd_block_markbad; + mtd->_block_isreserved = spinand_mtd_block_isreserved; + mtd->_erase = spinand_mtd_erase; + + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); + + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_nanddev; + + mtd->oobavail = ret; + + return 0; + +err_cleanup_nanddev: + nanddev_cleanup(nand); + +err_manuf_cleanup: + spinand_manufacturer_cleanup(spinand); + +err_free_bufs: + kfree(spinand->databuf); + kfree(spinand->scratchbuf); + return ret; +} + +static void spinand_cleanup(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + nanddev_cleanup(nand); + spinand_manufacturer_cleanup(spinand); + kfree(spinand->databuf); + kfree(spinand->scratchbuf); +} + +static int spinand_probe(struct udevice *dev) +{ + struct spinand_device *spinand = dev_get_priv(dev); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct mtd_info *mtd = dev_get_uclass_priv(dev); + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + +#ifndef __UBOOT__ + spinand = devm_kzalloc(&mem->spi->dev, sizeof(*spinand), + GFP_KERNEL); + if (!spinand) + return -ENOMEM; + + spinand->spimem = mem; + spi_mem_set_drvdata(mem, spinand); + spinand_set_of_node(spinand, mem->spi->dev.of_node); + mutex_init(&spinand->lock); + + mtd = spinand_to_mtd(spinand); + mtd->dev.parent = &mem->spi->dev; +#else + nand->mtd = mtd; + mtd->priv = nand; + mtd->dev = dev; + mtd->name = malloc(20); + if (!mtd->name) + return -ENOMEM; + sprintf(mtd->name, "spi-nand%d", spi_nand_idx++); + spinand->slave = slave; + spinand_set_of_node(spinand, dev->node.np); +#endif + + ret = spinand_init(spinand); + if (ret) + return ret; + +#ifndef __UBOOT__ + ret = mtd_device_register(mtd, NULL, 0); +#else + ret = add_mtd_device(mtd); +#endif + if (ret) + goto err_spinand_cleanup; + + return 0; + +err_spinand_cleanup: + spinand_cleanup(spinand); + + return ret; +} + +#ifndef __UBOOT__ +static int spinand_remove(struct udevice *slave) +{ + struct spinand_device *spinand; + struct mtd_info *mtd; + int ret; + + spinand = spi_mem_get_drvdata(slave); + mtd = spinand_to_mtd(spinand); + free(mtd->name); + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + spinand_cleanup(spinand); + + return 0; +} + +static const struct spi_device_id spinand_ids[] = { + { .name = "spi-nand" }, + { /* sentinel */ }, +}; + +#ifdef CONFIG_OF +static const struct of_device_id spinand_of_ids[] = { + { .compatible = "spi-nand" }, + { /* sentinel */ }, +}; +#endif + +static struct spi_mem_driver spinand_drv = { + .spidrv = { + .id_table = spinand_ids, + .driver = { + .name = "spi-nand", + .of_match_table = of_match_ptr(spinand_of_ids), + }, + }, + .probe = spinand_probe, + .remove = spinand_remove, +}; +module_spi_mem_driver(spinand_drv); + +MODULE_DESCRIPTION("SPI NAND framework"); +MODULE_AUTHOR("Peter Pan"); +MODULE_LICENSE("GPL v2"); +#endif /* __UBOOT__ */ + +static const struct udevice_id spinand_ids[] = { + { .compatible = "spi-nand" }, + { /* sentinel */ }, +}; + +U_BOOT_DRIVER(spinand) = { + .name = "spi_nand", + .id = UCLASS_MTD, + .of_match = spinand_ids, + .priv_auto_alloc_size = sizeof(struct spinand_device), + .probe = spinand_probe, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h new file mode 100644 index 0000000000..ad59fc01ef --- /dev/null +++ b/include/linux/mtd/spinand.h @@ -0,0 +1,427 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ +#ifndef __LINUX_MTD_SPINAND_H +#define __LINUX_MTD_SPINAND_H + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +/** + * Standard SPI NAND flash operations + */ + +#define SPINAND_RESET_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_WR_EN_DIS_OP(enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_READID_OP(ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_SET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, valptr, 1)) + +#define SPINAND_GET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, valptr, 1)) + +#define SPINAND_BLK_ERASE_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 2), \ + SPI_MEM_OP_DUMMY(ndummy, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 4), \ + SPI_MEM_OP_DUMMY(ndummy, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PROG_EXEC_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PROG_LOAD(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 1)) + +#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 4)) + +/** + * Standard SPI NAND flash commands + */ +#define SPINAND_CMD_PROG_LOAD_X4 0x32 +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34 + +/* feature register */ +#define REG_BLOCK_LOCK 0xa0 +#define BL_ALL_UNLOCKED 0x00 + +/* configuration register */ +#define REG_CFG 0xb0 +#define CFG_OTP_ENABLE BIT(6) +#define CFG_ECC_ENABLE BIT(4) +#define CFG_QUAD_ENABLE BIT(0) + +/* status register */ +#define REG_STATUS 0xc0 +#define STATUS_BUSY BIT(0) +#define STATUS_ERASE_FAILED BIT(2) +#define STATUS_PROG_FAILED BIT(3) +#define STATUS_ECC_MASK GENMASK(5, 4) +#define STATUS_ECC_NO_BITFLIPS (0 << 4) +#define STATUS_ECC_HAS_BITFLIPS (1 << 4) +#define STATUS_ECC_UNCOR_ERROR (2 << 4) + +struct spinand_op; +struct spinand_device; + +#define SPINAND_MAX_ID_LEN 4 + +/** + * struct spinand_id - SPI NAND id structure + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length + * + * struct_spinand_id->data contains all bytes returned after a READ_ID command, + * including dummy bytes if the chip does not emit ID bytes right after the + * READ_ID command. The responsibility to extract real ID bytes is left to + * struct_manufacurer_ops->detect(). + */ +struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; +}; + +/** + * struct manufacurer_ops - SPI NAND manufacturer specific operations + * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed + * the core calls the struct_manufacurer_ops->detect() hook of each + * registered manufacturer until one of them return 1. Note that + * the first thing to check in this hook is that the manufacturer ID + * in struct_spinand_device->id matches the manufacturer whose + * ->detect() hook has been called. Should return 1 if there's a + * match, 0 if the manufacturer ID does not match and a negative + * error code otherwise. When true is returned, the core assumes + * that properties of the NAND chip (spinand->base.memorg and + * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that + * NAND chips coming from this vendor can be detected and initialized properly. + */ +struct spinand_manufacturer_ops { + int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); +}; + +/** + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name + * @ops: manufacturer operations + */ +struct spinand_manufacturer { + u8 id; + char *name; + const struct spinand_manufacturer_ops *ops; +}; + +/** + * struct spinand_op_variants - SPI NAND operation variants + * @ops: the list of variants for a given operation + * @nops: the number of variants + * + * Some operations like read-from-cache/write-to-cache have several variants + * depending on the number of IO lines you use to transfer data or address + * cycles. This structure is a way to describe the different variants supported + * by a chip and let the core pick the best one based on the SPI mem controller + * capabilities. + */ +struct spinand_op_variants { + const struct spi_mem_op *ops; + unsigned int nops; +}; + +#define SPINAND_OP_VARIANTS(name, ...) \ + const struct spinand_op_variants name = { \ + .ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \ + .nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \ + sizeof(struct spi_mem_op), \ + } + +/** + * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND + * chip + * @get_status: get the ECC status. Should return a positive number encoding + * the number of corrected bitflips if correction was possible or + * -EBADMSG if there are uncorrectable errors. I can also return + * other negative error codes if the error is not caused by + * uncorrectable bitflips + * @ooblayout: the OOB layout used by the on-die ECC implementation + */ +struct spinand_ecc_info { + int (*get_status)(struct spinand_device *spinand, u8 status); + const struct mtd_ooblayout_ops *ooblayout; +}; + +#define SPINAND_HAS_QE_BIT BIT(0) + +/** + * struct spinand_info - Structure used to describe SPI NAND chips + * @model: model name + * @devid: device ID + * @flags: OR-ing of the SPINAND_XXX flags + * @memorg: memory organization + * @eccreq: ECC requirements + * @eccinfo: on-die ECC info + * @op_variants: operations variants + * @op_variants.read_cache: variants of the read-cache operation + * @op_variants.write_cache: variants of the write-cache operation + * @op_variants.update_cache: variants of the update-cache operation + * @select_target: function used to select a target/die. Required only for + * multi-die chips + * + * Each SPI NAND manufacturer driver should have a spinand_info table + * describing all the chips supported by the driver. + */ +struct spinand_info { + const char *model; + u8 devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct spinand_ecc_info eccinfo; + struct { + const struct spinand_op_variants *read_cache; + const struct spinand_op_variants *write_cache; + const struct spinand_op_variants *update_cache; + } op_variants; + int (*select_target)(struct spinand_device *spinand, + unsigned int target); +}; + +#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ + .write_cache = __write, \ + .update_cache = __update, \ + } + +#define SPINAND_ECCINFO(__ooblayout, __get_status) \ + .eccinfo = { \ + .ooblayout = __ooblayout, \ + .get_status = __get_status, \ + } + +#define SPINAND_SELECT_TARGET(__func) \ + .select_target = __func, + +#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ + __flags, ...) \ + { \ + .model = __model, \ + .devid = __id, \ + .memorg = __memorg, \ + .eccreq = __eccreq, \ + .op_variants = __op_variants, \ + .flags = __flags, \ + __VA_ARGS__ \ + } + +/** + * struct spinand_device - SPI NAND device instance + * @base: NAND device instance + * @slave: pointer to the SPI slave object + * @lock: lock used to serialize accesses to the NAND + * @id: NAND ID as returned by READ_ID + * @flags: NAND flags + * @op_templates: various SPI mem op templates + * @op_templates.read_cache: read cache op template + * @op_templates.write_cache: write cache op template + * @op_templates.update_cache: update cache op template + * @select_target: select a specific target/die. Usually called before sending + * a command addressing a page or an eraseblock embedded in + * this die. Only required if your chip exposes several dies + * @cur_target: currently selected target/die + * @eccinfo: on-die ECC information + * @cfg_cache: config register cache. One entry per die + * @databuf: bounce buffer for data + * @oobbuf: bounce buffer for OOB data + * @scratchbuf: buffer used for everything but page accesses. This is needed + * because the spi-mem interface explicitly requests that buffers + * passed in spi_mem_op be DMA-able, so we can't based the bufs on + * the stack + * @manufacturer: SPI NAND manufacturer information + * @priv: manufacturer private data + */ +struct spinand_device { + struct nand_device base; +#ifndef __UBOOT__ + struct spi_mem *spimem; + struct mutex lock; +#else + struct spi_slave *slave; +#endif + struct spinand_id id; + u32 flags; + + struct { + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; + } op_templates; + + int (*select_target)(struct spinand_device *spinand, + unsigned int target); + unsigned int cur_target; + + struct spinand_ecc_info eccinfo; + + u8 *cfg_cache; + u8 *databuf; + u8 *oobbuf; + u8 *scratchbuf; + const struct spinand_manufacturer *manufacturer; + void *priv; +}; + +/** + * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance + * @mtd: MTD instance + * + * Return: the SPI NAND device attached to @mtd. + */ +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nanddev(mtd), struct spinand_device, base); +} + +/** + * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device + * @spinand: SPI NAND device + * + * Return: the MTD device embedded in @spinand. + */ +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand) +{ + return nanddev_to_mtd(&spinand->base); +} + +/** + * nand_to_spinand() - Get the SPI NAND device embedding an NAND object + * @nand: NAND object + * + * Return: the SPI NAND device embedding @nand. + */ +static inline struct spinand_device *nand_to_spinand(struct nand_device *nand) +{ + return container_of(nand, struct spinand_device, base); +} + +/** + * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object + * @spinand: SPI NAND device + * + * Return: the NAND device embedded in @spinand. + */ +static inline struct nand_device * +spinand_to_nand(struct spinand_device *spinand) +{ + return &spinand->base; +} + +/** + * spinand_set_of_node - Attach a DT node to a SPI NAND device + * @spinand: SPI NAND device + * @np: DT node + * + * Attach a DT node to a SPI NAND device. + */ +static inline void spinand_set_of_node(struct spinand_device *spinand, + const struct device_node *np) +{ + nanddev_set_of_node(&spinand->base, np); +} + +int spinand_match_and_init(struct spinand_device *dev, + const struct spinand_info *table, + unsigned int table_size, u8 devid); + +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_select_target(struct spinand_device *spinand, unsigned int target); + +#endif /* __LINUX_MTD_SPINAND_H */ From patchwork Thu Aug 16 15:30:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958422 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrHF1L54z9s3Z for ; Fri, 17 Aug 2018 01:44:25 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id C7B5EC21E0F; Thu, 16 Aug 2018 15:38:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 3AC08C21EDC; Thu, 16 Aug 2018 15:31:20 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 09B64C21E88; Thu, 16 Aug 2018 15:31:09 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 7B496C21E5B for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 932E42083D; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id C82A0212F2; Thu, 16 Aug 2018 17:30:34 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:13 +0200 Message-Id: <20180816153029.15521-16-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki , Peter Pan Subject: [U-Boot] [PATCH v6 15/31] mtd: spinand: Add initial support for Micron MT29F2G01ABAGD X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Peter Pan Add a basic driver for Micron SPI NANDs. Only one device is supported right now, but the driver will be extended to support more devices afterwards. Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 17 +++++ drivers/mtd/nand/spi/micron.c | 135 ++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 3 + 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/micron.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index f0c6e69d2e..4eb745abd4 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o +spinand-objs := core.o micron.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 08f853ae11..36b8b52bc2 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -829,8 +829,25 @@ static const struct nand_ops spinand_ops = { .isbad = spinand_isbad, }; +static const struct spinand_manufacturer *spinand_manufacturers[] = { + µn_spinand_manufacturer, +}; + static int spinand_manufacturer_detect(struct spinand_device *spinand) { + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { + ret = spinand_manufacturers[i]->ops->detect(spinand); + if (ret > 0) { + spinand->manufacturer = spinand_manufacturers[i]; + return 0; + } else if (ret < 0) { + return ret; + } + } + return -ENOTSUPP; } diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c new file mode 100644 index 0000000000..83951c5d0f --- /dev/null +++ b/drivers/mtd/nand/spi/micron.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_MICRON 0x2c + +#define MICRON_STATUS_ECC_MASK GENMASK(7, 4) +#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) +#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) +#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 64; + region->length = 64; + + return 0; +} + +static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { + .ecc = mt29f2g01abagd_ooblayout_ecc, + .free = mt29f2g01abagd_ooblayout_free, +}; + +static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & MICRON_STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case MICRON_STATUS_ECC_1TO3_BITFLIPS: + return 3; + + case MICRON_STATUS_ECC_4TO6_BITFLIPS: + return 6; + + case MICRON_STATUS_ECC_7TO8_BITFLIPS: + return 8; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info micron_spinand_table[] = { + SPINAND_INFO("MT29F2G01ABAGD", 0x24, + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, + mt29f2g01abagd_ecc_get_status)), +}; + +static int micron_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Micron SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_MICRON) + return 0; + + ret = spinand_match_and_init(spinand, micron_spinand_table, + ARRAY_SIZE(micron_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .detect = micron_spinand_detect, +}; + +const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, + .name = "Micron", + .ops = µn_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index ad59fc01ef..5302d38de8 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -200,6 +200,9 @@ struct spinand_manufacturer { const struct spinand_manufacturer_ops *ops; }; +/* SPI NAND manufacturers */ +extern const struct spinand_manufacturer micron_spinand_manufacturer; + /** * struct spinand_op_variants - SPI NAND operation variants * @ops: the list of variants for a given operation From patchwork Thu Aug 16 15:30:14 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958413 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrBR74V0z9s4s for ; Fri, 17 Aug 2018 01:40:15 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id AA45BC21DDC; Thu, 16 Aug 2018 15:35:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 772E2C21E13; Thu, 16 Aug 2018 15:31:15 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id AA5F4C21DFA; Thu, 16 Aug 2018 15:31:11 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 804ACC21E7F for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 9611620828; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 2FD9F212F3; Thu, 16 Aug 2018 17:30:35 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:14 +0200 Message-Id: <20180816153029.15521-17-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 16/31] mtd: spinand: Add initial support for Winbond W25M02GV X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Frieder Schrempf Add support for the W25M02GV chip. Signed-off-by: Frieder Schrempf Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/winbond.c | 143 +++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/winbond.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 4eb745abd4..11ba5de68b 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o +spinand-objs := core.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 36b8b52bc2..ef3e6445d8 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -831,6 +831,7 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { µn_spinand_manufacturer, + &winbond_spinand_manufacturer, }; static int spinand_manufacturer_detect(struct spinand_device *spinand) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c new file mode 100644 index 0000000000..eac811d97c --- /dev/null +++ b/drivers/mtd/nand/spi/winbond.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 exceet electronics GmbH + * + * Authors: + * Frieder Schrempf + * Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_WINBOND 0xEF + +#define WINBOND_CFG_BUF_READ BIT(3) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 2; + region->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { + .ecc = w25m02gv_ooblayout_ecc, + .free = w25m02gv_ooblayout_free, +}; + +static int w25m02gv_select_target(struct spinand_device *spinand, + unsigned int target) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, + spinand->scratchbuf, + 1)); + + *spinand->scratchbuf = target; + return spi_mem_exec_op(spinand->slave, &op); +} + +static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", 0xAB, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), +}; + +/** + * winbond_spinand_detect - initialize device related part in spinand_device + * struct if it is a Winbond device. + * @spinand: SPI NAND device structure + */ +static int winbond_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Winbond SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_WINBOND) + return 0; + + ret = spinand_match_and_init(spinand, winbond_spinand_table, + ARRAY_SIZE(winbond_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static int winbond_spinand_init(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + /* + * Make sure all dies are in buffer read mode and not continuous read + * mode. + */ + for (i = 0; i < nand->memorg.ntargets; i++) { + spinand_select_target(spinand, i); + spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, + WINBOND_CFG_BUF_READ); + } + + return 0; +} + +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { + .detect = winbond_spinand_detect, + .init = winbond_spinand_init, +}; + +const struct spinand_manufacturer winbond_spinand_manufacturer = { + .id = SPINAND_MFR_WINBOND, + .name = "Winbond", + .ops = &winbond_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5302d38de8..d40a7c8f4e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -202,6 +202,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer winbond_spinand_manufacturer; /** * struct spinand_op_variants - SPI NAND operation variants From patchwork Thu Aug 16 15:30:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958403 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rr7j3NFKz9s4Z for ; Fri, 17 Aug 2018 01:37:53 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 2C689C21E18; Thu, 16 Aug 2018 15:37:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 4B3F4C21E63; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 5A535C21E74; Thu, 16 Aug 2018 15:31:10 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 89F7DC21E88 for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id A1635208C2; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 7D7DC212F4; Thu, 16 Aug 2018 17:30:35 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:15 +0200 Message-Id: <20180816153029.15521-18-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 17/31] mtd: spinand: Add initial support for the MX35LF1GE4AB chip X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Add minimal support for the MX35LF1GE4AB SPI NAND chip. Signed-off-by: Boris Brezillon Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/macronix.c | 138 ++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/macronix.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 11ba5de68b..a66edd9199 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o winbond.o +spinand-objs := core.o macronix.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ef3e6445d8..362d104846 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -830,6 +830,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + ¯onix_spinand_manufacturer, µn_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c new file mode 100644 index 0000000000..dd351dcb6c --- /dev/null +++ b/drivers/mtd/nand/spi/macronix.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Macronix + * + * Author: Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_MACRONIX 0xC2 + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 2; + region->length = mtd->oobsize - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = { + .ecc = mx35lf1ge4ab_ooblayout_ecc, + .free = mx35lf1ge4ab_ooblayout_free, +}; + +static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_DUMMY(1, 1), + SPI_MEM_OP_DATA_IN(1, eccsr, 1)); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 eccsr; + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) + return nand->eccreq.strength; + + if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) + return nand->eccreq.strength; + + return eccsr; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO("MX35LF1GE4AB", 0x12, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +}; + +static int macronix_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Macronix SPI NAND read ID needs a dummy byte, so the first byte in + * raw_id is garbage. + */ + if (id[1] != SPINAND_MFR_MACRONIX) + return 0; + + ret = spinand_match_and_init(spinand, macronix_spinand_table, + ARRAY_SIZE(macronix_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { + .detect = macronix_spinand_detect, +}; + +const struct spinand_manufacturer macronix_spinand_manufacturer = { + .id = SPINAND_MFR_MACRONIX, + .name = "Macronix", + .ops = ¯onix_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index d40a7c8f4e..8c9c756179 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -201,6 +201,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; From patchwork Thu Aug 16 15:30:16 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958411 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrB51TmNz9s4Z for ; Fri, 17 Aug 2018 01:39:57 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id C808CC21E26; Thu, 16 Aug 2018 15:38:36 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 6FC29C21EC9; Thu, 16 Aug 2018 15:31:19 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 99D05C21E73; Thu, 16 Aug 2018 15:31:11 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 87AACC21E85 for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 9F5B420890; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id C3BFC212F5; Thu, 16 Aug 2018 17:30:35 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:16 +0200 Message-Id: <20180816153029.15521-19-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 18/31] mtd: spinand: Add initial support for the MX35LF2GE4AB chip X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Add support for the MX35LF2GE4AB chip, which is similar to its cousin MX35LF1GE4AB, with two planes instead of one. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/macronix.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index dd351dcb6c..662c561e50 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -27,13 +27,13 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, +static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { return -ERANGE; } -static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, +static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -45,9 +45,9 @@ static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = { - .ecc = mx35lf1ge4ab_ooblayout_ecc, - .free = mx35lf1ge4ab_ooblayout_free, +static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { + .ecc = mx35lfxge4ab_ooblayout_ecc, + .free = mx35lfxge4ab_ooblayout_free, }; static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) @@ -102,8 +102,16 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF2GE4AB", 0x22, + NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; static int macronix_spinand_detect(struct spinand_device *spinand) From patchwork Thu Aug 16 15:30:17 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958429 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrMb1dqJz9s3Z for ; Fri, 17 Aug 2018 01:48:11 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id D243EC21E02; Thu, 16 Aug 2018 15:40:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 364A3C21D8A; Thu, 16 Aug 2018 15:31:24 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 7876AC21E62; Thu, 16 Aug 2018 15:31:11 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id B53DDC21E7E for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id D36CF20DBD; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 189EB2130B; Thu, 16 Aug 2018 17:30:36 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:17 +0200 Message-Id: <20180816153029.15521-20-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 19/31] dt-bindings: Add bindings for SPI NAND devices X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Boris Brezillon Add bindings for SPI NAND chips. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- doc/device-tree-bindings/mtd/spi-nand.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/device-tree-bindings/mtd/spi-nand.txt diff --git a/doc/device-tree-bindings/mtd/spi-nand.txt b/doc/device-tree-bindings/mtd/spi-nand.txt new file mode 100644 index 0000000000..8b51f3b6d5 --- /dev/null +++ b/doc/device-tree-bindings/mtd/spi-nand.txt @@ -0,0 +1,5 @@ +SPI NAND flash + +Required properties: +- compatible: should be "spi-nand" +- reg: should encode the chip-select line used to access the NAND chip From patchwork Thu Aug 16 15:30:18 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958435 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrQg63glz9s3Z for ; Fri, 17 Aug 2018 01:50:51 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 7D21AC21DFD; Thu, 16 Aug 2018 15:43:25 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id C135CC21E75; Thu, 16 Aug 2018 15:31:29 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 4DC8EC21E88; Thu, 16 Aug 2018 15:31:16 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id BB8ECC21DF8 for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id DA2F120EB9; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 625602130E; Thu, 16 Aug 2018 17:30:36 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:18 +0200 Message-Id: <20180816153029.15521-21-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 20/31] mtd: declare MTD_PARTITIONS symbol in Kconfig X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" UBI selects MTD_PARTITIONS which is the symbol to compile drivers/mtd/mtdpart.c. Unfortunately, the symbol was not defined in Kconfig and this worked only with board files defining it. Fix this by adding a boolean in Kconfig so boards defined by defconfig files only will work as expected. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 9341d518f3..d98457e223 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,5 +1,8 @@ menu "MTD Support" +config MTD_PARTITIONS + bool + config MTD bool "Enable Driver Model for MTD drivers" depends on DM From patchwork Thu Aug 16 15:30:19 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958443 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrTP5XGBz9s3Z for ; Fri, 17 Aug 2018 01:53:13 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 6BEABC21E02; Thu, 16 Aug 2018 15:45:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 73ADAC21F3E; Thu, 16 Aug 2018 15:31:35 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id A9450C21E2C; Thu, 16 Aug 2018 15:31:16 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id C3D6FC21E42 for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id DF0C420F25; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id AD4C721310; Thu, 16 Aug 2018 17:30:36 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:19 +0200 Message-Id: <20180816153029.15521-22-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 21/31] mtd: mtdpart: balance debug messages X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Balance debug message in the partition allocation/removal process in order to keep track of them more easily. Signed-off-by: Miquel Raynal --- drivers/mtd/mtdpart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ccbb1757ea..9ccb1b3361 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -362,6 +362,8 @@ int del_mtd_partitions(struct mtd_info *master) struct mtd_part *slave, *next; int ret, err = 0; + debug("Deleting MTD partitions on \"%s\":\n", master->name); + mutex_lock(&mtd_partitions_mutex); list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { From patchwork Thu Aug 16 15:30:20 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958448 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrVT419Lz9s4s for ; Fri, 17 Aug 2018 01:54:09 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 2D634C21C57; Thu, 16 Aug 2018 15:43:46 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id E5F4EC21F1C; Thu, 16 Aug 2018 15:31:30 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id B070DC21E88; Thu, 16 Aug 2018 15:31:16 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id C5050C21E90 for ; Thu, 16 Aug 2018 15:31:01 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id E49EB20F71; Thu, 16 Aug 2018 17:31:00 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 049BA2140B; Thu, 16 Aug 2018 17:30:37 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:20 +0200 Message-Id: <20180816153029.15521-23-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 22/31] cmd: ubi: delete useless and misleading definitions X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" These definitions are simply not used and are misleading because similar definitions exist in jffs2/load_kernel.h and are used widely to define MTD device types (which is, by the way, totally redundant with what the MTD core does). Remove these definitions. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Boris Brezillon --- cmd/ubi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/ubi.c b/cmd/ubi.c index 913f0f71fd..0a3405a3b1 100644 --- a/cmd/ubi.c +++ b/cmd/ubi.c @@ -27,11 +27,6 @@ #undef ubi_msg #define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__) -#define DEV_TYPE_NONE 0 -#define DEV_TYPE_NAND 1 -#define DEV_TYPE_ONENAND 2 -#define DEV_TYPE_NOR 3 - /* Private own data */ static struct ubi_device *ubi; static char buffer[80]; From patchwork Thu Aug 16 15:30:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958427 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrKb5SGvz9rvt for ; Fri, 17 Aug 2018 01:46:27 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 104F0C21E13; Thu, 16 Aug 2018 15:42:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 8EECFC21E7F; Thu, 16 Aug 2018 15:31:27 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 01806C21E50; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 111E4C21E63 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 2EC4820F73; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 4BE8F21512; Thu, 16 Aug 2018 17:30:37 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:21 +0200 Message-Id: <20180816153029.15521-24-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 23/31] cmd: mtdparts: add fallthrough in switch statement X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Switch blocks for deriving size naturally use fallthrough between 'case' statements. Make it explicit. Signed-off-by: Miquel Raynal --- cmd/mtdparts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 0da3afd75f..756fc6018f 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -177,13 +177,16 @@ static u64 memsize_parse (const char *const ptr, const char **retptr) case 'G': case 'g': ret <<= 10; + /* Fallthrough */ case 'M': case 'm': ret <<= 10; + /* Fallthrough */ case 'K': case 'k': ret <<= 10; (*retptr)++; + /* Fallthrough */ default: break; } From patchwork Thu Aug 16 15:30:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958439 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrRR4vY2z9s3Z for ; Fri, 17 Aug 2018 01:51:31 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 6C09FC21DF8; Thu, 16 Aug 2018 15:42:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 7ED03C21EF7; Thu, 16 Aug 2018 15:31:26 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 7DAF3C21E02; Thu, 16 Aug 2018 15:31:16 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 17BF4C21E70 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 31B5120F77; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 9853F21513; Thu, 16 Aug 2018 17:30:37 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:22 +0200 Message-Id: <20180816153029.15521-25-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 24/31] cmd: mtdparts: accept spi-nand devices X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Let spi-nand devices be recognized by mtdparts. This is superfluous but a full mtdparts rework would be very time-consuming. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Boris Brezillon --- cmd/mtdparts.c | 13 ++++++++----- include/jffs2/load_kernel.h | 7 +++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 756fc6018f..2e547894c6 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -37,7 +37,7 @@ * mtdids=[,,...] * * := = - * := 'nand'|'nor'|'onenand' + * := 'nand'|'nor'|'onenand'|'spi-nand' * := mtd device number, 0... * := unique device tag used by linux kernel to find mtd device (mtd->name) * @@ -339,7 +339,7 @@ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) if (!mtd->numeraseregions) { /* - * Only one eraseregion (NAND, OneNAND or uniform NOR), + * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR), * checking for alignment is easy here */ offset = part->offset; @@ -1030,7 +1030,7 @@ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_ } /** - * Parse device id string := 'nand'|'nor'|'onenand', + * Parse device id string := 'nand'|'nor'|'onenand'|'spi-nand', * return device type and number. * * @param id string describing device id @@ -1054,6 +1054,9 @@ int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, } else if (strncmp(p, "onenand", 7) == 0) { *dev_type = MTD_DEV_TYPE_ONENAND; p += 7; + } else if (strncmp(p, "spi-nand", 8) == 0) { + *dev_type = MTD_DEV_TYPE_SPINAND; + p += 8; } else { printf("incorrect device type in %s\n", id); return 1; @@ -1636,7 +1639,7 @@ static int parse_mtdids(const char *const ids) while(p && (*p != '\0')) { ret = 1; - /* parse 'nor'|'nand'|'onenand' */ + /* parse 'nor'|'nand'|'onenand'|'spi-nand' */ if (mtd_id_parse(p, &p, &type, &num) != 0) break; @@ -2112,7 +2115,7 @@ static char mtdparts_help_text[] = "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" "mtdids=[,,...]\n\n" " := =\n" - " := 'nand'|'nor'|'onenand'\n" + " := 'nand'|'nor'|'onenand'|'spi-nand'\n" " := mtd device number, 0...\n" " := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" "'mtdparts' - partition list\n\n" diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index 1ddff062ad..9346d7ee9f 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -15,9 +15,12 @@ #define MTD_DEV_TYPE_NOR 0x0001 #define MTD_DEV_TYPE_NAND 0x0002 #define MTD_DEV_TYPE_ONENAND 0x0004 +#define MTD_DEV_TYPE_SPINAND 0x0008 -#define MTD_DEV_TYPE(type) ((type == MTD_DEV_TYPE_NAND) ? "nand" : \ - (type == MTD_DEV_TYPE_ONENAND) ? "onenand" : "nor") +#define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" : \ + (type == MTD_DEV_TYPE_NOR ? "nor" : \ + (type == MTD_DEV_TYPE_ONENAND ? "onenand" : \ + "spi-nand"))) \ struct mtd_device { struct list_head link; From patchwork Thu Aug 16 15:30:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958431 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrNX2KHfz9s3Z for ; Fri, 17 Aug 2018 01:49:00 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 13A2EC21DEC; Thu, 16 Aug 2018 15:45:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 4BD4EC21E08; Thu, 16 Aug 2018 15:31:34 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 3B22EC21E7E; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 390EBC21E96 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 57D68212C4; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id E4F422158C; Thu, 16 Aug 2018 17:30:37 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:23 +0200 Message-Id: <20180816153029.15521-26-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 25/31] cmd: mtdparts: add a generic 'mtdparts' parser X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The current parser is very specific to U-Boot mtdparts implementation. It does not use MTD structures like mtd_info and mtd_partition. Write some kind of a wrapper around the current implementation to allow other commands to benefit from this parsing in a user-friendly way. This new command will allocate an mtd_partition array for each successful call. This array must be freed after use by the caller. The given 'mtdparts' buffer pointer will be moved forward to the next MTD device (if any, it will point towards a '\0' character otherwise). Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- cmd/mtdparts.c | 71 ++++++++++++++++++++++++++++++++++ include/linux/mtd/partitions.h | 3 ++ 2 files changed, 74 insertions(+) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 2e547894c6..7fd9e5cbdb 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -78,6 +78,7 @@ #include #include #include +#include #if defined(CONFIG_CMD_NAND) #include @@ -708,6 +709,76 @@ static int part_parse(const char *const partdef, const char **ret, struct part_i return 0; } +int mtdparts_parse_part(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nb_parts) +{ + const char *mtdparts = *_mtdparts; + struct part_info *part_legacy; + struct mtd_partition *parts; + int cur_off = 0, cur_sz = 0; + int nb_parts = 0; + char *names; + int ret, idx; + + *_parts = NULL; + *_nb_parts = 0; + + /* First, iterate over the partitions until we know their number */ + while (mtdparts[0] != '\0' && mtdparts[0] != ';') { + ret = part_parse(mtdparts, &mtdparts, &part_legacy); + if (ret) + return ret; + + nb_parts++; + free(part_legacy); + } + + /* Allocate an array of partitions to give back to the caller */ + parts = malloc((sizeof(*parts) + 20) * nb_parts); + names = (char *)&parts[nb_parts]; + if (!parts) { + printf("Could not allocate enough space to save partitions meta-data\n"); + return -ENOMEM; + } + + /* Iterate again over each partition to save the data in our array */ + for (idx = 0; idx < nb_parts; idx++) { + char *name; + + ret = part_parse(*_mtdparts, _mtdparts, &part_legacy); + if (ret) + return ret; + + name = &names[idx * 20]; + strncpy(name, part_legacy->name, 20); + parts[idx].name = name; + + parts[idx].size = part_legacy->size; + if (parts[idx].size == SIZE_REMAINING) + parts[idx].size = parent->size - cur_sz; + cur_sz += parts[idx].size; + + parts[idx].offset = part_legacy->offset; + if (parts[idx].offset == OFFSET_NOT_SPECIFIED) + parts[idx].offset = cur_off; + cur_off += parts[idx].size; + + parts[idx].mask_flags = part_legacy->mask_flags; + parts[idx].ecclayout = parent->ecclayout; + + free(part_legacy); + } + + /* Offset by one mtdparts to point to the next device if needed */ + if (*_mtdparts[0] == ';') + _mtdparts++; + + *_parts = parts; + *_nb_parts = nb_parts; + + return 0; +} + /** * Check device number to be within valid range for given device type. * diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index ce0e8dbee4..0cf26ca945 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -87,4 +87,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name, int mtd_del_partition(struct mtd_info *master, int partno); uint64_t mtd_get_device_size(const struct mtd_info *mtd); +int mtdparts_parse_part(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nb_parts); + #endif From patchwork Thu Aug 16 15:30:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958440 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrRl4ZqPz9s3Z for ; Fri, 17 Aug 2018 01:51:47 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 4B959C21E30; Thu, 16 Aug 2018 15:46:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 42D4DC21F51; Thu, 16 Aug 2018 15:31:39 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id D53C3C21ECF; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 3EF9CC21E9F for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 59BC52090A; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 3AD5A215C7; Thu, 16 Aug 2018 17:30:38 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:24 +0200 Message-Id: <20180816153029.15521-27-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 26/31] mtd: uclass: add probe function X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The user might want to trigger the probe of any MTD device, export these functions so they can be called from a command source file. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/mtd-uclass.c | 9 +++++++++ include/linux/mtd/mtd.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index 9ca049c437..9a9470410c 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -13,6 +14,14 @@ * The uclass private is pointed to mtd_info. */ +int mtd_probe(struct udevice *dev) +{ + if (device_active(dev)) + return 0; + + return device_probe(dev); +} + UCLASS_DRIVER(mtd) = { .id = UCLASS_MTD, .name = "mtd", diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b8c2c3fd59..755c785416 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -549,5 +549,8 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, const uint64_t length, uint64_t *len_incl_bad, int *truncated); + +int mtd_probe(struct udevice *dev); + #endif #endif /* __MTD_MTD_H__ */ From patchwork Thu Aug 16 15:30:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958446 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrTt714Pz9s3Z for ; Fri, 17 Aug 2018 01:53:38 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id B613DC21E2B; Thu, 16 Aug 2018 15:44:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 3917CC21F2F; Thu, 16 Aug 2018 15:31:33 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 0FB2DC21E88; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 7F08DC21E68 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id 9BB84212F4; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 86466215C9; Thu, 16 Aug 2018 17:30:38 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:25 +0200 Message-Id: <20180816153029.15521-28-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 27/31] mtd: move mtdparts_init() declaration X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" mtdparts_init() is called from various source files. It is declared in include/jffs2/load_kernel.h while it has nothing to do with jffs2 anymore. Move its declaration to include/linux/mtd/partitions.h which has way more meaning. Signed-off-by: Miquel Raynal --- cmd/flash.c | 1 + cmd/jffs2.c | 1 + cmd/mtdparts.c | 35 +++++++++++++++++++++++----------- cmd/nand.c | 1 + common/fdt_support.c | 1 + drivers/dfu/dfu_nand.c | 1 + drivers/fastboot/fb_nand.c | 1 + drivers/mtd/mtd_uboot.c | 1 + include/jffs2/load_kernel.h | 1 - include/linux/mtd/partitions.h | 3 +++ 10 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd/flash.c b/cmd/flash.c index cd1758d7e2..383c015b87 100644 --- a/cmd/flash.c +++ b/cmd/flash.c @@ -12,6 +12,7 @@ #if defined(CONFIG_CMD_MTDPARTS) #include +#include /* partition handling routines */ int mtdparts_init(void); diff --git a/cmd/jffs2.c b/cmd/jffs2.c index 64621f2546..0ca2d30e2e 100644 --- a/cmd/jffs2.c +++ b/cmd/jffs2.c @@ -76,6 +76,7 @@ #include #include #include +#include #include #if defined(CONFIG_CMD_NAND) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 7fd9e5cbdb..33becb86e8 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -44,7 +44,7 @@ * * 'mtdparts' - partition list * - * mtdparts=mtdparts=[;...] + * mtdparts=[mtdparts=][;...] * * := :[,...] * := unique device tag used by linux kernel to find mtd device (mtd->name) @@ -62,11 +62,11 @@ * * 1 NOR Flash, with 1 single writable partition: * mtdids=nor0=edb7312-nor - * mtdparts=mtdparts=edb7312-nor:- + * mtdparts=[mtdparts=]edb7312-nor:- * * 1 NOR Flash with 2 partitions, 1 NAND with one * mtdids=nor0=edb7312-nor,nand0=edb7312-nand - * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) * */ @@ -1170,9 +1170,6 @@ static int generate_mtdparts(char *buf, u32 buflen) return 0; } - strcpy(p, "mtdparts="); - p += 9; - list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); @@ -1643,11 +1640,9 @@ static int parse_mtdparts(const char *const mtdparts) if (!p) p = mtdparts; - if (strncmp(p, "mtdparts=", 9) != 0) { - printf("mtdparts variable doesn't start with 'mtdparts='\n"); - return err; - } - p += 9; + /* Skip the useless prefix, if any */ + if (strncmp(p, "mtdparts=", 9) == 0) + p += 9; while (*p != '\0') { err = 1; @@ -1786,6 +1781,24 @@ static int parse_mtdids(const char *const ids) return 0; } +int mtdids_search_alternate_name(const char *mtdname, char *altname, + unsigned int max_len) +{ + struct list_head *entry; + struct mtdids *id; + + list_for_each(entry, &mtdids) { + id = list_entry(entry, struct mtdids, link); + if (!strncmp(mtdname, id->mtd_id, max_len)) { + snprintf(altname, max_len, "%s%d", + MTD_DEV_TYPE(id->type), id->num); + + return 0; + } + } + + return -EINVAL; +} /** * Parse and initialize global mtdids mapping and create global diff --git a/cmd/nand.c b/cmd/nand.c index a22945d144..507966c513 100644 --- a/cmd/nand.c +++ b/cmd/nand.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/common/fdt_support.c b/common/fdt_support.c index 34d2bd59c4..3906b7446d 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/dfu/dfu_nand.c b/drivers/dfu/dfu_nand.c index 0bfdbf9428..a3164491eb 100644 --- a/drivers/dfu/dfu_nand.c +++ b/drivers/dfu/dfu_nand.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/fastboot/fb_nand.c b/drivers/fastboot/fb_nand.c index 526bc12307..776f628712 100644 --- a/drivers/fastboot/fb_nand.c +++ b/drivers/fastboot/fb_nand.c @@ -11,6 +11,7 @@ #include #include +#include #include #include diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index 2b3b2eecca..55cdde284c 100644 --- a/drivers/mtd/mtd_uboot.c +++ b/drivers/mtd/mtd_uboot.c @@ -5,6 +5,7 @@ */ #include #include +#include #include static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index 9346d7ee9f..3ef56ab0c5 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -62,7 +62,6 @@ struct mtdids { #define led_blink(x, y, z, a) /* common/cmd_jffs2.c */ -extern int mtdparts_init(void); extern int find_dev_and_part(const char *id, struct mtd_device **dev, u8 *part_num, struct part_info **part); extern struct mtd_device *device_find(u8 type, u8 num); diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 0cf26ca945..0d6e0cfa59 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -87,6 +87,9 @@ int mtd_add_partition(struct mtd_info *master, const char *name, int mtd_del_partition(struct mtd_info *master, int partno); uint64_t mtd_get_device_size(const struct mtd_info *mtd); +int mtdparts_init(void); +int mtdids_search_alternate_name(const char *mtdname, char *altname, + unsigned int max_len); int mtdparts_parse_part(struct mtd_info *parent, const char **_mtdparts, struct mtd_partition **_parts, int *_nb_parts); From patchwork Thu Aug 16 15:30:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958434 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrPZ3kqlz9s4s for ; Fri, 17 Aug 2018 01:49:54 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 25E68C21C38; Thu, 16 Aug 2018 15:46:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 33F18C21F6D; Thu, 16 Aug 2018 15:31:42 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 18077C21DD7; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 8B27BC21E50 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id A7B0E212F5; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id D0374215CB; Thu, 16 Aug 2018 17:30:38 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:26 +0200 Message-Id: <20180816153029.15521-29-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 28/31] mtd: mtdpart: implement proper partition handling X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Instead of collecting partitions in a flat list, create a hierarchy within the mtd_info structure: use a partitions list to keep track of the partitions of an MTD device (which might be itself a partition of another MTD device), a pointer to the parent device (NULL when the MTD device is the root one, not a partition). By also saving directly in mtd_info the offset of the partition, we can get rid of the mtd_part structure. Signed-off-by: Miquel Raynal --- drivers/mtd/mtd-uclass.c | 13 +- drivers/mtd/mtdpart.c | 385 +++++++++++++-------------------- include/linux/mtd/mtd.h | 19 ++ include/linux/mtd/partitions.h | 1 - 4 files changed, 185 insertions(+), 233 deletions(-) diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index 9a9470410c..348ba847d4 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -16,10 +16,21 @@ int mtd_probe(struct udevice *dev) { + struct mtd_info *mtd; + int ret; + if (device_active(dev)) return 0; - return device_probe(dev); + ret = device_probe(dev); + if (ret) + return ret; + + mtd = dev_get_uclass_priv(dev); + INIT_LIST_HEAD(&mtd->partitions); + INIT_LIST_HEAD(&mtd->node); + + return 0; } UCLASS_DRIVER(mtd) = { diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9ccb1b3361..e4d4858e13 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -29,29 +29,12 @@ #include "mtdcore.h" -/* Our partition linked list */ -static LIST_HEAD(mtd_partitions); #ifndef __UBOOT__ static DEFINE_MUTEX(mtd_partitions_mutex); #else DEFINE_MUTEX(mtd_partitions_mutex); #endif -/* Our partition node structure */ -struct mtd_part { - struct mtd_info mtd; - struct mtd_info *master; - uint64_t offset; - struct list_head list; -}; - -/* - * Given a pointer to the MTD object in the mtd_part structure, we can retrieve - * the pointer to that structure with this macro. - */ -#define PART(x) ((struct mtd_part *)(x)) - - #ifdef __UBOOT__ /* from mm/util.c */ @@ -84,19 +67,18 @@ char *kstrdup(const char *s, gfp_t gfp) static int part_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); struct mtd_ecc_stats stats; int res; - stats = part->master->ecc_stats; - res = part->master->_read(part->master, from + part->offset, len, - retlen, buf); + stats = mtd->parent->ecc_stats; + res = mtd->parent->_read(mtd->parent, from + mtd->offset, len, + retlen, buf); if (unlikely(mtd_is_eccerr(res))) mtd->ecc_stats.failed += - part->master->ecc_stats.failed - stats.failed; + mtd->parent->ecc_stats.failed - stats.failed; else mtd->ecc_stats.corrected += - part->master->ecc_stats.corrected - stats.corrected; + mtd->parent->ecc_stats.corrected - stats.corrected; return res; } @@ -104,17 +86,13 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, static int part_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { - struct mtd_part *part = PART(mtd); - - return part->master->_point(part->master, from + part->offset, len, - retlen, virt, phys); + return mtd->parent->_point(mtd->parent, from + mtd->offset, len, + retlen, virt, phys); } static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) { - struct mtd_part *part = PART(mtd); - - return part->master->_unpoint(part->master, from + part->offset, len); + return mtd->parent->_unpoint(mtd->parent, from + mtd->offset, len); } #endif @@ -123,17 +101,13 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd, unsigned long offset, unsigned long flags) { - struct mtd_part *part = PART(mtd); - - offset += part->offset; - return part->master->_get_unmapped_area(part->master, len, offset, - flags); + offset += mtd->offset; + return mtd->parent->_get_unmapped_area(mtd->parent, len, offset, flags); } static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct mtd_part *part = PART(mtd); int res; if (from >= mtd->size) @@ -158,7 +132,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - res = part->master->_read_oob(part->master, from + part->offset, ops); + res = mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops); if (unlikely(res)) { if (mtd_is_bitflip(res)) mtd->ecc_stats.corrected++; @@ -171,99 +145,87 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_read_user_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_read_user_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_get_user_prot_info(part->master, len, retlen, - buf); + return mtd->parent->_get_user_prot_info(mtd->parent, len, retlen, + buf); } static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_read_fact_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_read_fact_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_get_fact_prot_info(part->master, len, retlen, - buf); + return mtd->parent->_get_fact_prot_info(mtd->parent, len, retlen, + buf); } static int part_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_write(part->master, to + part->offset, len, - retlen, buf); + return mtd->parent->_write(mtd->parent, to + mtd->offset, len, + retlen, buf); } static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_panic_write(part->master, to + part->offset, len, - retlen, buf); + return mtd->parent->_panic_write(mtd->parent, to + mtd->offset, len, + retlen, buf); } static int part_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - struct mtd_part *part = PART(mtd); - if (to >= mtd->size) return -EINVAL; if (ops->datbuf && to + ops->len > mtd->size) return -EINVAL; - return part->master->_write_oob(part->master, to + part->offset, ops); + return mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops); } static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_write_user_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_write_user_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_lock_user_prot_reg(part->master, from, len); + return mtd->parent->_lock_user_prot_reg(mtd->parent, from, len); } #ifndef __UBOOT__ static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { - struct mtd_part *part = PART(mtd); - return part->master->_writev(part->master, vecs, count, - to + part->offset, retlen); + return mtd->parent->_writev(mtd->parent, vecs, count, + to + mtd->offset, retlen); } #endif static int part_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct mtd_part *part = PART(mtd); int ret; - instr->addr += part->offset; - ret = part->master->_erase(part->master, instr); + instr->addr += mtd->offset; + ret = mtd->parent->_erase(mtd->parent, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; + instr->fail_addr -= mtd->offset; + instr->addr -= mtd->offset; } return ret; } @@ -271,11 +233,9 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) void mtd_erase_callback(struct erase_info *instr) { if (instr->mtd->_erase == part_erase) { - struct mtd_part *part = PART(instr->mtd); - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; + instr->fail_addr -= instr->mtd->offset; + instr->addr -= instr->mtd->offset; } if (instr->callback) instr->callback(instr); @@ -284,71 +244,62 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback); static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_lock(part->master, ofs + part->offset, len); + return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len); } static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_unlock(part->master, ofs + part->offset, len); + return mtd->parent->_unlock(mtd->parent, ofs + mtd->offset, len); } static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_is_locked(part->master, ofs + part->offset, len); + return mtd->parent->_is_locked(mtd->parent, ofs + mtd->offset, len); } static void part_sync(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - part->master->_sync(part->master); + mtd->parent->_sync(mtd->parent); } #ifndef __UBOOT__ static int part_suspend(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - return part->master->_suspend(part->master); + return mtd->parent->_suspend(mtd->parent); } static void part_resume(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - part->master->_resume(part->master); + mtd->parent->_resume(mtd->parent); } #endif static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); - ofs += part->offset; - return part->master->_block_isreserved(part->master, ofs); + ofs += mtd->offset; + return mtd->parent->_block_isreserved(mtd->parent, ofs); } static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); - ofs += part->offset; - return part->master->_block_isbad(part->master, ofs); + ofs += mtd->offset; + return mtd->parent->_block_isbad(mtd->parent, ofs); } static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); int res; - ofs += part->offset; - res = part->master->_block_markbad(part->master, ofs); + ofs += mtd->offset; + res = mtd->parent->_block_markbad(mtd->parent, ofs); if (!res) mtd->ecc_stats.badblocks++; return res; } -static inline void free_partition(struct mtd_part *p) +static inline void free_partition(struct mtd_info *p) { - kfree(p->mtd.name); + kfree(p->name); kfree(p); } @@ -359,32 +310,31 @@ static inline void free_partition(struct mtd_part *p) int del_mtd_partitions(struct mtd_info *master) { - struct mtd_part *slave, *next; + struct mtd_info *slave, *next; int ret, err = 0; debug("Deleting MTD partitions on \"%s\":\n", master->name); mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if (slave->master == master) { - ret = del_mtd_device(&slave->mtd); - if (ret < 0) { - err = ret; - continue; - } - list_del(&slave->list); - free_partition(slave); + list_for_each_entry_safe(slave, next, &master->partitions, node) { + ret = del_mtd_device(slave); + if (ret < 0) { + err = ret; + continue; } + list_del(&slave->node); + free_partition(slave); + } mutex_unlock(&mtd_partitions_mutex); return err; } -static struct mtd_part *allocate_partition(struct mtd_info *master, - const struct mtd_partition *part, int partno, - uint64_t cur_offset) +static struct mtd_info *allocate_partition(struct mtd_info *master, + const struct mtd_partition *part, + int partno, uint64_t cur_offset) { - struct mtd_part *slave; + struct mtd_info *slave; char *name; /* allocate the partition structure */ @@ -399,85 +349,87 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } /* set up the MTD object for this partition */ - slave->mtd.type = master->type; - slave->mtd.flags = master->flags & ~part->mask_flags; - slave->mtd.size = part->size; - slave->mtd.writesize = master->writesize; - slave->mtd.writebufsize = master->writebufsize; - slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; - slave->mtd.subpage_sft = master->subpage_sft; + slave->type = master->type; + slave->flags = master->flags & ~part->mask_flags; + slave->size = part->size; + slave->writesize = master->writesize; + slave->writebufsize = master->writebufsize; + slave->oobsize = master->oobsize; + slave->oobavail = master->oobavail; + slave->subpage_sft = master->subpage_sft; - slave->mtd.name = name; - slave->mtd.owner = master->owner; + slave->name = name; + slave->owner = master->owner; #ifndef __UBOOT__ - slave->mtd.backing_dev_info = master->backing_dev_info; + slave->backing_dev_info = master->backing_dev_info; /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone * to have the same data be in two different partitions. */ - slave->mtd.dev.parent = master->dev.parent; + slave->dev.parent = master->dev.parent; #endif if (master->_read) - slave->mtd._read = part_read; + slave->_read = part_read; if (master->_write) - slave->mtd._write = part_write; + slave->_write = part_write; if (master->_panic_write) - slave->mtd._panic_write = part_panic_write; + slave->_panic_write = part_panic_write; #ifndef __UBOOT__ if (master->_point && master->_unpoint) { - slave->mtd._point = part_point; - slave->mtd._unpoint = part_unpoint; + slave->_point = part_point; + slave->_unpoint = part_unpoint; } #endif if (master->_get_unmapped_area) - slave->mtd._get_unmapped_area = part_get_unmapped_area; + slave->_get_unmapped_area = part_get_unmapped_area; if (master->_read_oob) - slave->mtd._read_oob = part_read_oob; + slave->_read_oob = part_read_oob; if (master->_write_oob) - slave->mtd._write_oob = part_write_oob; + slave->_write_oob = part_write_oob; if (master->_read_user_prot_reg) - slave->mtd._read_user_prot_reg = part_read_user_prot_reg; + slave->_read_user_prot_reg = part_read_user_prot_reg; if (master->_read_fact_prot_reg) - slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; + slave->_read_fact_prot_reg = part_read_fact_prot_reg; if (master->_write_user_prot_reg) - slave->mtd._write_user_prot_reg = part_write_user_prot_reg; + slave->_write_user_prot_reg = part_write_user_prot_reg; if (master->_lock_user_prot_reg) - slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; + slave->_lock_user_prot_reg = part_lock_user_prot_reg; if (master->_get_user_prot_info) - slave->mtd._get_user_prot_info = part_get_user_prot_info; + slave->_get_user_prot_info = part_get_user_prot_info; if (master->_get_fact_prot_info) - slave->mtd._get_fact_prot_info = part_get_fact_prot_info; + slave->_get_fact_prot_info = part_get_fact_prot_info; if (master->_sync) - slave->mtd._sync = part_sync; + slave->_sync = part_sync; #ifndef __UBOOT__ if (!partno && !master->dev.class && master->_suspend && master->_resume) { - slave->mtd._suspend = part_suspend; - slave->mtd._resume = part_resume; + slave->_suspend = part_suspend; + slave->_resume = part_resume; } if (master->_writev) - slave->mtd._writev = part_writev; + slave->_writev = part_writev; #endif if (master->_lock) - slave->mtd._lock = part_lock; + slave->_lock = part_lock; if (master->_unlock) - slave->mtd._unlock = part_unlock; + slave->_unlock = part_unlock; if (master->_is_locked) - slave->mtd._is_locked = part_is_locked; + slave->_is_locked = part_is_locked; if (master->_block_isreserved) - slave->mtd._block_isreserved = part_block_isreserved; + slave->_block_isreserved = part_block_isreserved; if (master->_block_isbad) - slave->mtd._block_isbad = part_block_isbad; + slave->_block_isbad = part_block_isbad; if (master->_block_markbad) - slave->mtd._block_markbad = part_block_markbad; - slave->mtd._erase = part_erase; - slave->master = master; + slave->_block_markbad = part_block_markbad; + slave->_erase = part_erase; + slave->parent = master; slave->offset = part->offset; + INIT_LIST_HEAD(&slave->partitions); + INIT_LIST_HEAD(&slave->node); if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; @@ -493,41 +445,41 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } if (slave->offset == MTDPART_OFS_RETAIN) { slave->offset = cur_offset; - if (master->size - slave->offset >= slave->mtd.size) { - slave->mtd.size = master->size - slave->offset - - slave->mtd.size; + if (master->size - slave->offset >= slave->size) { + slave->size = master->size - slave->offset + - slave->size; } else { debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", part->name, master->size - slave->offset, - slave->mtd.size); + slave->size); /* register to preserve ordering */ goto out_register; } } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = master->size - slave->offset; + if (slave->size == MTDPART_SIZ_FULL) + slave->size = master->size - slave->offset; debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, - (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); + (unsigned long long)(slave->offset + slave->size), slave->name); /* let's do some sanity checks */ if (slave->offset >= master->size) { /* let's register it anyway to preserve ordering */ slave->offset = 0; - slave->mtd.size = 0; + slave->size = 0; printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", part->name); goto out_register; } - if (slave->offset + slave->mtd.size > master->size) { - slave->mtd.size = master->size - slave->offset; + if (slave->offset + slave->size > master->size) { + slave->size = master->size - slave->offset; printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", - part->name, master->name, (unsigned long long)slave->mtd.size); + part->name, master->name, (unsigned long long)slave->size); } if (master->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = master->numeraseregions; - u64 end = slave->offset + slave->mtd.size; + u64 end = slave->offset + slave->size; struct mtd_erase_region_info *regions = master->eraseregions; /* Find the first erase regions which is part of this @@ -540,44 +492,44 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; + if (slave->erasesize < regions[i].erasesize) { + slave->erasesize = regions[i].erasesize; } } - BUG_ON(slave->mtd.erasesize == 0); + BUG_ON(slave->erasesize == 0); } else { /* Single erase size */ - slave->mtd.erasesize = master->erasesize; + slave->erasesize = master->erasesize; } - if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->offset, &slave->mtd)) { + if ((slave->flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset, slave)) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; + slave->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", part->name); } - if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { - slave->mtd.flags &= ~MTD_WRITEABLE; + if ((slave->flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->size, slave)) { + slave->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", part->name); } - slave->mtd.ecclayout = master->ecclayout; - slave->mtd.ecc_step_size = master->ecc_step_size; - slave->mtd.ecc_strength = master->ecc_strength; - slave->mtd.bitflip_threshold = master->bitflip_threshold; + slave->ecclayout = master->ecclayout; + slave->ecc_step_size = master->ecc_step_size; + slave->ecc_strength = master->ecc_strength; + slave->bitflip_threshold = master->bitflip_threshold; if (master->_block_isbad) { uint64_t offs = 0; - while (offs < slave->mtd.size) { + while (offs < slave->size) { if (mtd_block_isbad(master, offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; + slave->ecc_stats.badblocks++; + offs += slave->erasesize; } } @@ -590,7 +542,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length) { struct mtd_partition part; - struct mtd_part *p, *new; + struct mtd_info *p, *new; uint64_t start, end; int ret = 0; @@ -619,21 +571,20 @@ int mtd_add_partition(struct mtd_info *master, const char *name, end = offset + length; mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(p, &mtd_partitions, list) - if (p->master == master) { - if ((start >= p->offset) && - (start < (p->offset + p->mtd.size))) - goto err_inv; + list_for_each_entry(p, &master->partitions, node) { + if ((start >= p->offset) && + (start < (p->offset + p->size))) + goto err_inv; - if ((end >= p->offset) && - (end < (p->offset + p->mtd.size))) - goto err_inv; - } + if ((end >= p->offset) && + (end < (p->offset + p->size))) + goto err_inv; + } - list_add(&new->list, &mtd_partitions); + list_add_tail(&new->node, &master->partitions); mutex_unlock(&mtd_partitions_mutex); - add_mtd_device(&new->mtd); + add_mtd_device(new); return ret; err_inv: @@ -645,18 +596,17 @@ EXPORT_SYMBOL_GPL(mtd_add_partition); int mtd_del_partition(struct mtd_info *master, int partno) { - struct mtd_part *slave, *next; + struct mtd_info *slave, *next; int ret = -EINVAL; mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if ((slave->master == master) && - (slave->mtd.index == partno)) { - ret = del_mtd_device(&slave->mtd); + list_for_each_entry_safe(slave, next, &master->partitions, node) + if (slave->index == partno) { + ret = del_mtd_device(slave); if (ret < 0) break; - list_del(&slave->list); + list_del(&slave->node); free_partition(slave); break; } @@ -680,20 +630,10 @@ int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; + struct mtd_info *slave; uint64_t cur_offset = 0; int i; -#ifdef __UBOOT__ - /* - * Need to init the list here, since LIST_INIT() does not - * work on platforms where relocation has problems (like MIPS - * & PPC). - */ - if (mtd_partitions.next == NULL) - INIT_LIST_HEAD(&mtd_partitions); -#endif - debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); for (i = 0; i < nbparts; i++) { @@ -702,12 +642,12 @@ int add_mtd_partitions(struct mtd_info *master, return PTR_ERR(slave); mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); + list_add_tail(&slave->node, &master->partitions); mutex_unlock(&mtd_partitions_mutex); - add_mtd_device(&slave->mtd); + add_mtd_device(slave); - cur_offset = slave->offset + slave->mtd.size; + cur_offset = slave->offset + slave->size; } return 0; @@ -810,29 +750,12 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, } #endif -int mtd_is_partition(const struct mtd_info *mtd) -{ - struct mtd_part *part; - int ispart = 0; - - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(part, &mtd_partitions, list) - if (&part->mtd == mtd) { - ispart = 1; - break; - } - mutex_unlock(&mtd_partitions_mutex); - - return ispart; -} -EXPORT_SYMBOL_GPL(mtd_is_partition); - /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) { - if (!mtd_is_partition(mtd)) - return mtd->size; + if (mtd_is_partition(mtd)) + return mtd->parent->size; - return PART(mtd)->master->size; + return mtd->size; } EXPORT_SYMBOL_GPL(mtd_get_device_size); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 755c785416..3902c9ec08 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -307,6 +307,15 @@ struct mtd_info { struct udevice *dev; #endif int usecount; + + /* MTD devices do not have any parent. MTD partitions do. */ + struct mtd_info *parent; + /* Offset of the partition relatively to the parent offset */ + u64 offset; + /* List node for this MTD device */ + struct list_head node; + /* List of this MTD devices partitions (if any) */ + struct list_head partitions; }; #if IS_ENABLED(CONFIG_DM) @@ -334,6 +343,16 @@ static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) } #endif +static inline bool mtd_is_partition(const struct mtd_info *mtd) +{ + return mtd->parent; +} + +static inline struct mtd_info *mtd_part_node_to_info(struct list_head *n) +{ + return container_of(n, struct mtd_info, node); +} + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 0d6e0cfa59..698b204fe8 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -81,7 +81,6 @@ extern void register_mtd_parser(struct mtd_part_parser *parser); extern void deregister_mtd_parser(struct mtd_part_parser *parser); #endif -int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); From patchwork Thu Aug 16 15:30:27 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958445 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrTn4zpQz9s3Z for ; Fri, 17 Aug 2018 01:53:33 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 720B0C21DED; Thu, 16 Aug 2018 15:44:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 1DC15C21F24; Thu, 16 Aug 2018 15:31:32 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id BE19AC21E9F; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 95E01C21E56 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id B428D2130E; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 29035215CD; Thu, 16 Aug 2018 17:30:39 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:27 +0200 Message-Id: <20180816153029.15521-30-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> MIME-Version: 1.0 Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 29/31] cmd: mtd: add 'mtd' command X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" There should not be a 'nand' command, a 'sf' command and certainly not a new 'spi-nand' command. Write a 'mtd' command instead to manage all MTD devices/partitions at once. This should be the preferred way to access any MTD device. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/mtd.c | 513 ++++++++++++++++++++++++++++++++++++++++ drivers/mtd/Makefile | 2 +- include/linux/mtd/mtd.h | 1 + 5 files changed, 523 insertions(+), 1 deletion(-) create mode 100644 cmd/mtd.c diff --git a/cmd/Kconfig b/cmd/Kconfig index ef43ed8dda..81351920ab 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -847,6 +847,13 @@ config CMD_MMC_SWRITE Enable support for the "mmc swrite" command to write Android sparse images to eMMC. +config CMD_MTD + bool "mtd" + depends on CMD_MTDPARTS + select MTD_PARTITIONS + help + MTD commands support. + config CMD_NAND bool "nand" default y if NAND_SUNXI diff --git a/cmd/Makefile b/cmd/Makefile index 323f1fd2c7..32fd102189 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_CMD_MISC) += misc.o obj-$(CONFIG_CMD_MMC) += mmc.o obj-$(CONFIG_CMD_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MP) += mp.o +obj-$(CONFIG_CMD_MTD) += mtd.o obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_NET) += net.o diff --git a/cmd/mtd.c b/cmd/mtd.c new file mode 100644 index 0000000000..b9d58725f0 --- /dev/null +++ b/cmd/mtd.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * mtd.c + * + * Generic command to handle basic operations on any memory device. + * + * Copyright: Bootlin, 2018 + * Author: Miquèl Raynal + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTD_NAME_MAX_LEN 20 + +static void mtd_dump_buf(u8 *buf, uint len, uint offset) +{ + int i, j; + + for (i = 0; i < len; ) { + printf("0x%08x:\t", offset + i); + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf(" "); + i += 8; + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf("\n"); + i += 8; + } +} + +static void mtd_show_parts(struct mtd_info *mtd, int level) +{ + struct mtd_info *part; + int i; + + if (list_empty(&mtd->partitions)) + return; + + list_for_each_entry(part, &mtd->partitions, node) { + for (i = 0; i < level; i++) + printf("\t"); + printf("* %s\n", part->name); + for (i = 0; i < level; i++) + printf("\t"); + printf(" > Offset: 0x%llx bytes\n", part->offset); + for (i = 0; i < level; i++) + printf("\t"); + printf(" > Size: 0x%llx bytes\n", part->size); + + mtd_show_parts(part, level + 1); + } +} + +static void mtd_show_device(struct mtd_info *mtd) +{ + /* Device */ + printf("* %s", mtd->name); + if (mtd->dev) + printf(" [device: %s] [parent: %s] [driver: %s]", + mtd->dev->name, mtd->dev->parent->name, + mtd->dev->driver->name); + printf("\n"); + + /* MTD device information */ + printf(" > type: "); + switch (mtd->type) { + case MTD_RAM: + printf("RAM\n"); + break; + case MTD_ROM: + printf("ROM\n"); + break; + case MTD_NORFLASH: + printf("NOR flash\n"); + break; + case MTD_NANDFLASH: + printf("NAND flash\n"); + break; + case MTD_DATAFLASH: + printf("Data flash\n"); + break; + case MTD_UBIVOLUME: + printf("UBI volume\n"); + break; + case MTD_MLCNANDFLASH: + printf("MLC NAND flash\n"); + break; + case MTD_ABSENT: + default: + printf("Unknown\n"); + break; + } + + printf(" > Size: 0x%llx bytes\n", mtd->size); + printf(" > Block: 0x%x bytes\n", mtd->erasesize); + printf(" > Min I/O: 0x%x bytes\n", mtd->writesize); + + if (mtd->oobsize) { + printf(" > OOB size: %u bytes\n", mtd->oobsize); + printf(" > OOB available: %u bytes\n", mtd->oobavail); + } + + if (mtd->ecc_strength) { + printf(" > ECC strength: %u bits\n", mtd->ecc_strength); + printf(" > ECC step size: %u bytes\n", mtd->ecc_step_size); + printf(" > Bitflip threshold: %u bits\n", + mtd->bitflip_threshold); + } + + /* MTD partitions, if any */ + mtd_show_parts(mtd, 1); +} + +/* + * Ensure the MTD device partition list has been initialized already (needed for + * non DM-compliant devices). + */ +static void mtd_quirk_init_non_dm_device_lists(void) +{ + struct mtd_info *mtd; + + mtd_for_each_device(mtd) { + if (!mtd_is_partition(mtd) && !mtd->partitions.next) { + INIT_LIST_HEAD(&mtd->partitions); + INIT_LIST_HEAD(&mtd->node); + } + } +} + +#if IS_ENABLED(CONFIG_DM) +static void mtd_probe_uclass_mtd_devs(void) +{ + struct udevice *dev; + int idx = 0; + + /* Probe devices with DM compliant drivers */ + while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { + mtd_probe(dev); + idx++; + } +} +#else +static void mtd_probe_uclass_mtd_devs(void) { } +#endif + +int mtd_probe_devices(void) +{ + const char *mtdparts = env_get("mtdparts"); + + mtd_quirk_init_non_dm_device_lists(); + + mtd_probe_uclass_mtd_devs(); + + /* Create the partitions defined in mtdparts, here the fun begins */ + + /* Ignore the extra 'mtdparts=' prefix if any */ + if (strstr(mtdparts, "mtdparts=")) + mtdparts += 9; + + /* For each MTD device in mtdparts */ + while (mtdparts[0] != '\0') { + struct mtd_partition *parts; + struct list_head *iterator; + bool part_in_use = false; + struct mtd_info *parent; + char mtdname[MTD_NAME_MAX_LEN]; + int mtdname_len, len, ret; + + mtdname_len = strchr(mtdparts, ':') - mtdparts; + strncpy(mtdname, mtdparts, mtdname_len); + mtdname[mtdname_len] = '\0'; + /* Move the pointer forward (including the ':') */ + mtdparts += mtdname_len + 1; + parent = get_mtd_device_nm(mtdname); + if (IS_ERR_OR_NULL(parent)) { + char altname[MTD_NAME_MAX_LEN]; + + /* + * The MTD device named "mtdname" does not exist. Try to + * find a correspondance with an MTD device having the + * same type and number as defined in the mtdids. + */ + printf("No device named %s\n", mtdname); + + ret = mtdparts_init(); + if (ret) { + printf("Could not initialize mtdparts\n"); + return ret; + } + + ret = mtdids_search_alternate_name(mtdname, altname, + MTD_NAME_MAX_LEN); + if (ret) { + printf("Did not found an equivalent in mtdids\n"); + return ret; + } + + parent = get_mtd_device_nm(altname); + if (IS_ERR_OR_NULL(parent)) { + printf("No device named %s\n", altname); + return -EINVAL; + } + } + put_mtd_device(parent); + + /* + * Parse the MTD device partitions. It will update the mtdparts + * pointer, create an array of parts (that must be freed), and + * return the number of partition structures in the array. + */ + ret = mtdparts_parse_part(parent, &mtdparts, &parts, &len); + if (ret) { + printf("Could not parse device %s\n", parent->name); + return -EINVAL; + } + + if (!len) + continue; + + /* Check that no partition is actually in use */ + list_for_each(iterator, &parent->partitions) { + struct mtd_info *p = mtd_part_node_to_info(iterator); + + if (p->usecount) { + printf("MTD partition \"%s\" already in use.\n", + p->name); + part_in_use = true; + } + } + + if (part_in_use) { + printf("Not reconstructing partition list for MTD device %s\n", + parent->name); + continue; + } + + /* Free the existing partitions */ + del_mtd_partitions(parent); + + /* Create the new MTD partitions */ + add_mtd_partitions(parent, parts, len); + free(parts); + } + + return 0; +} + +static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len) +{ + do_div(len, mtd->writesize); + + return len; +} + +static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size) +{ + return do_div(size, mtd->writesize); +} + +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{ + return do_div(size, mtd->erasesize); +} + +static int do_mtd_list(void) +{ + struct mtd_info *mtd; + int dev_nb = 0; + + /* Ensure all devices (and their partitions) are probed */ + mtd_probe_devices(); + + printf("List of MTD devices:\n"); + mtd_for_each_device(mtd) { + if (!mtd_is_partition(mtd)) + mtd_show_device(mtd); + + dev_nb++; + } + + if (!dev_nb) { + printf("No MTD device found\n"); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct mtd_info *mtd; + const char *cmd; + char *mtdname; + int ret; + + /* All MTD commands need at least two arguments */ + if (argc < 2) + return CMD_RET_USAGE; + + /* Parse the command name and its optional suffixes */ + cmd = argv[1]; + + /* List the MTD devices if that is what the user wants */ + if (strcmp(cmd, "list") == 0) + return do_mtd_list(); + + /* + * The remaining commands require also at least a device ID. + * Check the selected device is valid. Ensure it is probed. + */ + if (argc < 3) + return CMD_RET_USAGE; + + mtdname = argv[2]; + mtd = get_mtd_device_nm(mtdname); + if (IS_ERR_OR_NULL(mtd)) { + mtd_probe_devices(); + mtd = get_mtd_device_nm(mtdname); + if (IS_ERR_OR_NULL(mtd)) { + printf("MTD device %s not found, ret %ld\n", + mtdname, PTR_ERR(mtd)); + return 1; + } + } + put_mtd_device(mtd); + + argc -= 3; + argv += 3; + + /* Do the parsing */ + if (!strncmp(cmd, "read", 4) || !strncmp(cmd, "dump", 4) || + !strncmp(cmd, "write", 5)) { + struct mtd_oob_ops io_op = {}; + bool dump, read, raw, woob; + uint user_addr = 0; + uint nb_pages; + u8 *buf; + u64 off, len, default_len; + u32 oob_len; + + dump = !strncmp(cmd, "dump", 4); + read = dump || !strncmp(cmd, "read", 4); + raw = strstr(cmd, ".raw"); + woob = strstr(cmd, ".oob"); + + if (!dump) { + if (!argc) + return CMD_RET_USAGE; + + user_addr = simple_strtoul(argv[0], NULL, 16); + argc--; + argv++; + } + + default_len = dump ? mtd->writesize : mtd->size; + off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : + default_len; + nb_pages = mtd_len_to_pages(mtd, len); + oob_len = woob ? nb_pages * mtd->oobsize : 0; + + if (mtd_is_aligned_with_min_io_size(mtd, off)) { + printf("Offset not aligned with a page (0x%x)\n", + mtd->writesize); + return CMD_RET_FAILURE; + } + + if (mtd_is_aligned_with_min_io_size(mtd, len)) { + printf("Size not a multiple of a page (0x%x)\n", + mtd->writesize); + return CMD_RET_FAILURE; + } + + if (dump) + buf = kmalloc(len + oob_len, GFP_KERNEL); + else + buf = map_sysmem(user_addr, 0); + + if (!buf) { + printf("Could not map/allocate the user buffer\n"); + return CMD_RET_FAILURE; + } + + printf("%s %lldB (%d page(s)) at offset 0x%08llx%s%s\n", + read ? "Reading" : "Writing", len, nb_pages, off, + raw ? " [raw]" : "", woob ? " [oob]" : ""); + + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB; + io_op.len = len; + io_op.ooblen = oob_len; + io_op.datbuf = buf; + io_op.oobbuf = woob ? &buf[len] : NULL; + + if (read) + ret = mtd_read_oob(mtd, off, &io_op); + else + ret = mtd_write_oob(mtd, off, &io_op); + + if (!ret && dump) { + uint page; + + for (page = 0; page < nb_pages; page++) { + u64 data_off = page * mtd->writesize; + + printf("\nDump %d data bytes from 0x%08llx:\n", + mtd->writesize, data_off); + mtd_dump_buf(buf, mtd->writesize, data_off); + + if (woob) { + u64 oob_off = page * mtd->oobsize; + + printf("Dump %d OOB bytes from page at 0x%08llx:\n", + mtd->oobsize, data_off); + mtd_dump_buf(&buf[len + oob_off], + mtd->oobsize, 0); + } + } + } + + if (dump) + kfree(buf); + else + unmap_sysmem(buf); + + if (ret) { + printf("%s on %s failed with error %d\n", + read ? "Read" : "Write", mtd->name, ret); + return CMD_RET_FAILURE; + } + + } else if (!strcmp(cmd, "erase")) { + bool scrub = strstr(cmd, ".dontskipbad"); + struct erase_info erase_op = {}; + u64 off, len; + + off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size; + + if (mtd_is_aligned_with_block_size(mtd, off)) { + printf("Offset not aligned with a block (0x%x)\n", + mtd->erasesize); + return CMD_RET_FAILURE; + } + + if (mtd_is_aligned_with_block_size(mtd, len)) { + printf("Size not a multiple of a block (0x%x)\n", + mtd->erasesize); + return CMD_RET_FAILURE; + } + + printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n", + off, off + len - 1, mtd_div_by_eb(len, mtd)); + + erase_op.mtd = mtd; + erase_op.addr = off; + erase_op.len = len; + erase_op.scrub = scrub; + + while (erase_op.len) { + ret = mtd_erase(mtd, &erase_op); + + /* Abort if its not an bad block error */ + if (ret != -EIO) + break; + + printf("Skipping bad block at 0x%08llx\n", + erase_op.fail_addr); + + /* Skip bad block and continue behind it */ + erase_op.len -= erase_op.fail_addr - erase_op.addr; + erase_op.len -= mtd->erasesize; + erase_op.addr = erase_op.fail_addr + mtd->erasesize; + } + } else { + return CMD_RET_USAGE; + } + + if (ret) + return CMD_RET_FAILURE; + return 0; +} + +static char mtd_help_text[] = +#ifdef CONFIG_SYS_LONGHELP + "- generic operations on memory technology devices\n\n" + "mtd list\n" + "mtd read[.raw][.oob] [ []]\n" + "mtd dump[.raw][.oob] [ []]\n" + "mtd write[.raw][.oob] [ []]\n" + "mtd erase[.dontskipbad] [ []]\n" + "\n" + "With:\n" + "\t: NAND partition/chip name\n" + "\t: user address from/to which data will be retrieved/stored\n" + "\t: offset in in bytes (default: start of the part)\n" + "\t\t* must be block-aligned for erase\n" + "\t\t* must be page-aligned otherwise\n" + "\t: length of the operation in bytes (default: the entire device)\n" + "\t\t* must be a multiple of a block for erase\n" + "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n" +#endif + ""; + +U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text); diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index cdf515256a..22ceda93c0 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF))) +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF)$(CONFIG_CMD_MTD))) obj-y += mtdcore.o mtd_uboot.o endif obj-$(CONFIG_MTD) += mtd-uclass.o diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 3902c9ec08..5ae12be67d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -570,6 +570,7 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, int *truncated); int mtd_probe(struct udevice *dev); +int mtd_probe_devices(void); #endif #endif /* __MTD_MTD_H__ */ From patchwork Thu Aug 16 15:30:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958444 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrTV4cLJz9s3Z for ; Fri, 17 Aug 2018 01:53:18 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 58691C21E15; Thu, 16 Aug 2018 15:45:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 0A5BBC21F52; Thu, 16 Aug 2018 15:31:38 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id A8153C21E60; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id 8E04BC21E76 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id AD4D22130B; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id 7624F215CF; Thu, 16 Aug 2018 17:30:39 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:28 +0200 Message-Id: <20180816153029.15521-31-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 30/31] cmd: mtdparts: try to probe the MTD devices as a fallback X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Current implementation of mtdparts command errors out if the desired MTD device is not found. Fallback to the new probe function in this case before erroring out. This will the save the user the need to call something like 'mtd list' before mtdparts. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- cmd/mtdparts.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 33becb86e8..fb6424d72a 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -308,9 +308,15 @@ static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd) sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num); *mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(*mtd)) { - printf("Device %s not found!\n", mtd_dev); - return 1; + if (IS_ERR_OR_NULL(*mtd)) { +#ifdef CONFIG_CMD_MTD + mtd_probe_devices(); + *mtd = get_mtd_device_nm(mtd_dev); +#endif + if (IS_ERR_OR_NULL(*mtd)) { + printf("Device %s not found!\n", mtd_dev); + return 1; + } } put_mtd_device(*mtd); From patchwork Thu Aug 16 15:30:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Miquel Raynal X-Patchwork-Id: 958433 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 41rrPY53l1z9s3Z for ; Fri, 17 Aug 2018 01:49:53 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 71301C21DC1; Thu, 16 Aug 2018 15:43:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 75B7CC21DFB; Thu, 16 Aug 2018 15:31:28 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 3D067C21E85; Thu, 16 Aug 2018 15:31:17 +0000 (UTC) Received: from mail.bootlin.com (mail.bootlin.com [62.4.15.54]) by lists.denx.de (Postfix) with ESMTP id B5472C21EA7 for ; Thu, 16 Aug 2018 15:31:02 +0000 (UTC) Received: by mail.bootlin.com (Postfix, from userid 110) id DE0F821310; Thu, 16 Aug 2018 17:31:01 +0200 (CEST) Received: from localhost.localdomain (AAubervilliers-681-1-43-114.w90-88.abo.wanadoo.fr [90.88.161.114]) by mail.bootlin.com (Postfix) with ESMTPSA id C2896215D0; Thu, 16 Aug 2018 17:30:39 +0200 (CEST) From: Miquel Raynal To: Tom Rini , Daniel Schwierzeck Date: Thu, 16 Aug 2018 17:30:29 +0200 Message-Id: <20180816153029.15521-32-miquel.raynal@bootlin.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180816153029.15521-1-miquel.raynal@bootlin.com> References: <20180816153029.15521-1-miquel.raynal@bootlin.com> Cc: Scott Wood , Alexandre Belloni , Boris Brezillon , Antoine Tenart , Allan Nielsen , u-boot@lists.denx.de, Miquel Raynal , Stefan Roese , Jagan Teki Subject: [U-Boot] [PATCH v6 31/31] cmd: ubi: clean the partition handling X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" UBI should not mess with MTD partitions, now that the partitions are handled in a clean way, clean the ubi command and avoid using this uneeded extra-glue to reference the devices. Signed-off-by: Miquel Raynal --- cmd/ubi.c | 95 ++++++++++++++----------------------------------------- 1 file changed, 24 insertions(+), 71 deletions(-) diff --git a/cmd/ubi.c b/cmd/ubi.c index 0a3405a3b1..e848a39894 100644 --- a/cmd/ubi.c +++ b/cmd/ubi.c @@ -29,17 +29,6 @@ /* Private own data */ static struct ubi_device *ubi; -static char buffer[80]; -static int ubi_initialized; - -struct selected_dev { - char part_name[80]; - int selected; - int nr; - struct mtd_info *mtd_info; -}; - -static struct selected_dev ubi_dev; #ifdef CONFIG_CMD_UBIFS int ubifs_is_mounted(void); @@ -404,43 +393,24 @@ int ubi_volume_read(char *volume, char *buf, size_t size) return err; } -static int ubi_dev_scan(struct mtd_info *info, char *ubidev, - const char *vid_header_offset) +static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset) { - struct mtd_device *dev; - struct part_info *part; - struct mtd_partition mtd_part; char ubi_mtd_param_buffer[80]; - u8 pnum; int err; - if (find_dev_and_part(ubidev, &dev, &pnum, &part) != 0) - return 1; + if (!vid_header_offset) + sprintf(ubi_mtd_param_buffer, "%s", info->name); + else + sprintf(ubi_mtd_param_buffer, "%s,%s", info->name, + vid_header_offset); - sprintf(buffer, "mtd=%d", pnum); - memset(&mtd_part, 0, sizeof(mtd_part)); - mtd_part.name = buffer; - mtd_part.size = part->size; - mtd_part.offset = part->offset; - add_mtd_partitions(info, &mtd_part, 1); - - strcpy(ubi_mtd_param_buffer, buffer); - if (vid_header_offset) - sprintf(ubi_mtd_param_buffer, "mtd=%d,%s", pnum, - vid_header_offset); err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL); - if (err) { - del_mtd_partitions(info); + if (err) return -err; - } err = ubi_init(); - if (err) { - del_mtd_partitions(info); + if (err) return -err; - } - - ubi_initialized = 1; return 0; } @@ -465,50 +435,33 @@ int ubi_detach(void) /* * Call ubi_exit() before re-initializing the UBI subsystem */ - if (ubi_initialized) { + if (ubi) ubi_exit(); - del_mtd_partitions(ubi_dev.mtd_info); - ubi_initialized = 0; - } - ubi_dev.selected = 0; + ubi = NULL; + return 0; } int ubi_part(char *part_name, const char *vid_header_offset) { + struct mtd_info *mtd; int err = 0; - char mtd_dev[16]; - struct mtd_device *dev; - struct part_info *part; - u8 pnum; ubi_detach(); - /* - * Search the mtd device number where this partition - * is located - */ - if (find_dev_and_part(part_name, &dev, &pnum, &part)) { + + mtd_probe_devices(); + mtd = get_mtd_device_nm(part_name); + if (IS_ERR(mtd)) { printf("Partition %s not found!\n", part_name); return 1; } - sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(dev->id->type), dev->id->num); - ubi_dev.mtd_info = get_mtd_device_nm(mtd_dev); - if (IS_ERR(ubi_dev.mtd_info)) { - printf("Partition %s not found on device %s!\n", part_name, - mtd_dev); - return 1; - } + put_mtd_device(mtd); - ubi_dev.selected = 1; - - strcpy(ubi_dev.part_name, part_name); - err = ubi_dev_scan(ubi_dev.mtd_info, ubi_dev.part_name, - vid_header_offset); + err = ubi_dev_scan(mtd, vid_header_offset); if (err) { printf("UBI init error %d\n", err); printf("Please check, if the correct MTD partition is used (size big enough?)\n"); - ubi_dev.selected = 0; return err; } @@ -539,13 +492,13 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /* Print current partition */ if (argc == 2) { - if (!ubi_dev.selected) { - printf("Error, no UBI device/partition selected!\n"); + if (!ubi) { + printf("Error, no UBI device selected!\n"); return 1; } - printf("Device %d: %s, partition %s\n", - ubi_dev.nr, ubi_dev.mtd_info->name, ubi_dev.part_name); + printf("Device %d: %s, MTD partition %s\n", + ubi->ubi_num, ubi->ubi_name, ubi->mtd->name); return 0; } @@ -558,8 +511,8 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return ubi_part(argv[2], vid_header_offset); } - if ((strcmp(argv[1], "part") != 0) && (!ubi_dev.selected)) { - printf("Error, no UBI device/partition selected!\n"); + if ((strcmp(argv[1], "part") != 0) && !ubi) { + printf("Error, no UBI device selected!\n"); return 1; }