From 43a0a45abc4ab386f3ba978c877a2b68a0cad448 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:01:59 +0100 Subject: mtd: nand: Get rid of comments giving the file path inside the file itself Some files add a comment giving the path of the file inside the Linux tree, which is pretty useless since the reader had to find the file to open it. Getting rid of these comments will also allow us to easily move these files around when needed. Signed-off-by: Boris Brezillon --- include/linux/mtd/bbm.h | 2 -- include/linux/mtd/nand_ecc.h | 2 -- include/linux/mtd/ndfc.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 3bf8f954b642..3102bd754d18 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -1,6 +1,4 @@ /* - * linux/include/linux/mtd/bbm.h - * * NAND family Bad Block Management (BBM) header file * - Bad Block Table (BBT) implementation * diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index 4d8406c81652..8a2decf7462c 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand_ecc.h - * * Copyright (C) 2000-2010 Steven J. Hill * David Woodhouse * Thomas Gleixner diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h index d0558a982628..357e88b3263a 100644 --- a/include/linux/mtd/ndfc.h +++ b/include/linux/mtd/ndfc.h @@ -1,6 +1,4 @@ /* - * linux/include/linux/mtd/ndfc.h - * * Copyright (c) 2006 Thomas Gleixner * * This program is free software; you can redistribute it and/or modify -- cgit v1.2.3 From 9c3736a3de21d916a6af0594418b85a112f4bef6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 5 Feb 2018 23:02:05 +0100 Subject: mtd: nand: Add core infrastructure to deal with NAND devices 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 --- drivers/mtd/nand/Kconfig | 3 + drivers/mtd/nand/Makefile | 3 + drivers/mtd/nand/bbt.c | 130 +++++++++ drivers/mtd/nand/core.c | 244 ++++++++++++++++ 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 (limited to 'include') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6d5373471809..1c1a1f487e20 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 32af7168c5ba..a72d3cb0f325 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +nandcore-objs := core.o bbt.o +obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o + obj-y += raw/ diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c new file mode 100644 index 000000000000..56cde38b92c0 --- /dev/null +++ b/drivers/mtd/nand/bbt.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand-bbt: " fmt + +#include +#include + +/** + * 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 000000000000..f237a688f8e9 --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand: " fmt + +#include +#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 + * @block: 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 + * @block: eraseblock to erase + * + * Erases @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); + einfo->state = MTD_ERASE_FAILED; + + return ret; + } + + nanddev_pos_next_eraseblock(nand, &pos); + } + + einfo->state = MTD_ERASE_DONE; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_mtd_erase); + +/** + * nanddev_init() - Initialize a NAND device + * @nand: NAND device + * @memorg: NAND memory organization descriptor + * @ops: NAND device operations + * + * Initializes a NAND device object. Consistency checks are done on @memorg and + * @ops. 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 000000000000..792ea5c26329 --- /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 container_of(mtd, struct nand_device, mtd); +} + +/** + * 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, + 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 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 */ -- cgit v1.2.3 From dcba51bbb9e0cc7f80d36eb20a033a4dff2ce9cc Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 12 Feb 2018 22:03:08 +0100 Subject: mtd: Get rid of unused fields in struct erase_info Some fields are not used by MTD drivers, users or core code. Moreover, those fields are not documented, so get rid of them to avoid any confusion. Signed-off-by: Boris Brezillon Reviewed-by: Richard Weinberger --- include/linux/mtd/mtd.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 205ededccc60..2a407dc9beaa 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -48,14 +48,9 @@ struct erase_info { uint64_t addr; uint64_t len; uint64_t fail_addr; - u_long time; - u_long retries; - unsigned dev; - unsigned cell; void (*callback) (struct erase_info *self); u_long priv; u_char state; - struct erase_info *next; }; struct mtd_erase_region_info { -- cgit v1.2.3 From 884cfd9023ce6afe8bcf181ec988d8516eb32bf0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 12 Feb 2018 22:03:09 +0100 Subject: mtd: Stop assuming mtd_erase() is asynchronous None of the mtd->_erase() implementations work in an asynchronous manner, so let's simplify MTD users that call mtd_erase(). All they need to do is check the value returned by mtd_erase() and assume that != 0 means failure. Signed-off-by: Boris Brezillon Reviewed-by: Richard Weinberger --- drivers/mtd/devices/bcm47xxsflash.c | 3 -- drivers/mtd/ftl.c | 51 ++++---------------- drivers/mtd/inftlmount.c | 5 +- drivers/mtd/mtdblock.c | 20 -------- drivers/mtd/mtdchar.c | 33 +------------ drivers/mtd/mtdconcat.c | 48 ++----------------- drivers/mtd/mtdcore.c | 8 ++-- drivers/mtd/mtdoops.c | 19 -------- drivers/mtd/mtdpart.c | 2 - drivers/mtd/mtdswap.c | 32 ------------- drivers/mtd/nftlmount.c | 4 +- drivers/mtd/rfd_ftl.c | 92 +++++++++++-------------------------- drivers/mtd/sm_ftl.c | 18 -------- drivers/mtd/sm_ftl.h | 4 -- drivers/mtd/tests/mtd_test.c | 4 -- drivers/mtd/tests/speedtest.c | 6 --- drivers/mtd/ubi/io.c | 35 -------------- fs/jffs2/erase.c | 36 ++------------- include/linux/mtd/mtd.h | 2 - 19 files changed, 52 insertions(+), 370 deletions(-) (limited to 'include') diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index e2bd81817df4..6b84947cfbea 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -95,9 +95,6 @@ static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) else erase->state = MTD_ERASE_DONE; - if (erase->callback) - erase->callback(erase); - return err; } diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 664d206a4cbe..fcf9907e7987 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -140,12 +140,6 @@ typedef struct partition_t { #define XFER_PREPARED 0x03 #define XFER_FAILED 0x04 -/*====================================================================*/ - - -static void ftl_erase_callback(struct erase_info *done); - - /*====================================================================== Scan_header() checks to see if a memory region contains an FTL @@ -349,17 +343,19 @@ static int erase_xfer(partition_t *part, return -ENOMEM; erase->mtd = part->mbd.mtd; - erase->callback = ftl_erase_callback; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; - erase->priv = (u_long)part; ret = mtd_erase(part->mbd.mtd, erase); + if (!ret) { + xfer->state = XFER_ERASED; + xfer->EraseCount++; + } else { + xfer->state = XFER_FAILED; + pr_notice("ftl_cs: erase failed: err = %d\n", ret); + } - if (!ret) - xfer->EraseCount++; - else - kfree(erase); + kfree(erase); return ret; } /* erase_xfer */ @@ -371,37 +367,6 @@ static int erase_xfer(partition_t *part, ======================================================================*/ -static void ftl_erase_callback(struct erase_info *erase) -{ - partition_t *part; - struct xfer_info_t *xfer; - int i; - - /* Look up the transfer unit */ - part = (partition_t *)(erase->priv); - - for (i = 0; i < part->header.NumTransferUnits; i++) - if (part->XferInfo[i].Offset == erase->addr) break; - - if (i == part->header.NumTransferUnits) { - printk(KERN_NOTICE "ftl_cs: internal error: " - "erase lookup failed!\n"); - return; - } - - xfer = &part->XferInfo[i]; - if (erase->state == MTD_ERASE_DONE) - xfer->state = XFER_ERASED; - else { - xfer->state = XFER_FAILED; - printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n", - erase->state); - } - - kfree(erase); - -} /* ftl_erase_callback */ - static int prepare_xfer(partition_t *part, int i) { erase_unit_header_t header; diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 8d6bb189ea8e..0f47be4834d8 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -393,9 +393,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) mark only the failed block in the bbt. */ for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) { - mtd_erase(inftl->mbd.mtd, instr); + int ret; - if (instr->state == MTD_ERASE_FAILED) { + ret = mtd_erase(inftl->mbd.mtd, instr); + if (ret) { printk(KERN_WARNING "INFTL: error while formatting block %d\n", block); goto fail; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index bb4c14f83c75..7b2b7f651181 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -55,48 +55,28 @@ struct mtdblk_dev { * being written to until a different sector is required. */ -static void erase_callback(struct erase_info *done) -{ - wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); -} - static int erase_write (struct mtd_info *mtd, unsigned long pos, int len, const char *buf) { struct erase_info erase; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; size_t retlen; int ret; /* * First, let's erase the flash block. */ - - init_waitqueue_head(&wait_q); erase.mtd = mtd; - erase.callback = erase_callback; erase.addr = pos; erase.len = len; - erase.priv = (u_long)&wait_q; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); ret = mtd_erase(mtd, &erase); if (ret) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] " "on \"%s\" failed\n", pos, len, mtd->name); return ret; } - schedule(); /* Wait for erase to finish. */ - remove_wait_queue(&wait_q, &wait); - /* * Next, write the data to flash. */ diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index de8c902059b8..2beb22dd6bbb 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -324,10 +324,6 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c IOCTL calls for getting device parameters. ======================================================================*/ -static void mtdchar_erase_callback (struct erase_info *instr) -{ - wake_up((wait_queue_head_t *)instr->priv); -} static int otp_select_filemode(struct mtd_file_info *mfi, int mode) { @@ -709,11 +705,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) if (!erase) ret = -ENOMEM; else { - wait_queue_head_t waitq; - DECLARE_WAITQUEUE(wait, current); - - init_waitqueue_head(&waitq); - if (cmd == MEMERASE64) { struct erase_info_user64 einfo64; @@ -736,30 +727,8 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) erase->len = einfo32.length; } erase->mtd = mtd; - erase->callback = mtdchar_erase_callback; - erase->priv = (unsigned long)&waitq; - - /* - FIXME: Allow INTERRUPTIBLE. Which means - not having the wait_queue head on the stack. - - If the wq_head is on the stack, and we - leave because we got interrupted, then the - wq_head is no longer there when the - callback routine tries to wake us up. - */ + ret = mtd_erase(mtd, erase); - if (!ret) { - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&waitq, &wait); - if (erase->state != MTD_ERASE_DONE && - erase->state != MTD_ERASE_FAILED) - schedule(); - remove_wait_queue(&waitq, &wait); - set_current_state(TASK_RUNNING); - - ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; - } kfree(erase); } break; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 60bf53df5454..caa09bf6e572 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -333,45 +333,6 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) return -EINVAL; } -static void concat_erase_callback(struct erase_info *instr) -{ - wake_up((wait_queue_head_t *) instr->priv); -} - -static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) -{ - int err; - wait_queue_head_t waitq; - DECLARE_WAITQUEUE(wait, current); - - /* - * This code was stol^H^H^H^Hinspired by mtdchar.c - */ - init_waitqueue_head(&waitq); - - erase->mtd = mtd; - erase->callback = concat_erase_callback; - erase->priv = (unsigned long) &waitq; - - /* - * FIXME: Allow INTERRUPTIBLE. Which means - * not having the wait_queue head on the stack. - */ - err = mtd_erase(mtd, erase); - if (!err) { - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&waitq, &wait); - if (erase->state != MTD_ERASE_DONE - && erase->state != MTD_ERASE_FAILED) - schedule(); - remove_wait_queue(&waitq, &wait); - set_current_state(TASK_RUNNING); - - err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; - } - return err; -} - static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) { struct mtd_concat *concat = CONCAT(mtd); @@ -466,7 +427,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) erase->len = length; length -= erase->len; - if ((err = concat_dev_erase(subdev, erase))) { + erase->mtd = subdev; + if ((err = mtd_erase(subdev, erase))) { /* sanity check: should never happen since * block alignment has been checked above */ BUG_ON(err == -EINVAL); @@ -487,12 +449,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) } instr->state = erase->state; kfree(erase); - if (err) - return err; - if (instr->callback) - instr->callback(instr); - return 0; + return err; } static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c87859ff338b..f92ad02959eb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -945,11 +945,9 @@ void __put_mtd_device(struct mtd_info *mtd) EXPORT_SYMBOL_GPL(__put_mtd_device); /* - * Erase is an asynchronous operation. Device drivers are supposed - * to call instr->callback() whenever the operation completes, even - * if it completes with a failure. - * Callers are supposed to pass a callback function and wait for it - * to be called before writing to the block. + * Erase is an synchronous operation. Device drivers are epected to return a + * negative error code if the operation failed and update instr->fail_addr + * to point the portion that was not properly erased. */ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 97bb8f6304d4..028ded59297b 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -84,12 +84,6 @@ static int page_is_used(struct mtdoops_context *cxt, int page) return test_bit(page, cxt->oops_page_used); } -static void mtdoops_erase_callback(struct erase_info *done) -{ - wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); -} - static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) { struct mtd_info *mtd = cxt->mtd; @@ -97,34 +91,21 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) u32 start_page = start_page_offset / record_size; u32 erase_pages = mtd->erasesize / record_size; struct erase_info erase; - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t wait_q; int ret; int page; - init_waitqueue_head(&wait_q); erase.mtd = mtd; - erase.callback = mtdoops_erase_callback; erase.addr = offset; erase.len = mtd->erasesize; - erase.priv = (u_long)&wait_q; - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&wait_q, &wait); ret = mtd_erase(mtd, &erase); if (ret) { - set_current_state(TASK_RUNNING); - remove_wait_queue(&wait_q, &wait); printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n", (unsigned long long)erase.addr, (unsigned long long)erase.len, mtddev); return ret; } - schedule(); /* Wait for erase to finish. */ - remove_wait_queue(&wait_q, &wait); - /* Mark pages as unused */ for (page = start_page; page < start_page + erase_pages; page++) mark_page_unused(cxt, page); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 76cd21d1171b..ae1206633d9d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -222,8 +222,6 @@ void mtd_erase_callback(struct erase_info *instr) instr->fail_addr -= part->offset; instr->addr -= part->offset; } - if (instr->callback) - instr->callback(instr); } EXPORT_SYMBOL_GPL(mtd_erase_callback); diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index 7eb0e1f4f980..d390324d102e 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -536,18 +536,10 @@ static void mtdswap_store_eb(struct mtdswap_dev *d, struct swap_eb *eb) mtdswap_rb_add(d, eb, MTDSWAP_HIFRAG); } - -static void mtdswap_erase_callback(struct erase_info *done) -{ - wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; - wake_up(wait_q); -} - static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb) { struct mtd_info *mtd = d->mtd; struct erase_info erase; - wait_queue_head_t wq; unsigned int retries = 0; int ret; @@ -556,14 +548,11 @@ static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb) d->max_erase_count = eb->erase_count; retry: - init_waitqueue_head(&wq); memset(&erase, 0, sizeof(struct erase_info)); erase.mtd = mtd; - erase.callback = mtdswap_erase_callback; erase.addr = mtdswap_eb_offset(d, eb); erase.len = mtd->erasesize; - erase.priv = (u_long)&wq; ret = mtd_erase(mtd, &erase); if (ret) { @@ -582,27 +571,6 @@ retry: return -EIO; } - ret = wait_event_interruptible(wq, erase.state == MTD_ERASE_DONE || - erase.state == MTD_ERASE_FAILED); - if (ret) { - dev_err(d->dev, "Interrupted erase block %#llx erasure on %s\n", - erase.addr, mtd->name); - return -EINTR; - } - - if (erase.state == MTD_ERASE_FAILED) { - if (retries++ < MTDSWAP_ERASE_RETRIES) { - dev_warn(d->dev, - "erase of erase block %#llx on %s failed", - erase.addr, mtd->name); - yield(); - goto retry; - } - - mtdswap_handle_badblock(d, eb); - return -EIO; - } - return 0; } diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 184c8fbfe465..07e122449759 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -331,9 +331,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; - mtd_erase(mtd, instr); - - if (instr->state == MTD_ERASE_FAILED) { + if (mtd_erase(mtd, instr)) { printk("Error while formatting block %d\n", block); goto fail; } diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index d1cbf26db2c0..4e0b55cd08e2 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -266,91 +266,55 @@ static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *b return 0; } -static void erase_callback(struct erase_info *erase) -{ - struct partition *part; - u16 magic; - int i, rc; - size_t retlen; - - part = (struct partition*)erase->priv; - - i = (u32)erase->addr / part->block_size; - if (i >= part->total_blocks || part->blocks[i].offset != erase->addr || - erase->addr > UINT_MAX) { - printk(KERN_ERR PREFIX "erase callback for unknown offset %llx " - "on '%s'\n", (unsigned long long)erase->addr, part->mbd.mtd->name); - return; - } - - if (erase->state != MTD_ERASE_DONE) { - printk(KERN_WARNING PREFIX "erase failed at 0x%llx on '%s', " - "state %d\n", (unsigned long long)erase->addr, - part->mbd.mtd->name, erase->state); - - part->blocks[i].state = BLOCK_FAILED; - part->blocks[i].free_sectors = 0; - part->blocks[i].used_sectors = 0; - - kfree(erase); - - return; - } - - magic = cpu_to_le16(RFD_MAGIC); - - part->blocks[i].state = BLOCK_ERASED; - part->blocks[i].free_sectors = part->data_sectors_per_block; - part->blocks[i].used_sectors = 0; - part->blocks[i].erases++; - - rc = mtd_write(part->mbd.mtd, part->blocks[i].offset, sizeof(magic), - &retlen, (u_char *)&magic); - - if (!rc && retlen != sizeof(magic)) - rc = -EIO; - - if (rc) { - printk(KERN_ERR PREFIX "'%s': unable to write RFD " - "header at 0x%lx\n", - part->mbd.mtd->name, - part->blocks[i].offset); - part->blocks[i].state = BLOCK_FAILED; - } - else - part->blocks[i].state = BLOCK_OK; - - kfree(erase); -} - static int erase_block(struct partition *part, int block) { struct erase_info *erase; - int rc = -ENOMEM; + int rc; erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); if (!erase) - goto err; + return -ENOMEM; erase->mtd = part->mbd.mtd; - erase->callback = erase_callback; erase->addr = part->blocks[block].offset; erase->len = part->block_size; - erase->priv = (u_long)part; part->blocks[block].state = BLOCK_ERASING; part->blocks[block].free_sectors = 0; rc = mtd_erase(part->mbd.mtd, erase); - if (rc) { printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' " "failed\n", (unsigned long long)erase->addr, (unsigned long long)erase->len, part->mbd.mtd->name); - kfree(erase); + part->blocks[block].state = BLOCK_FAILED; + part->blocks[block].free_sectors = 0; + part->blocks[block].used_sectors = 0; + } else { + u16 magic = cpu_to_le16(RFD_MAGIC); + size_t retlen; + + part->blocks[block].state = BLOCK_ERASED; + part->blocks[block].free_sectors = part->data_sectors_per_block; + part->blocks[block].used_sectors = 0; + part->blocks[block].erases++; + + rc = mtd_write(part->mbd.mtd, part->blocks[block].offset, + sizeof(magic), &retlen, (u_char *)&magic); + if (!rc && retlen != sizeof(magic)) + rc = -EIO; + + if (rc) { + pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n", + part->mbd.mtd->name, part->blocks[block].offset); + part->blocks[block].state = BLOCK_FAILED; + } else { + part->blocks[block].state = BLOCK_OK; + } } -err: + kfree(erase); + return rc; } diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 4237c7cebf02..c11156f9d96f 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -461,10 +461,8 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, struct erase_info erase; erase.mtd = mtd; - erase.callback = sm_erase_callback; erase.addr = sm_mkoffset(ftl, zone_num, block, 0); erase.len = ftl->block_size; - erase.priv = (u_long)ftl; if (ftl->unstable) return -EIO; @@ -482,15 +480,6 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, goto error; } - if (erase.state == MTD_ERASE_PENDING) - wait_for_completion(&ftl->erase_completion); - - if (erase.state != MTD_ERASE_DONE) { - sm_printk("erase of block %d in zone %d failed after wait", - block, zone_num); - goto error; - } - if (put_free) kfifo_in(&zone->free_sectors, (const unsigned char *)&block, sizeof(block)); @@ -501,12 +490,6 @@ error: return -EIO; } -static void sm_erase_callback(struct erase_info *self) -{ - struct sm_ftl *ftl = (struct sm_ftl *)self->priv; - complete(&ftl->erase_completion); -} - /* Thoroughly test that block is valid. */ static int sm_check_block(struct sm_ftl *ftl, int zone, int block) { @@ -1141,7 +1124,6 @@ static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) mutex_init(&ftl->mutex); timer_setup(&ftl->timer, sm_cache_flush_timer, 0); INIT_WORK(&ftl->flush_work, sm_cache_flush_work); - init_completion(&ftl->erase_completion); /* Read media information */ if (sm_get_media_info(ftl, mtd)) { diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h index 43bb7300785b..0a46d75cdc6a 100644 --- a/drivers/mtd/sm_ftl.h +++ b/drivers/mtd/sm_ftl.h @@ -53,9 +53,6 @@ struct sm_ftl { struct work_struct flush_work; struct timer_list timer; - /* Async erase stuff */ - struct completion erase_completion; - /* Geometry stuff */ int heads; int sectors; @@ -86,7 +83,6 @@ struct chs_entry { printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) -static void sm_erase_callback(struct erase_info *self); static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, int put_free); static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block); diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c index 3d0b8b5c1a53..0ac625e8f798 100644 --- a/drivers/mtd/tests/mtd_test.c +++ b/drivers/mtd/tests/mtd_test.c @@ -24,10 +24,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum) return err; } - if (ei.state == MTD_ERASE_FAILED) { - pr_info("some erase error occurred at EB %d\n", ebnum); - return -EIO; - } return 0; } diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index 0b89418a0888..f8e5dc11f943 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -70,12 +70,6 @@ static int multiblock_erase(int ebnum, int blocks) return err; } - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d," - "blocks %d\n", ebnum, blocks); - return -EIO; - } - return 0; } diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 8290432017ce..8843d26837b2 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -308,18 +308,6 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, return err; } -/** - * erase_callback - MTD erasure call-back. - * @ei: MTD erase information object. - * - * Note, even though MTD erase interface is asynchronous, all the current - * implementations are synchronous anyway. - */ -static void erase_callback(struct erase_info *ei) -{ - wake_up_interruptible((wait_queue_head_t *)ei->priv); -} - /** * do_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object @@ -333,7 +321,6 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum) { int err, retries = 0; struct erase_info ei; - wait_queue_head_t wq; dbg_io("erase PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); @@ -344,14 +331,11 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum) } retry: - init_waitqueue_head(&wq); memset(&ei, 0, sizeof(struct erase_info)); ei.mtd = ubi->mtd; ei.addr = (loff_t)pnum * ubi->peb_size; ei.len = ubi->peb_size; - ei.callback = erase_callback; - ei.priv = (unsigned long)&wq; err = mtd_erase(ubi->mtd, &ei); if (err) { @@ -366,25 +350,6 @@ retry: return err; } - err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || - ei.state == MTD_ERASE_FAILED); - if (err) { - ubi_err(ubi, "interrupted PEB %d erasure", pnum); - return -EINTR; - } - - if (ei.state == MTD_ERASE_FAILED) { - if (retries++ < UBI_IO_RETRIES) { - ubi_warn(ubi, "error while erasing PEB %d, retry", - pnum); - yield(); - goto retry; - } - ubi_err(ubi, "cannot erase PEB %d", pnum); - dump_stack(); - return -EIO; - } - err = ubi_self_check_all_ff(ubi, pnum, 0, ubi->peb_size); if (err) return err; diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 4a6cf289be24..09bb6c00b869 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -21,14 +21,6 @@ #include #include "nodelist.h" -struct erase_priv_struct { - struct jffs2_eraseblock *jeb; - struct jffs2_sb_info *c; -}; - -#ifndef __ECOS -static void jffs2_erase_callback(struct erase_info *); -#endif static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); @@ -51,7 +43,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, jffs2_dbg(1, "%s(): erase block %#08x (range %#08x-%#08x)\n", __func__, jeb->offset, jeb->offset, jeb->offset + c->sector_size); - instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); + instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL); if (!instr) { pr_warn("kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); mutex_lock(&c->erase_free_sem); @@ -70,15 +62,13 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, instr->mtd = c->mtd; instr->addr = jeb->offset; instr->len = c->sector_size; - instr->callback = jffs2_erase_callback; - instr->priv = (unsigned long)(&instr[1]); - - ((struct erase_priv_struct *)instr->priv)->jeb = jeb; - ((struct erase_priv_struct *)instr->priv)->c = c; ret = mtd_erase(c->mtd, instr); - if (!ret) + if (!ret) { + jffs2_erase_succeeded(c, jeb); + kfree(instr); return; + } bad_offset = instr->fail_addr; kfree(instr); @@ -214,22 +204,6 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock wake_up(&c->erase_wait); } -#ifndef __ECOS -static void jffs2_erase_callback(struct erase_info *instr) -{ - struct erase_priv_struct *priv = (void *)instr->priv; - - if(instr->state != MTD_ERASE_DONE) { - pr_warn("Erase at 0x%08llx finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", - (unsigned long long)instr->addr, instr->state); - jffs2_erase_failed(priv->c, priv->jeb, instr->fail_addr); - } else { - jffs2_erase_succeeded(priv->c, priv->jeb); - } - kfree(instr); -} -#endif /* !__ECOS */ - /* Hmmm. Maybe we should accept the extra space it takes and make this a standard doubly-linked list? */ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 2a407dc9beaa..5018437d7999 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -48,8 +48,6 @@ struct erase_info { uint64_t addr; uint64_t len; uint64_t fail_addr; - void (*callback) (struct erase_info *self); - u_long priv; u_char state; }; -- cgit v1.2.3 From 8f347c4232d5fc097599b711a3385722a6834005 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 12 Feb 2018 22:03:10 +0100 Subject: mtd: Unconditionally update ->fail_addr and ->addr in part_erase() ->fail_addr and ->addr can be updated no matter the result of parent->_erase(), we just need to remove the code doing the same thing in mtd_erase_callback() to avoid adjusting those fields twice. Note that this can be done because all MTD users have been converted to not pass an erase_info->callback() and are thus only taking the ->addr_fail and ->addr fields into account after part_erase() has returned. While we're at it, get rid of the erase_info->mtd field which was only needed to let mtd_erase_callback() get the partition device back. Signed-off-by: Boris Brezillon Reviewed-by: Richard Weinberger --- drivers/mtd/ftl.c | 1 - drivers/mtd/inftlmount.c | 3 --- drivers/mtd/mtdblock.c | 1 - drivers/mtd/mtdchar.c | 1 - drivers/mtd/mtdconcat.c | 1 - drivers/mtd/mtdoops.c | 1 - drivers/mtd/mtdpart.c | 16 ++++------------ drivers/mtd/mtdswap.c | 2 -- drivers/mtd/nand/nand_base.c | 1 - drivers/mtd/nand/nand_bbt.c | 1 - drivers/mtd/nftlmount.c | 1 - drivers/mtd/rfd_ftl.c | 1 - drivers/mtd/sm_ftl.c | 1 - drivers/mtd/tests/mtd_test.c | 1 - drivers/mtd/tests/speedtest.c | 1 - drivers/mtd/ubi/io.c | 1 - fs/jffs2/erase.c | 1 - include/linux/mtd/mtd.h | 3 ++- 18 files changed, 6 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index fcf9907e7987..0a6adfaec7b5 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -342,7 +342,6 @@ static int erase_xfer(partition_t *part, if (!erase) return -ENOMEM; - erase->mtd = part->mbd.mtd; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 0f47be4834d8..aab4f68bd36f 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -208,8 +208,6 @@ static int find_boot_record(struct INFTLrecord *inftl) if (ip->Reserved0 != ip->firstUnit) { struct erase_info *instr = &inftl->instr; - instr->mtd = inftl->mbd.mtd; - /* * Most likely this is using the * undocumented qiuck mount feature. @@ -385,7 +383,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) _first_? */ /* Use async erase interface, test return code */ - instr->mtd = inftl->mbd.mtd; instr->addr = block * inftl->EraseSize; instr->len = inftl->mbd.mtd->erasesize; /* Erase one physical eraseblock at a time, even though the NAND api diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 7b2b7f651181..a5b1933c0490 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -65,7 +65,6 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, /* * First, let's erase the flash block. */ - erase.mtd = mtd; erase.addr = pos; erase.len = len; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 2beb22dd6bbb..c06b33f80e75 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -726,7 +726,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) erase->addr = einfo32.start; erase->len = einfo32.length; } - erase->mtd = mtd; ret = mtd_erase(mtd, erase); kfree(erase); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index caa09bf6e572..93c47e56d9d8 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -427,7 +427,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) erase->len = length; length -= erase->len; - erase->mtd = subdev; if ((err = mtd_erase(subdev, erase))) { /* sanity check: should never happen since * block alignment has been checked above */ diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 028ded59297b..9f25111fd559 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -94,7 +94,6 @@ static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset) int ret; int page; - erase.mtd = mtd; erase.addr = offset; erase.len = mtd->erasesize; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ae1206633d9d..1c07a6f0dfe5 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -205,23 +205,15 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) instr->addr += part->offset; ret = part->parent->_erase(part->parent, instr); - if (ret) { - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; - } + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + return ret; } void mtd_erase_callback(struct erase_info *instr) { - if (instr->mtd->_erase == part_erase) { - struct mtd_part *part = mtd_to_part(instr->mtd); - - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; - } } EXPORT_SYMBOL_GPL(mtd_erase_callback); diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index d390324d102e..7161f8a17f62 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -549,8 +549,6 @@ static int mtdswap_erase_block(struct mtdswap_dev *d, struct swap_eb *eb) retry: memset(&erase, 0, sizeof(struct erase_info)); - - erase.mtd = mtd; erase.addr = mtdswap_eb_offset(d, eb); erase.len = mtd->erasesize; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e70ca16a5118..16c8bc06975d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -527,7 +527,6 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) /* Attempt erase before marking OOB */ memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; einfo.addr = ofs; einfo.len = 1ULL << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 36092850be2c..d9f4ceff2568 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -852,7 +852,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, } memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; einfo.addr = to; einfo.len = 1 << this->bbt_erase_shift; res = nand_erase_nand(mtd, &einfo, 1); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 07e122449759..d8f6dba01c87 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -328,7 +328,6 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) memset(instr, 0, sizeof(struct erase_info)); /* XXX: use async erase interface, XXX: test return code */ - instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; if (mtd_erase(mtd, instr)) { diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 4e0b55cd08e2..df27f24ce0fa 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -275,7 +275,6 @@ static int erase_block(struct partition *part, int block) if (!erase) return -ENOMEM; - erase->mtd = part->mbd.mtd; erase->addr = part->blocks[block].offset; erase->len = part->block_size; diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index c11156f9d96f..72740ede9f05 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -460,7 +460,6 @@ static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, struct mtd_info *mtd = ftl->trans->mtd; struct erase_info erase; - erase.mtd = mtd; erase.addr = sm_mkoffset(ftl, zone_num, block, 0); erase.len = ftl->block_size; diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c index 0ac625e8f798..c84250beffdc 100644 --- a/drivers/mtd/tests/mtd_test.c +++ b/drivers/mtd/tests/mtd_test.c @@ -14,7 +14,6 @@ int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum) loff_t addr = (loff_t)ebnum * mtd->erasesize; memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; ei.addr = addr; ei.len = mtd->erasesize; diff --git a/drivers/mtd/tests/speedtest.c b/drivers/mtd/tests/speedtest.c index f8e5dc11f943..20edb3b49c77 100644 --- a/drivers/mtd/tests/speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -59,7 +59,6 @@ static int multiblock_erase(int ebnum, int blocks) loff_t addr = (loff_t)ebnum * mtd->erasesize; memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; ei.addr = addr; ei.len = mtd->erasesize * blocks; diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 8843d26837b2..0e3a76a9e2f8 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -333,7 +333,6 @@ static int do_sync_erase(struct ubi_device *ubi, int pnum) retry: memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = ubi->mtd; ei.addr = (loff_t)pnum * ubi->peb_size; ei.len = ubi->peb_size; diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 09bb6c00b869..83b8f06b4a64 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -59,7 +59,6 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, memset(instr, 0, sizeof(*instr)); - instr->mtd = c->mtd; instr->addr = jeb->offset; instr->len = c->sector_size; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 5018437d7999..4cbb7f555244 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -38,13 +38,14 @@ #define MTD_FAIL_ADDR_UNKNOWN -1LL +struct mtd_info; + /* * If the erase fails, fail_addr might indicate exactly which block failed. If * fail_addr = MTD_FAIL_ADDR_UNKNOWN, the failure was not at the device level * or was not specific to any particular block. */ struct erase_info { - struct mtd_info *mtd; uint64_t addr; uint64_t len; uint64_t fail_addr; -- cgit v1.2.3 From b958758e686aebe84672acc8871aca87d04f13a3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:19 +0100 Subject: mtd: rawnand: rename SET/GET FEATURES related functions SET/GET FEATURES are flagged ONFI-compliant because of their name. This is not accurate as non-ONFI NAND chips support it and use it. Rename the hooks and helpers to remove the "onfi" prefix. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c | 4 +-- drivers/mtd/nand/raw/cafe_nand.c | 4 +-- drivers/mtd/nand/raw/docg4.c | 4 +-- drivers/mtd/nand/raw/fsl_elbc_nand.c | 4 +-- drivers/mtd/nand/raw/fsl_ifc_nand.c | 4 +-- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 8 ++--- drivers/mtd/nand/raw/hisi504_nand.c | 4 +-- drivers/mtd/nand/raw/mpc5121_nfc.c | 4 +-- drivers/mtd/nand/raw/mxc_nand.c | 14 ++++---- drivers/mtd/nand/raw/nand_base.c | 42 +++++++++++------------- drivers/mtd/nand/raw/nand_micron.c | 16 ++++----- drivers/mtd/nand/raw/qcom_nandc.c | 4 +-- drivers/mtd/nand/raw/sh_flctl.c | 4 +-- drivers/staging/mt29f_spinand/mt29f_spinand.c | 4 +-- include/linux/mtd/rawnand.h | 17 +++++----- 15 files changed, 66 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c index 54bac5b73f0a..60874de430eb 100644 --- a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -392,8 +392,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte; b47n->nand_chip.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; b47n->nand_chip.write_buf = bcm47xxnflash_ops_bcm4706_write_buf; - b47n->nand_chip.onfi_set_features = nand_onfi_get_set_features_notsupp; - b47n->nand_chip.onfi_get_features = nand_onfi_get_set_features_notsupp; + b47n->nand_chip.set_features = nand_get_set_features_notsupp; + b47n->nand_chip.get_features = nand_get_set_features_notsupp; nand_chip->chip_delay = 50; b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index 37cfae2761d4..3c1b6a3786b2 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -645,8 +645,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.read_buf = cafe_read_buf; cafe->nand.write_buf = cafe_write_buf; cafe->nand.select_chip = cafe_select_chip; - cafe->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; - cafe->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; + cafe->nand.set_features = nand_get_set_features_notsupp; + cafe->nand.get_features = nand_get_set_features_notsupp; cafe->nand.chip_delay = 0; diff --git a/drivers/mtd/nand/raw/docg4.c b/drivers/mtd/nand/raw/docg4.c index 72f1327c4430..1314aa99b9ab 100644 --- a/drivers/mtd/nand/raw/docg4.c +++ b/drivers/mtd/nand/raw/docg4.c @@ -1269,8 +1269,8 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->read_buf = docg4_read_buf; nand->write_buf = docg4_write_buf16; nand->erase = docg4_erase_block; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + nand->set_features = nand_get_set_features_notsupp; + nand->get_features = nand_get_set_features_notsupp; nand->ecc.read_page = docg4_read_page; nand->ecc.write_page = docg4_write_page; nand->ecc.read_page_raw = docg4_read_page_raw; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 84f7d6a94be7..d28df991c73c 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -775,8 +775,8 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->select_chip = fsl_elbc_select_chip; chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 4f0bbc8a803d..7ca678f05ae3 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -838,8 +838,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->select_chip = fsl_ifc_select_chip; chip->cmdfunc = fsl_ifc_cmdfunc; chip->waitfunc = fsl_ifc_wait; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 97787246af41..f78d907ca61e 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -934,15 +934,15 @@ static int enable_edo_mode(struct gpmi_nand_data *this, int mode) /* [1] send SET FEATURE command to NAND */ feature[0] = mode; - ret = nand->onfi_set_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); + ret = nand->set_features(mtd, nand, + ONFI_FEATURE_ADDR_TIMING_MODE, feature); if (ret) goto err_out; /* [2] send GET FEATURE command to double-check the timing mode */ memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); - ret = nand->onfi_get_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); + ret = nand->get_features(mtd, nand, + ONFI_FEATURE_ADDR_TIMING_MODE, feature); if (ret || feature[0] != mode) goto err_out; diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index cb862793ab6d..27558a67fa41 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -762,8 +762,8 @@ static int hisi_nfc_probe(struct platform_device *pdev) chip->write_buf = hisi_nfc_write_buf; chip->read_buf = hisi_nfc_read_buf; chip->chip_delay = HINFC504_CHIP_DELAY; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; hisi_nfc_host_init(host); diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 913b9d1225c6..6d1740d54e0d 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -707,8 +707,8 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->read_buf = mpc5121_nfc_read_buf; chip->write_buf = mpc5121_nfc_write_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.algo = NAND_ECC_HAMMING; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 87b5ee66e501..61501654e708 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1421,9 +1421,8 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } -static int mxc_nand_onfi_set_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +static int mxc_nand_set_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { struct nand_chip *nand_chip = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(nand_chip); @@ -1447,9 +1446,8 @@ static int mxc_nand_onfi_set_features(struct mtd_info *mtd, return 0; } -static int mxc_nand_onfi_get_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +static int mxc_nand_get_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { struct nand_chip *nand_chip = mtd_to_nand(mtd); struct mxc_nand_host *host = nand_get_controller_data(nand_chip); @@ -1752,8 +1750,8 @@ static int mxcnd_probe(struct platform_device *pdev) this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; - this->onfi_set_features = mxc_nand_onfi_set_features; - this->onfi_get_features = mxc_nand_onfi_get_features; + this->set_features = mxc_nand_set_features; + this->get_features = mxc_nand_get_features; host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index d1d5e2281860..802cd9ed44a1 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -349,7 +349,7 @@ static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) * 8-bits of the data bus. During address transfers, the host shall * set the upper 8-bits of the data bus to 00h. * - * One user of the write_byte callback is nand_onfi_set_features. The + * One user of the write_byte callback is nand_set_features. The * four parameters are specified to be written to I/O[7:0], but this is * neither an address nor a command transfer. Let's assume a 0 on the * upper I/O lines is OK. @@ -1231,9 +1231,9 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) chip->onfi_timing_mode_default, }; - ret = chip->onfi_set_features(mtd, chip, - ONFI_FEATURE_ADDR_TIMING_MODE, - tmode_param); + ret = chip->set_features(mtd, chip, + ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); if (ret) goto err; } @@ -4769,15 +4769,15 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) } /** - * nand_default_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * nand_default_set_features- [REPLACEABLE] set NAND chip features * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. * @subfeature_param: the subfeature parameters, a four bytes array. */ -static int nand_default_onfi_set_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - uint8_t *subfeature_param) +static int nand_default_set_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + uint8_t *subfeature_param) { if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) @@ -4788,15 +4788,15 @@ static int nand_default_onfi_set_features(struct mtd_info *mtd, } /** - * nand_default_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * nand_default_get_features- [REPLACEABLE] get NAND chip features * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. * @subfeature_param: the subfeature parameters, a four bytes array. */ -static int nand_default_onfi_get_features(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - uint8_t *subfeature_param) +static int nand_default_get_features(struct mtd_info *mtd, + struct nand_chip *chip, int addr, + uint8_t *subfeature_param) { if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) @@ -4807,8 +4807,7 @@ static int nand_default_onfi_get_features(struct mtd_info *mtd, } /** - * nand_onfi_get_set_features_notsupp - set/get features stub returning - * -ENOTSUPP + * nand_get_set_features_notsupp - set/get features stub returning -ENOTSUPP * @mtd: MTD device structure * @chip: nand chip info structure * @addr: feature address. @@ -4817,13 +4816,12 @@ static int nand_default_onfi_get_features(struct mtd_info *mtd, * Should be used by NAND controller drivers that do not support the SET/GET * FEATURES operations. */ -int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param) +int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param) { return -ENOTSUPP; } -EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp); +EXPORT_SYMBOL(nand_get_set_features_notsupp); /** * nand_suspend - [MTD Interface] Suspend the NAND flash @@ -4880,10 +4878,10 @@ static void nand_set_defaults(struct nand_chip *chip) chip->select_chip = nand_select_chip; /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_default_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_default_onfi_get_features; + if (!chip->set_features) + chip->set_features = nand_default_set_features; + if (!chip->get_features) + chip->get_features = nand_default_get_features; /* If called twice, pointers that depend on busw may need to be reset */ if (!chip->read_byte || chip->read_byte == nand_read_byte) diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 02e109ae73f1..ab3e3a1a5212 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -48,8 +48,8 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) struct nand_chip *chip = mtd_to_nand(mtd); u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; - return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, - feature); + return chip->set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, + feature); } /* @@ -108,8 +108,8 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) if (enable) feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; - return chip->onfi_set_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + return chip->set_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); } static int @@ -219,8 +219,8 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + chip->get_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) return MICRON_ON_DIE_UNSUPPORTED; @@ -228,8 +228,8 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->onfi_get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + chip->get_features(nand_to_mtd(chip), chip, + ONFI_FEATURE_ON_DIE_ECC, feature); if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) return MICRON_ON_DIE_MANDATORY; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 563b759ffca6..b554fb6e609c 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2651,8 +2651,8 @@ static int qcom_nand_host_init(struct qcom_nand_controller *nandc, chip->read_byte = qcom_nandc_read_byte; chip->read_buf = qcom_nandc_read_buf; chip->write_buf = qcom_nandc_write_buf; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; /* * the bad block marker is readable only when we read the last codeword diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index a60d43adff3c..7a740d583866 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1180,8 +1180,8 @@ static int flctl_probe(struct platform_device *pdev) nand->read_buf = flctl_read_buf; nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; - nand->onfi_set_features = nand_onfi_get_set_features_notsupp; - nand->onfi_get_features = nand_onfi_get_set_features_notsupp; + nand->set_features = nand_get_set_features_notsupp; + nand->get_features = nand_get_set_features_notsupp; if (pdata->flcmncr_val & SEL_16BIT) nand->options |= NAND_BUSWIDTH_16; diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index 264ad362d858..6819dd2c1117 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -918,8 +918,8 @@ static int spinand_probe(struct spi_device *spi_nand) chip->waitfunc = spinand_wait; chip->options |= NAND_CACHEPRG; chip->select_chip = spinand_select_chip; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; + chip->set_features = nand_get_set_features_notsupp; + chip->get_features = nand_get_set_features_notsupp; mtd = nand_to_mtd(chip); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 56c5570aadbe..fb2e288ef8b1 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1170,8 +1170,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * @blocks_per_die: [INTERN] The number of PEBs in a die * @data_interface: [INTERN] NAND interface timing information * @read_retries: [INTERN] the number of read retry modes supported - * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand - * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand + * @set_features: [REPLACEABLE] set the NAND chip features + * @get_features: [REPLACEABLE] get the NAND chip features * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If * chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this * means the configuration should not be applied but @@ -1212,10 +1212,10 @@ struct nand_chip { bool check_only); int (*erase)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); - int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip, - int feature_addr, uint8_t *subfeature_para); - int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, - int feature_addr, uint8_t *subfeature_para); + int (*set_features)(struct mtd_info *mtd, struct nand_chip *chip, + int feature_addr, uint8_t *subfeature_para); + int (*get_features)(struct mtd_info *mtd, struct nand_chip *chip, + int feature_addr, uint8_t *subfeature_para); int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode); int (*setup_data_interface)(struct mtd_info *mtd, int chipnr, const struct nand_data_interface *conf); @@ -1630,9 +1630,8 @@ int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page); /* Stub used by drivers that do not support GET/SET FEATURES operations */ -int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd, - struct nand_chip *chip, int addr, - u8 *subfeature_param); +int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip, + int addr, u8 *subfeature_param); /* Default read_page_raw implementation */ int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, -- cgit v1.2.3 From 97baea1e6b74c73973fa0922252f880ab15450ea Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:20 +0100 Subject: mtd: rawnand: use wrappers to call onfi GET/SET_FEATURES Prepare the fact that some features managed by GET/SET_FEATURES could be overloaded by vendor code. To handle this logic, use new wrappers instead of directly call the ->get/set_features() hooks. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 6 +-- drivers/mtd/nand/raw/nand_base.c | 89 ++++++++++++++++++++----------- drivers/mtd/nand/raw/nand_micron.c | 18 ++++--- include/linux/mtd/rawnand.h | 3 ++ 4 files changed, 74 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index f78d907ca61e..039f2795617f 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -934,15 +934,13 @@ static int enable_edo_mode(struct gpmi_nand_data *this, int mode) /* [1] send SET FEATURE command to NAND */ feature[0] = mode; - ret = nand->set_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); + ret = nand_set_features(nand, ONFI_FEATURE_ADDR_TIMING_MODE, feature); if (ret) goto err_out; /* [2] send GET FEATURE command to double-check the timing mode */ memset(feature, 0, ONFI_SUBFEATURE_PARAM_LEN); - ret = nand->get_features(mtd, nand, - ONFI_FEATURE_ADDR_TIMING_MODE, feature); + ret = nand_get_features(nand, ONFI_FEATURE_ADDR_TIMING_MODE, feature); if (ret || feature[0] != mode) goto err_out; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 802cd9ed44a1..d344dcb12620 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1160,6 +1160,55 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) return status; } +static bool nand_supports_set_get_features(struct nand_chip *chip) +{ + return (chip->onfi_version && + (le16_to_cpu(chip->onfi_params.opt_cmd) & + ONFI_OPT_CMD_SET_GET_FEATURES)); +} + +/** + * nand_get_features - wrapper to perform a GET_FEATURE + * @chip: NAND chip info structure + * @addr: feature address + * @subfeature_param: the subfeature parameters, a four bytes array + * + * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the + * operation cannot be handled. + */ +int nand_get_features(struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!nand_supports_set_get_features(chip)) + return -ENOTSUPP; + + return chip->get_features(mtd, chip, addr, subfeature_param); +} +EXPORT_SYMBOL_GPL(nand_get_features); + +/** + * nand_set_features - wrapper to perform a SET_FEATURE + * @chip: NAND chip info structure + * @addr: feature address + * @subfeature_param: the subfeature parameters, a four bytes array + * + * Returns 0 for success, a negative error otherwise. Returns -ENOTSUPP if the + * operation cannot be handled. + */ +int nand_set_features(struct nand_chip *chip, int addr, + u8 *subfeature_param) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!nand_supports_set_get_features(chip)) + return -ENOTSUPP; + + return chip->set_features(mtd, chip, addr, subfeature_param); +} +EXPORT_SYMBOL_GPL(nand_set_features); + /** * nand_reset_data_interface - Reset data interface and timings * @chip: The NAND chip @@ -1215,32 +1264,22 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) { struct mtd_info *mtd = nand_to_mtd(chip); + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { + chip->onfi_timing_mode_default, + }; int ret; if (!chip->setup_data_interface) return 0; - /* - * Ensure the timing mode has been changed on the chip side - * before changing timings on the controller side. - */ - if (chip->onfi_version && - (le16_to_cpu(chip->onfi_params.opt_cmd) & - ONFI_OPT_CMD_SET_GET_FEATURES)) { - u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { - chip->onfi_timing_mode_default, - }; - - ret = chip->set_features(mtd, chip, - ONFI_FEATURE_ADDR_TIMING_MODE, - tmode_param); - if (ret) - goto err; - } + /* Change the mode on the chip side */ + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + if (ret) + return ret; - ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); -err: - return ret; + /* Change the mode on the controller side */ + return chip->setup_data_interface(mtd, chipnr, &chip->data_interface); } /** @@ -4779,11 +4818,6 @@ static int nand_default_set_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - return nand_set_features_op(chip, addr, subfeature_param); } @@ -4798,11 +4832,6 @@ static int nand_default_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -EINVAL; - return nand_get_features_op(chip, addr, subfeature_param); } diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index ab3e3a1a5212..b825656f6284 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -48,8 +48,7 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) struct nand_chip *chip = mtd_to_nand(mtd); u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; - return chip->set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, - feature); + return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); } /* @@ -108,8 +107,7 @@ static int micron_nand_on_die_ecc_setup(struct nand_chip *chip, bool enable) if (enable) feature[0] |= ONFI_FEATURE_ON_DIE_ECC_EN; - return chip->set_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + return nand_set_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); } static int @@ -219,8 +217,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + if (ret < 0) + return ret; + if ((feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) == 0) return MICRON_ON_DIE_UNSUPPORTED; @@ -228,8 +228,10 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) if (ret) return MICRON_ON_DIE_UNSUPPORTED; - chip->get_features(nand_to_mtd(chip), chip, - ONFI_FEATURE_ON_DIE_ECC, feature); + ret = nand_get_features(chip, ONFI_FEATURE_ON_DIE_ECC, feature); + if (ret < 0) + return ret; + if (feature[0] & ONFI_FEATURE_ON_DIE_ECC_EN) return MICRON_ON_DIE_MANDATORY; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index fb2e288ef8b1..3cc2a3435b20 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1629,6 +1629,9 @@ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page); +/* Wrapper to use in order for controllers/vendors to GET/SET FEATURES */ +int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param); +int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param); /* Stub used by drivers that do not support GET/SET FEATURES operations */ int nand_get_set_features_notsupp(struct mtd_info *mtd, struct nand_chip *chip, int addr, u8 *subfeature_param); -- cgit v1.2.3 From f4531b2b1929806d2bec1a2f19805031d8bc0806 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:26 +0100 Subject: mtd: rawnand: prepare the removal of ONFI/JEDEC parameter pages The NAND chip parameter page is statically allocated within the nand_chip structure, which reserves a lot of space. Even not ONFI nor JEDEC chips have it embedded. Also, only a few parameters are actually read from the parameter page after the detection. To prepare to the removal of such huge structure, a small NAND parameter structure is allocated statically and contains only very few members that are generic to all chips and actually used elsewhere in the code. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 39 +++++++++++++++++---------------------- include/linux/mtd/rawnand.h | 13 +++++++++++++ 2 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 4099d8a1e25e..0f1f45526c7f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1162,9 +1162,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) static bool nand_supports_set_get_features(struct nand_chip *chip) { - return (chip->onfi_version && - (le16_to_cpu(chip->onfi_params.opt_cmd) & - ONFI_OPT_CMD_SET_GET_FEATURES)); + return chip->parameters.supports_set_get_features; } /** @@ -5145,8 +5143,8 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; + strncpy(chip->parameters.model, p->model, + sizeof(chip->parameters.model) - 1); mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5193,6 +5191,10 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) pr_warn("Could not retrieve ONFI ECC requirements\n"); } + /* Save some parameters from the parameter page for future use */ + if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) + chip->parameters.supports_set_get_features = true; + return 1; } @@ -5245,8 +5247,8 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; + strncpy(chip->parameters.model, p->model, + sizeof(chip->parameters.model) - 1); mtd->writesize = le32_to_cpu(p->byte_per_page); @@ -5433,8 +5435,8 @@ static bool find_full_id_nand(struct nand_chip *chip, chip->onfi_timing_mode_default = type->onfi_timing_mode_default; - if (!mtd->name) - mtd->name = type->name; + strncpy(chip->parameters.model, type->name, + sizeof(chip->parameters.model) - 1); return true; } @@ -5587,8 +5589,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) if (!type->name) return -ENODEV; - if (!mtd->name) - mtd->name = type->name; + strncpy(chip->parameters.model, type->name, + sizeof(chip->parameters.model) - 1); chip->chipsize = (uint64_t)type->chipsize << 20; @@ -5601,6 +5603,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) chip->options |= type->options; ident_done: + if (!mtd->name) + mtd->name = chip->parameters.model; if (chip->options & NAND_BUSWIDTH_AUTO) { WARN_ON(busw & NAND_BUSWIDTH_16); @@ -5647,17 +5651,8 @@ ident_done: pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", maf_id, dev_id); - - if (chip->onfi_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->onfi_params.model); - else if (chip->jedec_version) - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - chip->jedec_params.model); - else - pr_info("%s %s\n", nand_manufacturer_name(manufacturer), - type->name); - + pr_info("%s %s\n", nand_manufacturer_name(manufacturer), + chip->parameters.model); pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3cc2a3435b20..a24591411d78 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -429,6 +429,16 @@ struct nand_jedec_params { __le16 crc; } __packed; +/** + * struct nand_parameters - NAND generic parameters from the parameter page + * @model: Model name + * @supports_set_get_features: The NAND chip supports setting/getting features + */ +struct nand_parameters { + char model[100]; + bool supports_set_get_features; +}; + /* The maximum expected count of bytes in the NAND ID sequence */ #define NAND_MAX_ID_LEN 8 @@ -1165,6 +1175,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * supported, 0 otherwise. * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is * supported, 0 otherwise. + * @parameters: [INTERN] holds generic parameters under an easily + * readable form. * @max_bb_per_die: [INTERN] the max number of bad blocks each die of a * this nand device will encounter their life times. * @blocks_per_die: [INTERN] The number of PEBs in a die @@ -1249,6 +1261,7 @@ struct nand_chip { struct nand_onfi_params onfi_params; struct nand_jedec_params jedec_params; }; + struct nand_parameters parameters; u16 max_bb_per_die; u32 blocks_per_die; -- cgit v1.2.3 From a97421c7532d382ab560ca153bdf9450f97c7e41 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:27 +0100 Subject: mtd: rawnand: prepare the removal of the ONFI parameter page The NAND chip parameter page is statically allocated within the nand_chip structure, which reserves a lot of space. Even not ONFI nor JEDEC chips have it embedded. Also, only a few parameters are actually read from the parameter page after the detection. ONFI-related parameters that will be used outside from the identification function are stored in a separate onfi_parameters structure embedded in nand_parameters, this small structure that already hold generic parameters. For now, the onfi_parameters structure is allocated statically. However, after some deep rework in the NAND framework, it will be possible to do dynamic allocations from the NAND identification phase, and this strcuture will then be dynamically allocated when needed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 30 +++++++++++++------- drivers/mtd/nand/raw/nand_micron.c | 19 ++++++------- drivers/mtd/nand/raw/nand_timings.c | 12 ++++---- include/linux/mtd/rawnand.h | 47 +++++++++++++++++++------------ 5 files changed, 64 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 039f2795617f..f3cc38372d79 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -971,7 +971,7 @@ int gpmi_extra_init(struct gpmi_nand_data *this) struct nand_chip *chip = &this->nand; /* Enable the asynchronous EDO feature. */ - if (GPMI_IS_MX6(this) && chip->onfi_version) { + if (GPMI_IS_MX6(this) && chip->parameters.onfi.version) { int mode = onfi_get_async_timing_mode(chip); /* We only support the timing mode 4 and mode 5. */ diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 0f1f45526c7f..789c11e0925e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5126,17 +5126,17 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 5)) - chip->onfi_version = 23; + chip->parameters.onfi.version = 23; else if (val & (1 << 4)) - chip->onfi_version = 22; + chip->parameters.onfi.version = 22; else if (val & (1 << 3)) - chip->onfi_version = 21; + chip->parameters.onfi.version = 21; else if (val & (1 << 2)) - chip->onfi_version = 20; + chip->parameters.onfi.version = 20; else if (val & (1 << 1)) - chip->onfi_version = 10; + chip->parameters.onfi.version = 10; - if (!chip->onfi_version) { + if (!chip->parameters.onfi.version) { pr_info("unsupported ONFI version: %d\n", val); return 0; } @@ -5166,14 +5166,14 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun); chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun); - if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) + if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) chip->options |= NAND_BUSWIDTH_16; if (p->ecc_bits != 0xff) { chip->ecc_strength_ds = p->ecc_bits; chip->ecc_step_ds = 512; - } else if (chip->onfi_version >= 21 && - (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + } else if (chip->parameters.onfi.version >= 21 && + (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { /* * The nand_flash_detect_ext_param_page() uses the @@ -5194,6 +5194,16 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) /* Save some parameters from the parameter page for future use */ if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) chip->parameters.supports_set_get_features = true; + chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog); + chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers); + chip->parameters.onfi.tR = le16_to_cpu(p->t_r); + chip->parameters.onfi.tCCS = le16_to_cpu(p->t_ccs); + chip->parameters.onfi.async_timing_mode = + le16_to_cpu(p->async_timing_mode); + chip->parameters.onfi.vendor_revision = + le16_to_cpu(p->vendor_revision); + memcpy(chip->parameters.onfi.vendor, p->vendor, + sizeof(p->vendor)); return 1; } @@ -5575,7 +5585,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) } } - chip->onfi_version = 0; + chip->parameters.onfi.version = 0; if (!type->name || !type->pagesize) { /* Check if the chip is ONFI compliant */ if (nand_flash_detect_onfi(chip)) diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index b825656f6284..c5974d8313e7 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -56,17 +56,14 @@ static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) */ static int micron_nand_onfi_init(struct nand_chip *chip) { - struct nand_onfi_params *p = &chip->onfi_params; - struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + struct nand_parameters *p = &chip->parameters; + struct nand_onfi_vendor_micron *micron = (void *)p->onfi.vendor; - if (!chip->onfi_version) - return 0; - - if (le16_to_cpu(p->vendor_revision) < 1) - return 0; + if (chip->parameters.onfi.version && p->onfi.vendor_revision) { + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = micron_nand_setup_read_retry; + } - chip->read_retries = micron->read_retry_options; - chip->setup_read_retry = micron_nand_setup_read_retry; return 0; } @@ -207,7 +204,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = { 0, }; int ret; - if (chip->onfi_version == 0) + if (!chip->parameters.onfi.version) return MICRON_ON_DIE_UNSUPPORTED; if (chip->bits_per_cell != 1) @@ -239,7 +236,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) * Some Micron NANDs have an on-die ECC of 4/512, some other * 8/512. We only support the former. */ - if (chip->onfi_params.ecc_bits != 4) + if (chip->ecc_strength_ds != 4) return MICRON_ON_DIE_UNSUPPORTED; return MICRON_ON_DIE_SUPPORTED; diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c index 9400d039ddbd..7c4e4a371bbc 100644 --- a/drivers/mtd/nand/raw/nand_timings.c +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -306,17 +306,17 @@ int onfi_fill_data_interface(struct nand_chip *chip, * tR, tPROG, tCCS, ... * These information are part of the ONFI parameter page. */ - if (chip->onfi_version) { - struct nand_onfi_params *params = &chip->onfi_params; + if (chip->parameters.onfi.version) { + struct nand_parameters *params = &chip->parameters; struct nand_sdr_timings *timings = &iface->timings.sdr; /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); - timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); - timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); + timings->tPROG_max = 1000000ULL * params->onfi.tPROG; + timings->tBERS_max = 1000000ULL * params->onfi.tBERS; + timings->tR_max = 1000000ULL * params->onfi.tR; /* nanoseconds -> picoseconds */ - timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + timings->tCCS_min = 1000UL * params->onfi.tCCS; } return 0; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index a24591411d78..7b5afa6ef5a9 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -429,14 +429,41 @@ struct nand_jedec_params { __le16 crc; } __packed; +/** + * struct onfi_params - ONFI specific parameters that will be reused + * @version: ONFI version (BCD encoded), 0 if ONFI is not supported + * @tPROG: Page program time + * @tBERS: Block erase time + * @tR: Page read time + * @tCCS: Change column setup time + * @async_timing_mode: Supported asynchronous timing mode + * @vendor_revision: Vendor specific revision number + * @vendor: Vendor specific data + */ +struct onfi_params { + int version; + u16 tPROG; + u16 tBERS; + u16 tR; + u16 tCCS; + u16 async_timing_mode; + u16 vendor_revision; + u8 vendor[88]; +}; + /** * struct nand_parameters - NAND generic parameters from the parameter page * @model: Model name * @supports_set_get_features: The NAND chip supports setting/getting features + * @onfi: ONFI specific parameters */ struct nand_parameters { + /* Generic parameters */ char model[100]; bool supports_set_get_features; + + /* ONFI parameters */ + struct onfi_params onfi; }; /* The maximum expected count of bytes in the NAND ID sequence */ @@ -1167,8 +1194,6 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @id: [INTERN] holds NAND ID - * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), - * non 0 if ONFI supported. * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded), * non 0 if JEDEC supported. * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is @@ -1255,7 +1280,6 @@ struct nand_chip { int badblockbits; struct nand_id id; - int onfi_version; int jedec_version; union { struct nand_onfi_params onfi_params; @@ -1548,26 +1572,13 @@ struct platform_nand_data { struct platform_nand_ctrl ctrl; }; -/* return the supported features. */ -static inline int onfi_feature(struct nand_chip *chip) -{ - return chip->onfi_version ? le16_to_cpu(chip->onfi_params.features) : 0; -} - /* return the supported asynchronous timing mode. */ static inline int onfi_get_async_timing_mode(struct nand_chip *chip) { - if (!chip->onfi_version) + if (!chip->parameters.onfi.version) return ONFI_TIMING_MODE_UNKNOWN; - return le16_to_cpu(chip->onfi_params.async_timing_mode); -} -/* return the supported synchronous timing mode. */ -static inline int onfi_get_sync_timing_mode(struct nand_chip *chip) -{ - if (!chip->onfi_version) - return ONFI_TIMING_MODE_UNKNOWN; - return le16_to_cpu(chip->onfi_params.src_sync_timing_mode); + return chip->parameters.onfi.async_timing_mode; } int onfi_fill_data_interface(struct nand_chip *chip, -- cgit v1.2.3 From 789157e41a0694e70bf80bceecd79438c3de98d6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:28 +0100 Subject: mtd: rawnand: allow vendors to declare (un)supported features If SET/GET_FEATURES is available (from the parameter page), use a bitmap to declare what feature is actually supported. Initialize the bitmap in the core to support timing changes (only feature used by the core), also add support for Micron specific features used in Micron initialization code (in the init routine). Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 26 +++++++++++++++++++------- drivers/mtd/nand/raw/nand_micron.c | 4 ++++ include/linux/mtd/rawnand.h | 8 +++++++- 3 files changed, 30 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 789c11e0925e..0bd9f22973e9 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1160,9 +1160,16 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) return status; } -static bool nand_supports_set_get_features(struct nand_chip *chip) +static bool nand_supports_get_features(struct nand_chip *chip, int addr) { - return chip->parameters.supports_set_get_features; + return (chip->parameters.supports_set_get_features && + test_bit(addr, chip->parameters.get_feature_list)); +} + +static bool nand_supports_set_features(struct nand_chip *chip, int addr) +{ + return (chip->parameters.supports_set_get_features && + test_bit(addr, chip->parameters.set_feature_list)); } /** @@ -1179,7 +1186,7 @@ int nand_get_features(struct nand_chip *chip, int addr, { struct mtd_info *mtd = nand_to_mtd(chip); - if (!nand_supports_set_get_features(chip)) + if (!nand_supports_get_features(chip, addr)) return -ENOTSUPP; return chip->get_features(mtd, chip, addr, subfeature_param); @@ -1200,7 +1207,7 @@ int nand_set_features(struct nand_chip *chip, int addr, { struct mtd_info *mtd = nand_to_mtd(chip); - if (!nand_supports_set_get_features(chip)) + if (!nand_supports_set_features(chip, addr)) return -ENOTSUPP; return chip->set_features(mtd, chip, addr, subfeature_param); @@ -1271,7 +1278,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return 0; /* Change the mode on the chip side (if supported by the NAND chip) */ - if (nand_supports_set_get_features(chip)) { + if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) { chip->select_chip(mtd, chipnr); ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); @@ -1286,7 +1293,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return ret; /* Check the mode has been accepted by the chip, if supported */ - if (!nand_supports_set_get_features(chip)) + if (!nand_supports_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) return 0; memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN); @@ -5192,8 +5199,13 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) } /* Save some parameters from the parameter page for future use */ - if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) + if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { chip->parameters.supports_set_get_features = true; + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + } chip->parameters.onfi.tPROG = le16_to_cpu(p->t_prog); chip->parameters.onfi.tBERS = le16_to_cpu(p->t_bers); chip->parameters.onfi.tR = le16_to_cpu(p->t_r); diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index c5974d8313e7..0af45b134c0c 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -64,6 +64,10 @@ static int micron_nand_onfi_init(struct nand_chip *chip) chip->setup_read_retry = micron_nand_setup_read_retry; } + if (p->supports_set_get_features) { + set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->set_feature_list); + set_bit(ONFI_FEATURE_ADDR_READ_RETRY, p->get_feature_list); + } return 0; } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 7b5afa6ef5a9..d9f417719d36 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -21,6 +21,7 @@ #include #include #include +#include struct mtd_info; struct nand_flash_dev; @@ -235,7 +236,8 @@ struct nand_chip; #define ONFI_TIMING_MODE_5 (1 << 5) #define ONFI_TIMING_MODE_UNKNOWN (1 << 6) -/* ONFI feature address */ +/* ONFI feature number/address */ +#define ONFI_FEATURE_NUMBER 256 #define ONFI_FEATURE_ADDR_TIMING_MODE 0x1 /* Vendor-specific feature address (Micron) */ @@ -455,12 +457,16 @@ struct onfi_params { * struct nand_parameters - NAND generic parameters from the parameter page * @model: Model name * @supports_set_get_features: The NAND chip supports setting/getting features + * @set_feature_list: Bitmap of features that can be set + * @get_feature_list: Bitmap of features that can be get * @onfi: ONFI specific parameters */ struct nand_parameters { /* Generic parameters */ char model[100]; bool supports_set_get_features; + DECLARE_BITMAP(set_feature_list, ONFI_FEATURE_NUMBER); + DECLARE_BITMAP(get_feature_list, ONFI_FEATURE_NUMBER); /* ONFI parameters */ struct onfi_params onfi; -- cgit v1.2.3 From 480139d9229e3be0530bc548da208b5f49b1ab90 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:30 +0100 Subject: mtd: rawnand: get rid of the JEDEC parameter page in nand_chip The NAND chip parameter page is statically allocated within the nand_chip structure, which reserves a lot of space. Even not ONFI nor JEDEC chips have it embedded. Also, only a few parameters are actually read from the parameter page after the detection. Now that there is a small nand_parameters structure that can held generic parameters, remove the JEDEC page from the nand_chip structure by just allocating it during the identification phase and removing it right after. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 41 +++++++++++++++++++++++++++------------- include/linux/mtd/rawnand.h | 17 +---------------- 2 files changed, 29 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 0bd9f22973e9..79348165e4a3 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5226,8 +5226,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) static int nand_flash_detect_jedec(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_jedec_params *p = &chip->jedec_params; + struct nand_jedec_params *p; struct jedec_ecc_info *ecc; + int jedec_version = 0; char id[5]; int i, val, ret; @@ -5236,14 +5237,23 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) if (ret || strncmp(id, "JEDEC", sizeof(id))) return 0; + /* JEDEC chip: allocate a buffer to hold its parameter page */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + ret = nand_read_param_page_op(chip, 0x40, NULL, 0); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_jedec_param_page; + } for (i = 0; i < 3; i++) { ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_jedec_param_page; + } if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == le16_to_cpu(p->crc)) @@ -5252,19 +5262,19 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) if (i == 3) { pr_err("Could not find valid JEDEC parameter page; aborting\n"); - return 0; + goto free_jedec_param_page; } /* Check version */ val = le16_to_cpu(p->revision); if (val & (1 << 2)) - chip->jedec_version = 10; + jedec_version = 10; else if (val & (1 << 1)) - chip->jedec_version = 1; /* vendor specific version */ + jedec_version = 1; /* vendor specific version */ - if (!chip->jedec_version) { + if (!jedec_version) { pr_info("unsupported JEDEC version: %d\n", val); - return 0; + goto free_jedec_param_page; } sanitize_string(p->manufacturer, sizeof(p->manufacturer)); @@ -5285,7 +5295,7 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; chip->bits_per_cell = p->bits_per_cell; - if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS) chip->options |= NAND_BUSWIDTH_16; /* ECC info */ @@ -5298,7 +5308,9 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) pr_warn("Invalid codeword size\n"); } - return 1; +free_jedec_param_page: + kfree(p); + return ret; } /* @@ -5604,7 +5616,10 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) goto ident_done; /* Check if the chip is JEDEC compliant */ - if (nand_flash_detect_jedec(chip)) + ret = nand_flash_detect_jedec(chip); + if (ret < 0) + return ret; + else if (ret) goto ident_done; } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index d9f417719d36..cf82a959b0f3 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1200,12 +1200,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @id: [INTERN] holds NAND ID - * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded), - * non 0 if JEDEC supported. * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is * supported, 0 otherwise. - * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is - * supported, 0 otherwise. * @parameters: [INTERN] holds generic parameters under an easily * readable form. * @max_bb_per_die: [INTERN] the max number of bad blocks each die of a @@ -1286,11 +1282,7 @@ struct nand_chip { int badblockbits; struct nand_id id; - int jedec_version; - union { - struct nand_onfi_params onfi_params; - struct nand_jedec_params jedec_params; - }; + struct nand_onfi_params onfi_params; struct nand_parameters parameters; u16 max_bb_per_die; u32 blocks_per_die; @@ -1621,13 +1613,6 @@ static inline int nand_opcode_8bits(unsigned int command) return 0; } -/* return the supported JEDEC features. */ -static inline int jedec_feature(struct nand_chip *chip) -{ - return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features) - : 0; -} - /* get timing characteristics from ONFI timing mode. */ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); -- cgit v1.2.3 From bd0b64340c2d66c0fe1aa99b0b23159d7e0c21f2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 19 Mar 2018 14:47:31 +0100 Subject: mtd: rawnand: get rid of the ONFI parameter page in nand_chip The NAND chip parameter page is statically allocated within the nand_chip structure, which reserves a lot of space. Even not ONFI nor JEDEC chips have it embedded. Also, only a few parameters are actually read from the parameter page after the detection. Now that there is a small nand_parameters structure that hold all needed ONFI parameters, remove the ONFI page from the nand_chip structure by just allocating it during the identification phase and removing it right after. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 34 +++++++++++++++++++++++++--------- include/linux/mtd/rawnand.h | 3 --- 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 79348165e4a3..d0b993fcf3a5 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5101,7 +5101,7 @@ ext_out: static int nand_flash_detect_onfi(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_onfi_params *p = &chip->onfi_params; + struct nand_onfi_params *p; char id[4]; int i, ret, val; @@ -5110,14 +5110,23 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (ret || strncmp(id, "ONFI", 4)) return 0; + /* ONFI chip: allocate a buffer to hold its parameter page */ + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + ret = nand_read_param_page_op(chip, 0, NULL, 0); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_onfi_param_page; + } for (i = 0; i < 3; i++) { ret = nand_read_data_op(chip, p, sizeof(*p), true); - if (ret) - return 0; + if (ret) { + ret = 0; + goto free_onfi_param_page; + } if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { @@ -5127,7 +5136,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (i == 3) { pr_err("Could not find valid ONFI parameter page; aborting\n"); - return 0; + goto free_onfi_param_page; } /* Check version */ @@ -5145,7 +5154,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) if (!chip->parameters.onfi.version) { pr_info("unsupported ONFI version: %d\n", val); - return 0; + goto free_onfi_param_page; + } else { + ret = 1; } sanitize_string(p->manufacturer, sizeof(p->manufacturer)); @@ -5217,7 +5228,9 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) memcpy(chip->parameters.onfi.vendor, p->vendor, sizeof(p->vendor)); - return 1; +free_onfi_param_page: + kfree(p); + return ret; } /* @@ -5612,7 +5625,10 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) chip->parameters.onfi.version = 0; if (!type->name || !type->pagesize) { /* Check if the chip is ONFI compliant */ - if (nand_flash_detect_onfi(chip)) + ret = nand_flash_detect_onfi(chip); + if (ret < 0) + return ret; + else if (ret) goto ident_done; /* Check if the chip is JEDEC compliant */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index cf82a959b0f3..5dad59b31244 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1200,8 +1200,6 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @id: [INTERN] holds NAND ID - * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is - * supported, 0 otherwise. * @parameters: [INTERN] holds generic parameters under an easily * readable form. * @max_bb_per_die: [INTERN] the max number of bad blocks each die of a @@ -1282,7 +1280,6 @@ struct nand_chip { int badblockbits; struct nand_id id; - struct nand_onfi_params onfi_params; struct nand_parameters parameters; u16 max_bb_per_die; u32 blocks_per_die; -- cgit v1.2.3 From e7bfb3fdbde3bfeeeb64e2d73ac6babe59519c9e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 12 Feb 2018 22:03:11 +0100 Subject: mtd: Stop updating erase_info->state and calling mtd_erase_callback() MTD users are no longer checking erase_info->state to determine if the erase operation failed or succeeded. Moreover, mtd_erase_callback() is now a NOP. We can safely get rid of all mtd_erase_callback() calls and all erase_info->state assignments. While at it, get rid of the erase_info->state field, all MTD_ERASE_XXX definitions and the mtd_erase_callback() function. Signed-off-by: Boris Brezillon Reviewed-by: Richard Weinberger Reviewed-by: Miquel Raynal Acked-by: Bert Kenward --- Changes in v2: - Address a few coding style issues (reported by Miquel) - Remove comments that are no longer valid (reported by Miquel) --- drivers/mtd/chips/cfi_cmdset_0001.c | 16 ++-------------- drivers/mtd/chips/cfi_cmdset_0002.c | 26 +++----------------------- drivers/mtd/chips/cfi_cmdset_0020.c | 3 --- drivers/mtd/chips/map_ram.c | 2 -- drivers/mtd/devices/bcm47xxsflash.c | 9 +-------- drivers/mtd/devices/block2mtd.c | 7 +------ drivers/mtd/devices/docg3.c | 16 ++-------------- drivers/mtd/devices/lart.c | 6 ------ drivers/mtd/devices/mtd_dataflash.c | 4 ---- drivers/mtd/devices/mtdram.c | 3 +-- drivers/mtd/devices/phram.c | 7 ------- drivers/mtd/devices/pmc551.c | 2 -- drivers/mtd/devices/powernv_flash.c | 12 ++---------- drivers/mtd/devices/slram.c | 7 +------ drivers/mtd/devices/spear_smi.c | 3 --- drivers/mtd/devices/sst25l.c | 3 --- drivers/mtd/devices/st_spi_fsm.c | 4 ---- drivers/mtd/lpddr/lpddr2_nvm.c | 10 ++-------- drivers/mtd/lpddr/lpddr_cmds.c | 2 -- drivers/mtd/mtdconcat.c | 1 - drivers/mtd/mtdcore.c | 6 ++---- drivers/mtd/mtdpart.c | 5 ----- drivers/mtd/nand/nand_base.c | 16 ++++------------ drivers/mtd/onenand/onenand_base.c | 17 ----------------- drivers/mtd/spi-nor/spi-nor.c | 3 --- drivers/mtd/ubi/gluebi.c | 3 --- drivers/net/ethernet/sfc/falcon/mtd.c | 11 +---------- drivers/net/ethernet/sfc/mtd.c | 11 +---------- drivers/staging/goldfish/goldfish_nand.c | 3 --- include/linux/mtd/mtd.h | 9 --------- 30 files changed, 23 insertions(+), 204 deletions(-) (limited to 'include') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5e1b68cbcd0a..d4c07b85f18e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1993,20 +1993,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { - unsigned long ofs, len; - int ret; - - ofs = instr->addr; - len = instr->len; - - ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); - if (ret) - return ret; - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; + return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr, + instr->len, NULL); } static void cfi_intelext_sync (struct mtd_info *mtd) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 56aa6b75213d..668e2cbc155b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -2415,20 +2415,8 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) { - unsigned long ofs, len; - int ret; - - ofs = instr->addr; - len = instr->len; - - ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); - if (ret) - return ret; - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; + return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr, + instr->len, NULL); } @@ -2436,7 +2424,6 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - int ret = 0; if (instr->addr != 0) return -EINVAL; @@ -2444,14 +2431,7 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) if (instr->len != mtd->size) return -EINVAL; - ret = do_erase_chip(map, &cfi->chips[0]); - if (ret) - return ret; - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; + return do_erase_chip(map, &cfi->chips[0]); } static int do_atmel_lock(struct map_info *map, struct flchip *chip, diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 7d342965f392..7b7658a05036 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -965,9 +965,6 @@ static int cfi_staa_erase_varsize(struct mtd_info *mtd, } } - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return 0; } diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 1cd0fff0e940..c37fce926864 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -131,8 +131,6 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr) allff = map_word_ff(map); for (i=0; ilen; i += map_bankwidth(map)) map_write(map, allff, instr->addr + i); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 6b84947cfbea..9baa81b8780c 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -68,7 +68,6 @@ static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) { struct bcm47xxsflash *b47s = mtd->priv; - int err; switch (b47s->type) { case BCM47XXSFLASH_TYPE_ST: @@ -89,13 +88,7 @@ static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) break; } - err = bcm47xxsflash_poll(b47s, HZ); - if (err) - erase->state = MTD_ERASE_FAILED; - else - erase->state = MTD_ERASE_DONE; - - return err; + return bcm47xxsflash_poll(b47s, HZ); } static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index bb0734600a07..c9e424993e37 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -88,17 +88,12 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) size_t len = instr->len; int err; - instr->state = MTD_ERASING; mutex_lock(&dev->write_mutex); err = _block2mtd_erase(dev, from, len); mutex_unlock(&dev->write_mutex); - if (err) { + if (err) pr_err("erase failed err = %d\n", err); - instr->state = MTD_ERASE_FAILED; - } else - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return err; } diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index a85af236b44d..c594fe5eac08 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1191,39 +1191,27 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *info) { struct docg3 *docg3 = mtd->priv; uint64_t len; - int block0, block1, page, ret, ofs = 0; + int block0, block1, page, ret = 0, ofs = 0; doc_dbg("doc_erase(from=%lld, len=%lld\n", info->addr, info->len); - info->state = MTD_ERASE_PENDING; calc_block_sector(info->addr + info->len, &block0, &block1, &page, &ofs, docg3->reliable); - ret = -EINVAL; if (info->addr + info->len > mtd->size || page || ofs) - goto reset_err; + return -EINVAL; - ret = 0; calc_block_sector(info->addr, &block0, &block1, &page, &ofs, docg3->reliable); mutex_lock(&docg3->cascade->lock); doc_set_device_id(docg3, docg3->device_id); doc_set_reliable_mode(docg3); for (len = info->len; !ret && len > 0; len -= mtd->erasesize) { - info->state = MTD_ERASING; ret = doc_erase_block(docg3, block0, block1); block0 += 2; block1 += 2; } mutex_unlock(&docg3->cascade->lock); - if (ret) - goto reset_err; - - info->state = MTD_ERASE_DONE; - return 0; - -reset_err: - info->state = MTD_ERASE_FAILED; return ret; } diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 555b94406e0b..f67b653c17d7 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -414,10 +414,7 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) while (len) { if (!erase_block (addr)) - { - instr->state = MTD_ERASE_FAILED; return (-EIO); - } addr += mtd->eraseregions[i].erasesize; len -= mtd->eraseregions[i].erasesize; @@ -425,9 +422,6 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr) if (addr == mtd->eraseregions[i].offset + (mtd->eraseregions[i].erasesize * mtd->eraseregions[i].numblocks)) i++; } - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return (0); } diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 5dc8bd042cc5..aaaeaae01e1d 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -220,10 +220,6 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) } mutex_unlock(&priv->lock); - /* Inform MTD subsystem that erase is complete */ - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return 0; } diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 0bf4aeaf0cb8..46238796145f 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -60,8 +60,7 @@ static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; memset((char *)mtd->priv + instr->addr, 0xff, instr->len); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + return 0; } diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 7287696a21f9..9ee04b5f9311 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -39,13 +39,6 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) memset(start + instr->addr, 0xff, instr->len); - /* - * This'll catch a few races. Free the thing before returning :) - * I don't feel at all ashamed. This kind of thing is possible anyway - * with flash, but unlikely. - */ - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index cadea0620cd0..5d842cbca3de 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -184,12 +184,10 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr) } out: - instr->state = MTD_ERASE_DONE; #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_erase() done\n"); #endif - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c index 26f9feaa5d17..c1312b141ae0 100644 --- a/drivers/mtd/devices/powernv_flash.c +++ b/drivers/mtd/devices/powernv_flash.c @@ -175,19 +175,11 @@ static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase) { int rc; - erase->state = MTD_ERASING; - - /* todo: register our own notifier to do a true async implementation */ rc = powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr, erase->len, NULL, NULL); - - if (rc) { + if (rc) erase->fail_addr = erase->addr; - erase->state = MTD_ERASE_FAILED; - } else { - erase->state = MTD_ERASE_DONE; - } - mtd_erase_callback(erase); + return rc; } diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 0ec85f316d24..10183ee4e12b 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -84,12 +84,7 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) slram_priv_t *priv = mtd->priv; memset(priv->start + instr->addr, 0xff, instr->len); - /* This'll catch a few races. Free the thing before returning :) - * I don't feel at all ashamed. This kind of thing is possible anyway - * with flash, but unlikely. - */ - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + return(0); } diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index ddf478976013..986f81d2f93e 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -518,7 +518,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info) /* preparing the command for flash */ ret = spear_smi_erase_sector(dev, bank, command, 4); if (ret) { - e_info->state = MTD_ERASE_FAILED; mutex_unlock(&flash->lock); return ret; } @@ -527,8 +526,6 @@ static int spear_mtd_erase(struct mtd_info *mtd, struct erase_info *e_info) } mutex_unlock(&flash->lock); - e_info->state = MTD_ERASE_DONE; - mtd_erase_callback(e_info); return 0; } diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 5b84d71efb36..1897f33fe3e7 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -195,7 +195,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) err = sst25l_erase_sector(flash, addr); if (err) { mutex_unlock(&flash->lock); - instr->state = MTD_ERASE_FAILED; dev_err(&flash->spi->dev, "Erase failed\n"); return err; } @@ -205,8 +204,6 @@ static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr) mutex_unlock(&flash->lock); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index a33f5fd6818c..55d4a77f3b7f 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -1825,13 +1825,9 @@ static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) mutex_unlock(&fsm->lock); - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return 0; out1: - instr->state = MTD_ERASE_FAILED; mutex_unlock(&fsm->lock); return ret; diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c index 2342277c9bcb..5d73db2a496d 100644 --- a/drivers/mtd/lpddr/lpddr2_nvm.c +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -380,14 +380,8 @@ out: */ static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr) { - int ret = lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len, - LPDDR2_NVM_ERASE); - if (!ret) { - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - } - - return ret; + return lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len, + LPDDR2_NVM_ERASE); } /* diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index 018c75faadb3..5c5ba3c7c79d 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -693,8 +693,6 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr) ofs += size; len -= size; } - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; } diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 93c47e56d9d8..6b86d1a73cf2 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -446,7 +446,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) erase->addr = 0; offset += subdev->size; } - instr->state = erase->state; kfree(erase); return err; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index f92ad02959eb..f25d65ea7149 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -961,11 +961,9 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if (!instr->len) { - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); + if (!instr->len) return 0; - } + ledtrig_mtd_activity(); return mtd->_erase(mtd, instr); } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 1c07a6f0dfe5..85fea8ea3423 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -212,11 +212,6 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } -void mtd_erase_callback(struct erase_info *instr) -{ -} -EXPORT_SYMBOL_GPL(mtd_erase_callback); - static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct mtd_part *part = mtd_to_part(mtd); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 16c8bc06975d..87b72bf626ae 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4604,22 +4604,20 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (nand_check_wp(mtd)) { pr_debug("%s: device is write protected!\n", __func__); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; goto erase_exit; } /* Loop through the pages */ len = instr->len; - instr->state = MTD_ERASING; - while (len) { /* Check if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(mtd, ((loff_t) page) << chip->page_shift, allowbbt)) { pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; goto erase_exit; } @@ -4637,7 +4635,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (status) { pr_debug("%s: failed erase, page 0x%08x\n", __func__, page); - instr->state = MTD_ERASE_FAILED; + ret = -EIO; instr->fail_addr = ((loff_t)page << chip->page_shift); goto erase_exit; @@ -4654,20 +4652,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, chip->select_chip(mtd, chipnr); } } - instr->state = MTD_ERASE_DONE; + ret = 0; erase_exit: - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - /* Deselect and wake up anyone waiting on the device */ chip->select_chip(mtd, -1); nand_release_device(mtd); - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - /* Return more or less happy */ return ret; } diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 979f4031f23c..8d19b78777b5 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2143,7 +2143,6 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd, if (ret) { printk(KERN_ERR "%s: Failed verify, block %d\n", __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; return -1; } @@ -2172,8 +2171,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd, int ret = 0; int bdry_block = 0; - instr->state = MTD_ERASING; - if (ONENAND_IS_DDP(this)) { loff_t bdry_addr = this->chipsize >> 1; if (addr < bdry_addr && (addr + len) > bdry_addr) @@ -2187,7 +2184,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd, printk(KERN_WARNING "%s: attempt to erase a bad block " "at addr 0x%012llx\n", __func__, (unsigned long long) addr); - instr->state = MTD_ERASE_FAILED; return -EIO; } len -= block_size; @@ -2227,7 +2223,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd, printk(KERN_ERR "%s: Failed multiblock erase, " "block %d\n", __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; return -EIO; } @@ -2247,7 +2242,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd, if (ret) { printk(KERN_ERR "%s: Failed erase, block %d\n", __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; return -EIO; } @@ -2259,7 +2253,6 @@ static int onenand_multiblock_erase(struct mtd_info *mtd, /* verify */ verify_instr.len = eb_count * block_size; if (onenand_multiblock_erase_verify(mtd, &verify_instr)) { - instr->state = verify_instr.state; instr->fail_addr = verify_instr.fail_addr; return -EIO; } @@ -2294,8 +2287,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd, region_end = region->offset + region->erasesize * region->numblocks; } - instr->state = MTD_ERASING; - /* Loop through the blocks */ while (len) { cond_resched(); @@ -2305,7 +2296,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd, printk(KERN_WARNING "%s: attempt to erase a bad block " "at addr 0x%012llx\n", __func__, (unsigned long long) addr); - instr->state = MTD_ERASE_FAILED; return -EIO; } @@ -2318,7 +2308,6 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd, if (ret) { printk(KERN_ERR "%s: Failed erase, block %d\n", __func__, onenand_block(this, addr)); - instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; return -EIO; } @@ -2407,12 +2396,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); - /* Do call back function */ - if (!ret) { - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - } - return ret; } diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d445a4d3b770..5bfa36e95f35 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -560,9 +560,6 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) erase_err: spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); - instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE; - mtd_erase_callback(instr); - return ret; } diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 1cb287ec32ad..6b655a53113b 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -272,12 +272,9 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) if (err) goto out_err; - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); return 0; out_err: - instr->state = MTD_ERASE_FAILED; instr->fail_addr = (long long)lnum * mtd->erasesize; return err; } diff --git a/drivers/net/ethernet/sfc/falcon/mtd.c b/drivers/net/ethernet/sfc/falcon/mtd.c index cde593cb1052..2d67e4621a3d 100644 --- a/drivers/net/ethernet/sfc/falcon/mtd.c +++ b/drivers/net/ethernet/sfc/falcon/mtd.c @@ -24,17 +24,8 @@ static int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) { struct ef4_nic *efx = mtd->priv; - int rc; - rc = efx->type->mtd_erase(mtd, erase->addr, erase->len); - if (rc == 0) { - erase->state = MTD_ERASE_DONE; - } else { - erase->state = MTD_ERASE_FAILED; - erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - } - mtd_erase_callback(erase); - return rc; + return efx->type->mtd_erase(mtd, erase->addr, erase->len); } static void ef4_mtd_sync(struct mtd_info *mtd) diff --git a/drivers/net/ethernet/sfc/mtd.c b/drivers/net/ethernet/sfc/mtd.c index a77a8bd2dd70..4ac30b6e5dab 100644 --- a/drivers/net/ethernet/sfc/mtd.c +++ b/drivers/net/ethernet/sfc/mtd.c @@ -24,17 +24,8 @@ static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) { struct efx_nic *efx = mtd->priv; - int rc; - rc = efx->type->mtd_erase(mtd, erase->addr, erase->len); - if (rc == 0) { - erase->state = MTD_ERASE_DONE; - } else { - erase->state = MTD_ERASE_FAILED; - erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - } - mtd_erase_callback(erase); - return rc; + return efx->type->mtd_erase(mtd, erase->addr, erase->len); } static void efx_mtd_sync(struct mtd_info *mtd) diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c index 52cc1363993e..f5e002ecba75 100644 --- a/drivers/staging/goldfish/goldfish_nand.c +++ b/drivers/staging/goldfish/goldfish_nand.c @@ -119,9 +119,6 @@ static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr) return -EIO; } - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - return 0; invalid_arg: diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4cbb7f555244..a86c4fa93115 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -30,12 +30,6 @@ #include -#define MTD_ERASE_PENDING 0x01 -#define MTD_ERASING 0x02 -#define MTD_ERASE_SUSPEND 0x04 -#define MTD_ERASE_DONE 0x08 -#define MTD_ERASE_FAILED 0x10 - #define MTD_FAIL_ADDR_UNKNOWN -1LL struct mtd_info; @@ -49,7 +43,6 @@ struct erase_info { uint64_t addr; uint64_t len; uint64_t fail_addr; - u_char state; }; struct mtd_erase_region_info { @@ -589,8 +582,6 @@ extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size); -void mtd_erase_callback(struct erase_info *instr); - static inline int mtd_is_bitflip(int err) { return err == -EUCLEAN; } -- cgit v1.2.3 From 5b644aa012f67fd211138a067b9f351f30bdcc60 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 14 Mar 2018 13:10:42 +0100 Subject: mtd: partitions: add of_match_table parser matching for the "ofpart" type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to properly support compatibility strings as described in the bindings/mtd/partition.txt "ofpart" type should be treated as an indication for looking into OF. MTD should check "compatible" property and search for a matching parser rather than blindly trying the one supporting "fixed-partitions". It also means that existing "fixed-partitions" parser should get renamed to use a more meaningful name. This commit achievies that aim by introducing a new mtd_part_of_parse(). It works by looking for a matching parser for every string in the "compatibility" property (starting with the most specific one). Please note that driver-specified parsers still take a precedence. It's assumed that driver providing a parser type has a good reason for that (e.g. having platform data with device-specific info). Also doing otherwise could break existing setups. The same applies to using default parsers (including "cmdlinepart") as some overwrite DT data with cmdline argument. Partition parsers can now provide an of_match_table to enable flash<-->parser matching via device tree as documented in the mtd/partition.txt. This support is currently limited to built-in parsers as it uses request_module() and friends. This should be sufficient for most cases though as compiling parsers as modules isn't a common choice. Signed-off-by: Brian Norris Signed-off-by: Rafał Miłecki Tested-by: Peter Rosin Reviewed-by: Richard Weinberger Signed-off-by: Boris Brezillon --- drivers/mtd/mtdpart.c | 116 +++++++++++++++++++++++++++++++++++++---- include/linux/mtd/partitions.h | 1 + 2 files changed, 108 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 85fea8ea3423..2b7bb834ff40 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "mtdcore.h" @@ -845,6 +846,92 @@ static int mtd_part_do_parse(struct mtd_part_parser *parser, return ret; } +/** + * mtd_part_get_compatible_parser - find MTD parser by a compatible string + * + * @compat: compatible string describing partitions in a device tree + * + * MTD parsers can specify supported partitions by providing a table of + * compatibility strings. This function finds a parser that advertises support + * for a passed value of "compatible". + */ +static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat) +{ + struct mtd_part_parser *p, *ret = NULL; + + spin_lock(&part_parser_lock); + + list_for_each_entry(p, &part_parsers, list) { + const struct of_device_id *matches; + + matches = p->of_match_table; + if (!matches) + continue; + + for (; matches->compatible[0]; matches++) { + if (!strcmp(matches->compatible, compat) && + try_module_get(p->owner)) { + ret = p; + break; + } + } + + if (ret) + break; + } + + spin_unlock(&part_parser_lock); + + return ret; +} + +static int mtd_part_of_parse(struct mtd_info *master, + struct mtd_partitions *pparts) +{ + struct mtd_part_parser *parser; + struct device_node *np; + struct property *prop; + const char *compat; + const char *fixed = "ofpart"; + int ret, err = 0; + + np = of_get_child_by_name(mtd_get_of_node(master), "partitions"); + of_property_for_each_string(np, "compatible", prop, compat) { + parser = mtd_part_get_compatible_parser(compat); + if (!parser) + continue; + ret = mtd_part_do_parse(parser, master, pparts, NULL); + if (ret > 0) { + of_node_put(np); + return ret; + } + mtd_part_parser_put(parser); + if (ret < 0 && !err) + err = ret; + } + of_node_put(np); + + /* + * For backward compatibility we have to try the "ofpart" + * parser. It supports old DT format with partitions specified as a + * direct subnodes of a flash device DT node without any compatibility + * specified we could match. + */ + parser = mtd_part_parser_get(fixed); + if (!parser && !request_module("%s", fixed)) + parser = mtd_part_parser_get(fixed); + if (parser) { + ret = mtd_part_do_parse(parser, master, pparts, NULL); + if (ret > 0) + return ret; + mtd_part_parser_put(parser); + if (ret < 0 && !err) + err = ret; + } + + return err; +} + /** * parse_mtd_partitions - parse MTD partitions * @master: the master partition (describes whole MTD device) @@ -877,19 +964,30 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, types = default_mtd_part_types; for ( ; *types; types++) { - pr_debug("%s: parsing partitions %s\n", master->name, *types); - parser = mtd_part_parser_get(*types); - if (!parser && !request_module("%s", *types)) + /* + * ofpart is a special type that means OF partitioning info + * should be used. It requires a bit different logic so it is + * handled in a separated function. + */ + if (!strcmp(*types, "ofpart")) { + ret = mtd_part_of_parse(master, pparts); + } else { + pr_debug("%s: parsing partitions %s\n", master->name, + *types); parser = mtd_part_parser_get(*types); - pr_debug("%s: got parser %s\n", master->name, - parser ? parser->name : NULL); - if (!parser) - continue; - ret = mtd_part_do_parse(parser, master, pparts, data); + if (!parser && !request_module("%s", *types)) + parser = mtd_part_parser_get(*types); + pr_debug("%s: got parser %s\n", master->name, + parser ? parser->name : NULL); + if (!parser) + continue; + ret = mtd_part_do_parse(parser, master, pparts, data); + if (ret <= 0) + mtd_part_parser_put(parser); + } /* Found partitions! */ if (ret > 0) return 0; - mtd_part_parser_put(parser); /* * Stash the first error we see; only report it if no parser * succeeds diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index c4beb70dacbd..11cb0c50cd84 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -77,6 +77,7 @@ struct mtd_part_parser { struct list_head list; struct module *owner; const char *name; + const struct of_device_id *of_match_table; int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, struct mtd_part_parser_data *); void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); -- cgit v1.2.3