From b0e137ad24b6cc36a4ab09558a401e124163eefb Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Mon, 15 Oct 2018 21:41:28 +0200 Subject: mtd: rawnand: Provide helper for polling GPIO R/B pin Each controller driver having access to NAND R/B pin over GPIO would have to reimplement the polling loop otherwise. Suggested-by: Boris Brezillon Signed-off-by: Janusz Krzysztofik Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- include/linux/mtd/rawnand.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index e10b126e148f..4e91a70ede10 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1346,4 +1346,8 @@ void nand_release(struct nand_chip *chip); */ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms); +struct gpio_desc; +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, + unsigned long timeout_ms); + #endif /* __LINUX_MTD_RAWNAND_H */ -- cgit v1.2.3 From cfd74017191036871af68368559330507209777c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 8 Nov 2018 06:39:20 +0000 Subject: mtd: rawnand: sh_flctl: convert to SPDX identifiers This patch updates license to use SPDX-License-Identifier instead of verbose license text. As original license mentioned, it is GPL-2.0 in SPDX. Then, MODULE_LICENSE() should be "GPL v2" instead of "GPL". See ${LINUX}/include/linux/module.h "GPL" [GNU Public License v2 or later] "GPL v2" [GNU Public License v2] Signed-off-by: Kuninori Morimoto Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sh_flctl.c | 17 ++--------------- include/linux/mtd/sh_flctl.h | 16 ++-------------- 2 files changed, 4 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 4d20d033de7b..30edcc77b111 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SuperH FLCTL nand controller * @@ -5,20 +6,6 @@ * Copyright (c) 2008 Atom Create Engineering Co., Ltd. * * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include @@ -1236,7 +1223,7 @@ static struct platform_driver flctl_driver = { module_platform_driver_probe(flctl_driver, flctl_probe); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_DESCRIPTION("SuperH FLCTL driver"); MODULE_ALIAS("platform:sh_flctl"); diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h index c759d403cbc0..78fc2d4218c8 100644 --- a/include/linux/mtd/sh_flctl.h +++ b/include/linux/mtd/sh_flctl.h @@ -1,20 +1,8 @@ -/* +/* SPDX-License-Identifier: GPL-2.0 + * * SuperH FLCTL nand controller * * Copyright © 2008 Renesas Solutions Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SH_FLCTL_H__ -- cgit v1.2.3 From 4348433d8c0234f44adb6e12112e69343f50f0c5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 18 Nov 2018 21:18:30 +0100 Subject: mtd: fix mtd_oobavail() incoherent returned value mtd_oobavail() returns either mtd->oovabail or mtd->oobsize. Both values are unsigned 32-bit entities, so there is no reason to pretend returning a signed one. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- include/linux/mtd/mtd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index cd0be91bdefa..035d641e8847 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -386,7 +386,7 @@ static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd) return dev_of_node(&mtd->dev); } -static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) +static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) { return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; } -- cgit v1.2.3 From 1186af457cc186c5ed01708da71b1ffbdf0a2638 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 20 Nov 2018 09:55:45 +0100 Subject: mtd: keep original flags for every struct mtd_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When allocating a new partition mtd subsystem runs internal tests in the allocate_partition(). They may result in modifying specified flags (e.g. dropping some /features/ like write access). Those constraints don't have to be necessary true for subpartitions. It may happen parent partition isn't block aligned (effectively disabling write access) while subpartition may fit blocks nicely. In such case all checks should be run again (starting with original flags value). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 2 ++ drivers/mtd/mtdpart.c | 3 ++- include/linux/mtd/mtd.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index afb4b17fb670..b6b93291aba9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -665,6 +665,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) } else { pr_debug("mtd device won't show a device symlink in sysfs\n"); } + + mtd->orig_flags = mtd->flags; } /** diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 99c460facd5e..2b6e53af47da 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -346,7 +346,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, /* set up the MTD object for this partition */ slave->mtd.type = parent->type; - slave->mtd.flags = parent->flags & ~part->mask_flags; + slave->mtd.flags = parent->orig_flags & ~part->mask_flags; + slave->mtd.orig_flags = slave->mtd.flags; slave->mtd.size = part->size; slave->mtd.writesize = parent->writesize; slave->mtd.writebufsize = parent->writebufsize; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 035d641e8847..ba8fa9072aca 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -207,6 +207,7 @@ struct mtd_debug_info { struct mtd_info { u_char type; uint32_t flags; + uint32_t orig_flags; /* Flags as before running mtd checks */ uint64_t size; // Total size of the MTD /* "Major" erase size for the device. Naïve users may take this -- cgit v1.2.3 From 10949af1681d5bb5cdbcc012815c6e40eec17d02 Mon Sep 17 00:00:00 2001 From: Schrempf Frieder Date: Thu, 8 Nov 2018 08:32:11 +0000 Subject: mtd: spinand: Add initial support for Toshiba TC58CVG2S0H MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add minimal support for the Toshiba TC58CVG2S0H SPI NAND chip. Signed-off-by: Frieder Schrempf Acked-by: Clément Péron Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/toshiba.c | 137 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/toshiba.c (limited to 'include') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index b74e074b363a..be5f73512ece 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o macronix.o micron.o winbond.o +spinand-objs := core.o macronix.o micron.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 30f83649c481..87bdf2a7b724 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -766,6 +766,7 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { ¯onix_spinand_manufacturer, µn_spinand_manufacturer, + &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c new file mode 100644 index 000000000000..081265557e70 --- /dev/null +++ b/drivers/mtd/nand/spi/toshiba.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 exceet electronics GmbH + * Copyright (c) 2018 Kontron Electronics GmbH + * + * Author: Frieder Schrempf + */ + +#include +#include +#include + +#define SPINAND_MFR_TOSHIBA 0x98 +#define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + region->offset = 128 + 16 * section; + region->length = 16; + + return 0; +} + +static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 0) + return -ERANGE; + + /* 2 bytes reserved for BBM */ + region->offset = 2; + region->length = 126; + + return 0; +} + +static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = { + .ecc = tc58cvg2s0h_ooblayout_ecc, + .free = tc58cvg2s0h_ooblayout_free, +}; + +static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 mbf = 0; + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + case TOSH_STATUS_ECC_HAS_BITFLIPS_T: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (spi_mem_exec_op(spinand->spimem, &op)) + return nand->eccreq.strength; + + mbf >>= 4; + + if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) + return nand->eccreq.strength; + + return mbf; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info toshiba_spinand_table[] = { + SPINAND_INFO("TC58CVG2S0H", 0xCD, + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout, + tc58cvg2s0h_ecc_get_status)), +}; + +static int toshiba_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Toshiba SPI NAND read ID needs a dummy byte, + * so the first byte in id is garbage. + */ + if (id[1] != SPINAND_MFR_TOSHIBA) + return 0; + + ret = spinand_match_and_init(spinand, toshiba_spinand_table, + ARRAY_SIZE(toshiba_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { + .detect = toshiba_spinand_detect, +}; + +const struct spinand_manufacturer toshiba_spinand_manufacturer = { + .id = SPINAND_MFR_TOSHIBA, + .name = "Toshiba", + .ops = &toshiba_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 088ff96c3eb6..816c4b00abca 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -196,6 +196,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer toshiba_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; /** -- cgit v1.2.3 From 1f2d29e634b3e7abc7b62adf6bb4a676615c02ef Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:06 +0100 Subject: mtd: rawnand: Move nand_exec_op() to internal.h nand_exec_op() is only used by core code (nand_xxx.c files). Let's move this inline function in drivers/mtd/nand/raw/internals.h. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/internals.h | 9 +++++++++ include/linux/mtd/rawnand.h | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index 04c2cf74eff3..6e2f61fbc5f0 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -95,6 +95,15 @@ void nand_decode_ext_id(struct nand_chip *chip); void panic_nand_wait(struct nand_chip *chip, unsigned long timeo); void sanitize_string(uint8_t *s, size_t len); +static inline int nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op) +{ + if (!chip->exec_op) + return -ENOTSUPP; + + return chip->exec_op(chip, op, false); +} + /* BBT functions */ int nand_markbad_bbt(struct nand_chip *chip, loff_t offs); int nand_isreserved_bbt(struct nand_chip *chip, loff_t offs); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 4e91a70ede10..85dd89abcd22 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1098,15 +1098,6 @@ struct nand_chip { } manufacturer; }; -static inline int nand_exec_op(struct nand_chip *chip, - const struct nand_operation *op) -{ - if (!chip->exec_op) - return -ENOTSUPP; - - return chip->exec_op(chip, op, false); -} - extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; -- cgit v1.2.3 From 336058c8f4c2c7991427304c8bde05acef156054 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:07 +0100 Subject: mtd: rawnand: Remove unused NAND_CONTROLLER_ALLOC flag Looks like NAND_CONTROLLER_ALLOC has been introduced a long time ago back when the dummy nand_hw_ctrl object was dynamically allocated instead of being embedded in nand_chip. We can safely get rid of this unused flag. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- include/linux/mtd/rawnand.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 85dd89abcd22..2a3dd3e633f1 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -203,10 +203,6 @@ enum nand_ecc_algo { */ #define NAND_IS_BOOT_MEDIUM 0x00400000 -/* Options set by nand scan */ -/* Nand scan has allocated controller struct */ -#define NAND_CONTROLLER_ALLOC 0x80000000 - /* Cell info constants */ #define NAND_CI_CHIPNR_MSK 0x03 #define NAND_CI_CELLTYPE_MSK 0x0C -- cgit v1.2.3 From 1d0178593d148e88d2ac1e3f09c7f7eb1c20796b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:14 +0100 Subject: mtd: rawnand: Add nand_[de]select_target() helpers Add a wrapper to prevent drivers and core code from directly calling the ->select_chip hook which we are about to deprecate. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 23 ++++-- drivers/mtd/nand/raw/jz4740_nand.c | 4 +- drivers/mtd/nand/raw/nand_base.c | 120 +++++++++++++++++++---------- drivers/mtd/nand/raw/r852.c | 4 +- include/linux/mtd/rawnand.h | 5 +- 5 files changed, 104 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 94c2b7525c85..302ddd3d4a5f 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1549,7 +1549,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) int column, page, chipnr; chipnr = (int)(ofs >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; @@ -1562,7 +1562,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) ret = nand_prog_page_op(chip, page, column, block_mark, 1); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -1610,7 +1610,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; saved_chip_number = this->current_chip; - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* * Loop through the first search area, looking for the NCB fingerprint. @@ -1638,7 +1638,10 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) } - chip->select_chip(chip, saved_chip_number); + if (saved_chip_number >= 0) + nand_select_target(chip, saved_chip_number); + else + nand_deselect_target(chip); if (found_an_ncb_fingerprint) dev_dbg(dev, "\tFound a fingerprint\n"); @@ -1681,7 +1684,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Select chip 0. */ saved_chip_number = this->current_chip; - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* Loop over blocks in the first search area, erasing them. */ dev_dbg(dev, "Erasing the search area...\n"); @@ -1713,7 +1716,11 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) } /* Deselect chip 0. */ - chip->select_chip(chip, saved_chip_number); + if (saved_chip_number >= 0) + nand_select_target(chip, saved_chip_number); + else + nand_deselect_target(chip); + return 0; } @@ -1762,10 +1769,10 @@ static int mx23_boot_init(struct gpmi_nand_data *this) byte = block << chip->phys_erase_shift; /* Send the command to read the conventional block mark. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_read_page_op(chip, page, mtd->writesize, NULL, 0); block_mark = chip->legacy.read_byte(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); /* * Check if the block is marked bad. If so, we need to mark it diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index fb59cfca11a7..d271004f16b0 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -335,14 +335,14 @@ static int jz_nand_detect_bank(struct platform_device *pdev, goto notfound_id; /* Retrieve the IDs from the first chip. */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); nand_reset_op(chip); nand_readid_op(chip, 0, id, sizeof(id)); *nand_maf_id = id[0]; *nand_dev_id = id[1]; } else { /* Detect additional chip. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_reset_op(chip); nand_readid_op(chip, 0, id, sizeof(id)); if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6d9de6949366..f85e6f3b1b2f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -228,6 +228,41 @@ static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len) return ret; } +/** + * nand_select_target() - Select a NAND target (A.K.A. die) + * @chip: NAND chip object + * @cs: the CS line to select. Note that this CS id is always from the chip + * PoV, not the controller one + * + * Select a NAND target so that further operations executed on @chip go to the + * selected NAND target. + */ +void nand_select_target(struct nand_chip *chip, unsigned int cs) +{ + /* + * cs should always lie between 0 and chip->numchips, when that's not + * the case it's a bug and the caller should be fixed. + */ + if (WARN_ON(cs > chip->numchips)) + return; + + chip->select_chip(chip, cs); +} +EXPORT_SYMBOL_GPL(nand_select_target); + +/** + * nand_deselect_target() - Deselect the currently selected target + * @chip: NAND chip object + * + * Deselect the currently selected NAND target. The result of operations + * executed on @chip after the target has been deselected is undefined. + */ +void nand_deselect_target(struct nand_chip *chip) +{ + chip->select_chip(chip, -1); +} +EXPORT_SYMBOL_GPL(nand_deselect_target); + /** * nand_release_device - [GENERIC] release chip * @chip: NAND chip object @@ -440,14 +475,14 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to, */ nand_reset(chip, chipnr); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Shift to get page */ page = (int)(to >> chip->page_shift); /* Check, if it is write protected */ if (nand_check_wp(chip)) { - chip->select_chip(chip, -1); + nand_deselect_target(chip); return -EROFS; } @@ -462,7 +497,7 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to, else status = chip->ecc.write_oob(chip, page & chip->pagemask); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (status) return status; @@ -816,10 +851,10 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) /* Change the mode on the chip side (if supported by the NAND chip) */ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) { - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) return ret; } @@ -834,10 +869,10 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) return 0; memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE, tmode_param); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) goto err_reset_chip; @@ -855,9 +890,9 @@ err_reset_chip: * timing mode. */ nand_reset_data_interface(chip, chipnr); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); nand_reset_op(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -2345,11 +2380,12 @@ int nand_reset(struct nand_chip *chip, int chipnr) /* * The CS line has to be released before we can apply the new NAND - * interface settings, hence this weird ->select_chip() dance. + * interface settings, hence this weird nand_select_target() + * nand_deselect_target() dance. */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_reset_op(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) return ret; @@ -3133,7 +3169,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from, bool ecc_fail = false; chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; @@ -3264,11 +3300,11 @@ read_retry: /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } - chip->select_chip(chip, -1); + nand_deselect_target(chip); ops->retlen = ops->len - (size_t) readlen; if (oob) @@ -3465,7 +3501,7 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from, len = mtd_oobavail(mtd, ops); chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Shift to get page */ realpage = (int)(from >> chip->page_shift); @@ -3498,11 +3534,11 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from, /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } - chip->select_chip(chip, -1); + nand_deselect_target(chip); ops->oobretlen = ops->ooblen - readlen; @@ -3946,7 +3982,7 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to, column = to & (mtd->writesize - 1); chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Check, if it is write protected */ if (nand_check_wp(chip)) { @@ -4022,8 +4058,8 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to, /* Check, if we cross a chip boundary */ if (!page) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } @@ -4032,7 +4068,7 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to, ops->oobretlen = ops->ooblen; err_out: - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } @@ -4058,7 +4094,7 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Grab the device */ panic_nand_get_device(chip, FL_WRITING); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Wait for the device to get ready */ panic_nand_wait(chip, 400); @@ -4171,7 +4207,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); /* Select the NAND device */ - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); /* Check, if it is write protected */ if (nand_check_wp(chip)) { @@ -4225,8 +4261,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, /* Check, if we cross a chip boundary */ if (len && !(page & chip->pagemask)) { chipnr++; - chip->select_chip(chip, -1); - chip->select_chip(chip, chipnr); + nand_deselect_target(chip); + nand_select_target(chip, chipnr); } } @@ -4234,7 +4270,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, erase_exit: /* Deselect and wake up anyone waiting on the device */ - chip->select_chip(chip, -1); + nand_deselect_target(chip); nand_release_device(chip); /* Return more or less happy */ @@ -4272,11 +4308,11 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) /* Select the NAND device */ nand_get_device(chip, FL_READING); - chip->select_chip(chip, chipnr); + nand_select_target(chip, chipnr); ret = nand_block_checkbad(chip, offs, 0); - chip->select_chip(chip, -1); + nand_deselect_target(chip); nand_release_device(chip); return ret; @@ -4645,7 +4681,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) return ret; /* Select the device */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); /* Send the command for reading device ID */ ret = nand_readid_op(chip, 0, id_data, 2); @@ -4989,6 +5025,12 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, if (ret) return ret; + /* + * Start with chips->numchips = maxchips to let nand_select_target() do + * its job. chip->numchips will be adjusted after. + */ + chip->numchips = maxchips; + /* Set the default functions */ nand_set_defaults(chip); @@ -4997,14 +5039,14 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, if (ret) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) pr_warn("No NAND device found\n"); - chip->select_chip(chip, -1); + nand_deselect_target(chip); return ret; } nand_maf_id = chip->id.data[0]; nand_dev_id = chip->id.data[1]; - chip->select_chip(chip, -1); + nand_deselect_target(chip); /* Check for a chip array */ for (i = 1; i < maxchips; i++) { @@ -5013,15 +5055,15 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, /* See comment in nand_get_flash_type for reset */ nand_reset(chip, i); - chip->select_chip(chip, i); + nand_select_target(chip, i); /* Send the command for reading device ID */ nand_readid_op(chip, 0, id, sizeof(id)); /* Read manufacturer and device IDs */ if (nand_maf_id != id[0] || nand_dev_id != id[1]) { - chip->select_chip(chip, -1); + nand_deselect_target(chip); break; } - chip->select_chip(chip, -1); + nand_deselect_target(chip); } if (i > 1) pr_info("%d chips detected\n", i); @@ -5447,9 +5489,9 @@ static int nand_scan_tail(struct nand_chip *chip) * to explictly select the relevant die when interacting with the NAND * chip. */ - chip->select_chip(chip, 0); + nand_select_target(chip, 0); ret = nand_manufacturer_init(chip); - chip->select_chip(chip, -1); + nand_deselect_target(chip); if (ret) goto err_free_buf; diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index 35f0b343cf90..c01422d953dd 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -1045,9 +1045,9 @@ static int r852_resume(struct device *device) /* Otherwise, initialize the card */ if (dev->card_registered) { r852_engine_enable(dev); - dev->chip->select_chip(dev->chip, 0); + nand_select_target(dev->chip, 0); nand_reset_op(dev->chip); - dev->chip->select_chip(dev->chip, -1); + nand_deselect_target(dev->chip); } /* Program card detection IRQ */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 2a3dd3e633f1..def6dff11e8b 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1332,9 +1332,12 @@ void nand_release(struct nand_chip *chip); * instruction and have no physical pin to check it. */ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms); - struct gpio_desc; int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, unsigned long timeout_ms); +/* Select/deselect a NAND target. */ +void nand_select_target(struct nand_chip *chip, unsigned int cs); +void nand_deselect_target(struct nand_chip *chip); + #endif /* __LINUX_MTD_RAWNAND_H */ -- cgit v1.2.3 From ae2294b10b0f066ef500954b36c94ee11c4ef20f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:15 +0100 Subject: mtd: rawnand: Pass the CS line to be selected in struct nand_operation In order to deprecate the ->select_chip hook we need to pass the CS line a NAND operations are targeting. This is done through the addition of a cs field to the nand_operation struct. We also need to keep track of the currently selected target to properly initialize op->cs, hence the ->cur_cs field addition to the nand_chip struct. Note that op->cs is not assigned in nand_exec_op() because we might rework the way we execute NAND operations in the future (adopt a queuing mechanism instead of the serialization we have right now). Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/internals.h | 3 +++ drivers/mtd/nand/raw/nand_base.c | 39 ++++++++++++++++++++++----------------- drivers/mtd/nand/raw/nand_hynix.c | 4 ++-- include/linux/mtd/rawnand.h | 11 ++++++++++- 4 files changed, 37 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index 6e2f61fbc5f0..b62728d5884b 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -101,6 +101,9 @@ static inline int nand_exec_op(struct nand_chip *chip, if (!chip->exec_op) return -ENOTSUPP; + if (WARN_ON(op->cs >= chip->numchips)) + return -EINVAL; + return chip->exec_op(chip, op, false); } diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f85e6f3b1b2f..7aa661f76891 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -246,6 +246,7 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs) if (WARN_ON(cs > chip->numchips)) return; + chip->cur_cs = cs; chip->select_chip(chip, cs); } EXPORT_SYMBOL_GPL(nand_select_target); @@ -260,6 +261,7 @@ EXPORT_SYMBOL_GPL(nand_select_target); void nand_deselect_target(struct nand_chip *chip) { chip->select_chip(chip, -1); + chip->cur_cs = -1; } EXPORT_SYMBOL_GPL(nand_deselect_target); @@ -1022,7 +1024,7 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; /* Drop the DATA_IN instruction if len is set to 0. */ @@ -1065,7 +1067,7 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; /* Drop the DATA_IN instruction if len is set to 0. */ @@ -1160,7 +1162,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, PSEC_TO_NSEC(sdr->tRR_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); /* Drop the DATA_IN instruction if len is set to 0. */ if (!len) @@ -1216,7 +1218,7 @@ int nand_change_read_column_op(struct nand_chip *chip, PSEC_TO_NSEC(sdr->tCCS_min)), NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; ret = nand_fill_column_cycles(chip, addrs, offset_in_page); @@ -1298,7 +1300,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); int ret; u8 status; @@ -1412,7 +1414,7 @@ int nand_prog_page_end_op(struct nand_chip *chip) PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); ret = nand_exec_op(chip, &op); if (ret) @@ -1520,7 +1522,7 @@ int nand_change_write_column_op(struct nand_chip *chip, NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), NAND_OP_DATA_OUT(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int ret; ret = nand_fill_column_cycles(chip, addrs, offset_in_page); @@ -1574,7 +1576,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); /* Drop the DATA_IN instruction if len is set to 0. */ if (!len) @@ -1613,7 +1615,7 @@ int nand_status_op(struct nand_chip *chip, u8 *status) PSEC_TO_NSEC(sdr->tADL_min)), NAND_OP_8BIT_DATA_IN(1, status, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); if (!status) op.ninstrs--; @@ -1646,7 +1648,7 @@ int nand_exit_status_op(struct nand_chip *chip) struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READ0, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1685,7 +1687,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) PSEC_TO_MSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); if (chip->options & NAND_ROW_ADDR_3) instrs[1].ctx.addr.naddrs++; @@ -1743,7 +1745,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1791,7 +1793,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, data, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1811,7 +1813,7 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms), PSEC_TO_NSEC(delay_ns)), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1844,7 +1846,7 @@ int nand_reset_op(struct nand_chip *chip) NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -1878,7 +1880,7 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, struct nand_op_instr instrs[] = { NAND_OP_DATA_IN(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); instrs[0].ctx.data.force_8bit = force_8bit; @@ -1922,7 +1924,7 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf, struct nand_op_instr instrs[] = { NAND_OP_DATA_OUT(len, buf, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); instrs[0].ctx.data.force_8bit = force_8bit; @@ -5006,6 +5008,9 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, unsigned int i; int ret; + /* Assume all dies are deselected when we enter nand_scan_ident(). */ + chip->cur_cs = -1; + /* Enforce the right timings for reset/detection */ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index ac1b5c103968..1e4499d01e14 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -84,7 +84,7 @@ static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) struct nand_op_instr instrs[] = { NAND_OP_CMD(cmd, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } @@ -103,7 +103,7 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) NAND_OP_ADDR(1, &addr, 0), NAND_OP_8BIT_DATA_OUT(1, &val, 0), }; - struct nand_operation op = NAND_OPERATION(instrs); + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); return nand_exec_op(chip, &op); } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index def6dff11e8b..aa1512df38a9 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -875,18 +875,21 @@ struct nand_op_parser { /** * struct nand_operation - NAND operation descriptor + * @cs: the CS line to select for this NAND operation * @instrs: array of instructions to execute * @ninstrs: length of the @instrs array * * The actual operation structure that will be passed to chip->exec_op(). */ struct nand_operation { + unsigned int cs; const struct nand_op_instr *instrs; unsigned int ninstrs; }; -#define NAND_OPERATION(_instrs) \ +#define NAND_OPERATION(_cs, _instrs) \ { \ + .cs = _cs, \ .instrs = _instrs, \ .ninstrs = ARRAY_SIZE(_instrs), \ } @@ -1008,6 +1011,10 @@ struct nand_legacy { * this nand device will encounter their life times. * @blocks_per_die: [INTERN] The number of PEBs in a die * @data_interface: [INTERN] NAND interface timing information + * @cur_cs: currently selected target. -1 means no target selected, + * otherwise we should always have cur_cs >= 0 && + * cur_cs < numchips. NAND Controller drivers should not + * modify this value, but they're allowed to read it. * @read_retries: [INTERN] the number of read retry modes supported * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If * chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this @@ -1069,6 +1076,8 @@ struct nand_chip { struct nand_data_interface data_interface; + int cur_cs; + int read_retries; flstate_t state; -- cgit v1.2.3 From 7d6c37e90cf9013bd18240cd861b9ae7b006f91f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:22 +0100 Subject: mtd: rawnand: Deprecate the ->select_chip() hook Now that the CS line to be selected is passed to ->exec_op() and stored in chip->cur_cs and after patching all drivers implementing ->exec_op() to stop implementing this method, we can deprecate it by moving it to the nand_legacy structure. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 4 ++-- drivers/mtd/nand/raw/au1550nd.c | 2 +- drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c | 2 +- drivers/mtd/nand/raw/cafe_nand.c | 2 +- drivers/mtd/nand/raw/davinci_nand.c | 2 +- drivers/mtd/nand/raw/denali.c | 2 +- drivers/mtd/nand/raw/diskonchip.c | 4 ++-- drivers/mtd/nand/raw/fsl_elbc_nand.c | 2 +- drivers/mtd/nand/raw/fsl_ifc_nand.c | 2 +- drivers/mtd/nand/raw/fsl_upm.c | 2 +- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- drivers/mtd/nand/raw/hisi504_nand.c | 2 +- drivers/mtd/nand/raw/jz4740_nand.c | 2 +- drivers/mtd/nand/raw/jz4780_nand.c | 2 +- drivers/mtd/nand/raw/mpc5121_nfc.c | 4 ++-- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/mxc_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 8 ++++---- drivers/mtd/nand/raw/nand_legacy.c | 9 +++++---- drivers/mtd/nand/raw/ndfc.c | 2 +- drivers/mtd/nand/raw/plat_nand.c | 2 +- drivers/mtd/nand/raw/qcom_nandc.c | 2 +- drivers/mtd/nand/raw/s3c2410.c | 2 +- drivers/mtd/nand/raw/sh_flctl.c | 2 +- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- drivers/mtd/nand/raw/tango_nand.c | 2 +- drivers/mtd/nand/raw/xway_nand.c | 2 +- include/linux/mtd/rawnand.h | 4 ++-- 28 files changed, 39 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index fb33f6be7c4f..d5c58eb040d8 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1477,7 +1477,7 @@ static void atmel_nand_init(struct atmel_nand_controller *nc, chip->legacy.write_byte = atmel_nand_write_byte; chip->legacy.read_buf = atmel_nand_read_buf; chip->legacy.write_buf = atmel_nand_write_buf; - chip->select_chip = atmel_nand_select_chip; + chip->legacy.select_chip = atmel_nand_select_chip; if (nc->mck && nc->caps->ops->setup_data_interface) chip->setup_data_interface = atmel_nand_setup_data_interface; @@ -1525,7 +1525,7 @@ static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, /* Overload some methods for the HSMC controller. */ chip->legacy.cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; - chip->select_chip = atmel_hsmc_nand_select_chip; + chip->legacy.select_chip = atmel_hsmc_nand_select_chip; } static int atmel_nand_controller_remove_nand(struct atmel_nand *nand) diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 9731c1c487f6..a963002663ed 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -430,7 +430,7 @@ static int au1550nd_probe(struct platform_device *pdev) ctx->cs = cs; this->legacy.dev_ready = au1550_device_ready; - this->select_chip = au1550_select_chip; + this->legacy.select_chip = au1550_select_chip; this->legacy.cmdfunc = au1550_command; /* 30 us command delay time */ diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c index 9095a79ebc7d..a37cbfe56567 100644 --- a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -383,7 +383,7 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) u8 tbits, col_bits, col_size, row_bits, row_bsize; u32 val; - b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; + nand_chip->legacy.select_chip = bcm47xxnflash_ops_bcm4706_select_chip; nand_chip->legacy.cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl; nand_chip->legacy.dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready; b47n->nand_chip.legacy.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc; diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index c1a745940d12..a85f5fa5c66d 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -708,7 +708,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->nand.legacy.read_byte = cafe_read_byte; cafe->nand.legacy.read_buf = cafe_read_buf; cafe->nand.legacy.write_buf = cafe_write_buf; - cafe->nand.select_chip = cafe_select_chip; + cafe->nand.legacy.select_chip = cafe_select_chip; cafe->nand.legacy.set_features = nand_get_set_features_notsupp; cafe->nand.legacy.get_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 80f228d23cd2..f430aeb917e8 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -762,7 +762,7 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.legacy.IO_ADDR_R = vaddr; info->chip.legacy.IO_ADDR_W = vaddr; info->chip.legacy.chip_delay = 0; - info->chip.select_chip = nand_davinci_select_chip; + info->chip.legacy.select_chip = nand_davinci_select_chip; /* options such as NAND_BBT_USE_FLASH */ info->chip.bbt_options = pdata->bbt_options; diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 830ea247277b..64895ca68c8d 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1355,7 +1355,7 @@ int denali_init(struct denali_nand_info *denali) if (!mtd->name) mtd->name = "denali-nand"; - chip->select_chip = denali_select_chip; + chip->legacy.select_chip = denali_select_chip; chip->legacy.read_byte = denali_read_byte; chip->legacy.write_byte = denali_write_byte; chip->legacy.cmd_ctrl = denali_cmd_ctrl; diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 3a4c373affab..53f57e0f007e 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1390,7 +1390,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) this->legacy.read_buf = doc2001plus_readbuf; doc->late_init = inftl_scan_bbt; this->legacy.cmd_ctrl = NULL; - this->select_chip = doc2001plus_select_chip; + this->legacy.select_chip = doc2001plus_select_chip; this->legacy.cmdfunc = doc2001plus_command; this->ecc.hwctl = doc2001plus_enable_hwecc; @@ -1568,7 +1568,7 @@ static int __init doc_probe(unsigned long physadr) mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); nand_set_controller_data(nand, doc); - nand->select_chip = doc200x_select_chip; + nand->legacy.select_chip = doc200x_select_chip; nand->legacy.cmd_ctrl = doc200x_hwcontrol; nand->legacy.dev_ready = doc200x_dev_ready; nand->legacy.waitfunc = doc200x_wait; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index d6ed697fcfe6..70f0d2b450ea 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -779,7 +779,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->legacy.read_byte = fsl_elbc_read_byte; chip->legacy.write_buf = fsl_elbc_write_buf; chip->legacy.read_buf = fsl_elbc_read_buf; - chip->select_chip = fsl_elbc_select_chip; + chip->legacy.select_chip = fsl_elbc_select_chip; chip->legacy.cmdfunc = fsl_elbc_cmdfunc; chip->legacy.waitfunc = fsl_elbc_wait; chip->legacy.set_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 6f4afc44381a..e65d274399f9 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -864,7 +864,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->legacy.write_buf = fsl_ifc_write_buf; chip->legacy.read_buf = fsl_ifc_read_buf; - chip->select_chip = fsl_ifc_select_chip; + chip->legacy.select_chip = fsl_ifc_select_chip; chip->legacy.cmdfunc = fsl_ifc_cmdfunc; chip->legacy.waitfunc = fsl_ifc_wait; chip->legacy.set_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index 673c5a0c9345..5ccc28ec0985 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -170,7 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun, fun->chip.ecc.mode = NAND_ECC_SOFT; fun->chip.ecc.algo = NAND_ECC_HAMMING; if (fun->mchip_count > 1) - fun->chip.select_chip = fun_select_chip; + fun->chip.legacy.select_chip = fun_select_chip; if (fun->rnb_gpio[0] >= 0) fun->chip.legacy.dev_ready = fun_chip_ready; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 302ddd3d4a5f..c461d5efabc0 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1907,7 +1907,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); - chip->select_chip = gpmi_select_chip; + chip->legacy.select_chip = gpmi_select_chip; chip->setup_data_interface = gpmi_setup_data_interface; chip->legacy.cmd_ctrl = gpmi_cmd_ctrl; chip->legacy.dev_ready = gpmi_dev_ready; diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index f043938ee36b..e41c13499fd5 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -783,7 +783,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) nand_set_controller_data(chip, host); nand_set_flash_node(chip, np); chip->legacy.cmdfunc = hisi_nfc_cmdfunc; - chip->select_chip = hisi_nfc_select_chip; + chip->legacy.select_chip = hisi_nfc_select_chip; chip->legacy.read_byte = hisi_nfc_read_byte; chip->legacy.write_buf = hisi_nfc_write_buf; chip->legacy.read_buf = hisi_nfc_read_buf; diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index d271004f16b0..0bcfdd3d66a8 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -427,7 +427,7 @@ static int jz_nand_probe(struct platform_device *pdev) chip->legacy.chip_delay = 50; chip->legacy.cmd_ctrl = jz_nand_cmd_ctrl; - chip->select_chip = jz_nand_select_chip; + chip->legacy.select_chip = jz_nand_select_chip; chip->dummy_controller.ops = &jz_nand_controller_ops; if (nand->busy_gpio) diff --git a/drivers/mtd/nand/raw/jz4780_nand.c b/drivers/mtd/nand/raw/jz4780_nand.c index cdf22100ab77..22e58975f0d5 100644 --- a/drivers/mtd/nand/raw/jz4780_nand.c +++ b/drivers/mtd/nand/raw/jz4780_nand.c @@ -279,7 +279,7 @@ static int jz4780_nand_init_chip(struct platform_device *pdev, chip->legacy.IO_ADDR_W = cs->base + OFFSET_DATA; chip->legacy.chip_delay = RB_DELAY_US; chip->options = NAND_NO_SUBPAGE_WRITE; - chip->select_chip = jz4780_nand_select_chip; + chip->legacy.select_chip = jz4780_nand_select_chip; chip->legacy.cmd_ctrl = jz4780_nand_cmd_ctrl; chip->ecc.mode = NAND_ECC_HW; chip->controller = &nfc->controller; diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 86a0aabe08df..062cd1eb2861 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -697,7 +697,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->legacy.read_byte = mpc5121_nfc_read_byte; chip->legacy.read_buf = mpc5121_nfc_read_buf; chip->legacy.write_buf = mpc5121_nfc_write_buf; - chip->select_chip = mpc5121_nfc_select_chip; + chip->legacy.select_chip = mpc5121_nfc_select_chip; chip->legacy.set_features = nand_get_set_features_notsupp; chip->legacy.get_features = nand_get_set_features_notsupp; chip->bbt_options = NAND_BBT_USE_FLASH; @@ -712,7 +712,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) return retval; } - chip->select_chip = ads5121_select_chip; + chip->legacy.select_chip = ads5121_select_chip; } /* Enable NFC clock */ diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 2bb0df1b7244..ce124f8c02cd 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1333,7 +1333,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ; nand->legacy.dev_ready = mtk_nfc_dev_ready; - nand->select_chip = mtk_nfc_select_chip; + nand->legacy.select_chip = mtk_nfc_select_chip; nand->legacy.write_byte = mtk_nfc_write_byte; nand->legacy.write_buf = mtk_nfc_write_buf; nand->legacy.read_byte = mtk_nfc_read_byte; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 88bd3f6a499c..c00b1d408a04 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1828,7 +1828,7 @@ static int mxcnd_probe(struct platform_device *pdev) this->ecc.bytes = host->devtype_data->eccbytes; host->eccsize = host->devtype_data->eccsize; - this->select_chip = host->devtype_data->select_chip; + this->legacy.select_chip = host->devtype_data->select_chip; this->ecc.size = 512; mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 93a19f551796..cef6633fdce9 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -248,8 +248,8 @@ void nand_select_target(struct nand_chip *chip, unsigned int cs) chip->cur_cs = cs; - if (chip->select_chip) - chip->select_chip(chip, cs); + if (chip->legacy.select_chip) + chip->legacy.select_chip(chip, cs); } EXPORT_SYMBOL_GPL(nand_select_target); @@ -262,8 +262,8 @@ EXPORT_SYMBOL_GPL(nand_select_target); */ void nand_deselect_target(struct nand_chip *chip) { - if (chip->select_chip) - chip->select_chip(chip, -1); + if (chip->legacy.select_chip) + chip->legacy.select_chip(chip, -1); chip->cur_cs = -1; } diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index f76b9356ba9c..4596a538b967 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -592,8 +592,8 @@ void nand_legacy_set_defaults(struct nand_chip *chip) if (chip->legacy.waitfunc == NULL) chip->legacy.waitfunc = nand_wait; - if (!chip->select_chip) - chip->select_chip = nand_select_chip; + if (!chip->legacy.select_chip) + chip->legacy.select_chip = nand_select_chip; /* If called twice, pointers that depend on busw may need to be reset */ if (!chip->legacy.read_byte || chip->legacy.read_byte == nand_read_byte) @@ -626,9 +626,10 @@ int nand_legacy_check_hooks(struct nand_chip *chip) /* * Default functions assigned for ->legacy.cmdfunc() and - * ->select_chip() both expect ->legacy.cmd_ctrl() to be populated. + * ->legacy.select_chip() both expect ->legacy.cmd_ctrl() to be + * populated. */ - if ((!chip->legacy.cmdfunc || !chip->select_chip) && + if ((!chip->legacy.cmdfunc || !chip->legacy.select_chip) && !chip->legacy.cmd_ctrl) { pr_err("->legacy.cmd_ctrl() should be provided\n"); return -EINVAL; diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index d49a7a17146c..9857e0e5acd4 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -146,7 +146,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->legacy.IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; chip->legacy.cmd_ctrl = ndfc_hwcontrol; chip->legacy.dev_ready = ndfc_ready; - chip->select_chip = ndfc_select_chip; + chip->legacy.select_chip = ndfc_select_chip; chip->legacy.chip_delay = 50; chip->controller = &ndfc->ndfc_control; chip->legacy.read_buf = ndfc_read_buf; diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index 86c536ddaf24..a994b76daa50 100644 --- a/drivers/mtd/nand/raw/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -63,7 +63,7 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.legacy.IO_ADDR_W = data->io_base; data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; - data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.legacy.select_chip = pdata->ctrl.select_chip; data->chip.legacy.write_buf = pdata->ctrl.write_buf; data->chip.legacy.read_buf = pdata->ctrl.read_buf; data->chip.legacy.chip_delay = pdata->chip.chip_delay; diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ef75dfa62a4f..6b76fb5c0aed 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2804,7 +2804,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, mtd->dev.parent = dev; chip->legacy.cmdfunc = qcom_nandc_command; - chip->select_chip = qcom_nandc_select_chip; + chip->legacy.select_chip = qcom_nandc_select_chip; chip->legacy.read_byte = qcom_nandc_read_byte; chip->legacy.read_buf = qcom_nandc_read_buf; chip->legacy.write_buf = qcom_nandc_write_buf; diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index d2e42e9d0e8c..a8905463701a 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -866,7 +866,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->legacy.write_buf = s3c2410_nand_write_buf; chip->legacy.read_buf = s3c2410_nand_read_buf; - chip->select_chip = s3c2410_nand_select_chip; + chip->legacy.select_chip = s3c2410_nand_select_chip; chip->legacy.chip_delay = 50; nand_set_controller_data(chip, nmtd); chip->options = set->options; diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 30edcc77b111..7ab50bc6ad3a 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1170,7 +1170,7 @@ static int flctl_probe(struct platform_device *pdev) nand->legacy.read_byte = flctl_read_byte; nand->legacy.write_buf = flctl_write_buf; nand->legacy.read_buf = flctl_read_buf; - nand->select_chip = flctl_select_chip; + nand->legacy.select_chip = flctl_select_chip; nand->legacy.cmdfunc = flctl_cmdfunc; nand->legacy.set_features = nand_get_set_features_notsupp; nand->legacy.get_features = nand_get_set_features_notsupp; diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 51b1a548064b..e489a6ff57d7 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1922,7 +1922,7 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, */ nand->ecc.mode = NAND_ECC_HW; nand_set_flash_node(nand, np); - nand->select_chip = sunxi_nfc_select_chip; + nand->legacy.select_chip = sunxi_nfc_select_chip; nand->legacy.cmd_ctrl = sunxi_nfc_cmd_ctrl; nand->legacy.read_buf = sunxi_nfc_read_buf; nand->legacy.write_buf = sunxi_nfc_write_buf; diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index 8818f893f300..ebca4579c033 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -567,7 +567,7 @@ static int chip_init(struct device *dev, struct device_node *np) chip->legacy.read_byte = tango_read_byte; chip->legacy.write_buf = tango_write_buf; chip->legacy.read_buf = tango_read_buf; - chip->select_chip = tango_select_chip; + chip->legacy.select_chip = tango_select_chip; chip->legacy.cmd_ctrl = tango_cmd_ctrl; chip->legacy.dev_ready = tango_dev_ready; chip->setup_data_interface = tango_set_timings; diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c index a234a5cb4868..4cb78106af14 100644 --- a/drivers/mtd/nand/raw/xway_nand.c +++ b/drivers/mtd/nand/raw/xway_nand.c @@ -176,7 +176,7 @@ static int xway_nand_probe(struct platform_device *pdev) data->chip.legacy.cmd_ctrl = xway_cmd_ctrl; data->chip.legacy.dev_ready = xway_dev_ready; - data->chip.select_chip = xway_select_chip; + data->chip.legacy.select_chip = xway_select_chip; data->chip.legacy.write_buf = xway_write_buf; data->chip.legacy.read_buf = xway_read_buf; data->chip.legacy.read_byte = xway_read_byte; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index aa1512df38a9..40b74fb1792d 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -902,6 +902,7 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * struct nand_legacy - NAND chip legacy fields/hooks * @IO_ADDR_R: address to read the 8 I/O lines of the flash device * @IO_ADDR_W: address to write the 8 I/O lines of the flash device + * @select_chip: select/deselect a specific target/die * @read_byte: read one byte from the chip * @write_byte: write a single byte to the chip on the low 8 I/O lines * @write_buf: write data from the buffer to the chip @@ -927,6 +928,7 @@ int nand_op_parser_exec_op(struct nand_chip *chip, struct nand_legacy { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; + void (*select_chip)(struct nand_chip *chip, int cs); u8 (*read_byte)(struct nand_chip *chip); void (*write_byte)(struct nand_chip *chip, u8 byte); void (*write_buf)(struct nand_chip *chip, const u8 *buf, int len); @@ -954,7 +956,6 @@ struct nand_legacy { * you're modifying an existing driver that is using those * fields/hooks, you should consider reworking the driver * avoid using them. - * @select_chip: [REPLACEABLE] select chip nr * @exec_op: controller specific method to execute NAND operations. * This method replaces ->cmdfunc(), * ->legacy.{read,write}_{buf,byte,word}(), @@ -1040,7 +1041,6 @@ struct nand_chip { struct nand_legacy legacy; - void (*select_chip)(struct nand_chip *chip, int cs); int (*exec_op)(struct nand_chip *chip, const struct nand_operation *op, bool check_only); -- cgit v1.2.3 From f2abfeb2078b9682bfeb77f91816fcf2177b3051 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:23 +0100 Subject: mtd: rawnand: Move the ->exec_op() method to nand_controller_ops ->exec_op() is a controller method and has nothing to do in the nand_chip struct. Let's move it to the nand_controller_ops struct and adjust the core and drivers accordingly. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ams-delta.c | 7 ++- drivers/mtd/nand/raw/fsmc_nand.c | 2 +- drivers/mtd/nand/raw/internals.h | 13 ++++- drivers/mtd/nand/raw/marvell_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 51 +++++++++---------- drivers/mtd/nand/raw/nand_hynix.c | 4 +- drivers/mtd/nand/raw/nand_legacy.c | 4 +- drivers/mtd/nand/raw/tegra_nand.c | 2 +- drivers/mtd/nand/raw/vf610_nfc.c | 4 +- include/linux/mtd/rawnand.h | 99 ++++++++++++++++++------------------- 10 files changed, 100 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 611c822e967f..f8eb4a419e77 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -176,6 +176,10 @@ static int ams_delta_exec_op(struct nand_chip *this, return ret; } +static const struct nand_controller_ops ams_delta_ops = { + .exec_op = ams_delta_exec_op, +}; + /* * Main initialization routine */ @@ -216,8 +220,6 @@ static int ams_delta_init(struct platform_device *pdev) priv->io_base = io_base; nand_set_controller_data(this, priv); - this->exec_op = ams_delta_exec_op; - priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); if (IS_ERR(priv->gpiod_rdy)) { err = PTR_ERR(priv->gpiod_rdy); @@ -277,6 +279,7 @@ static int ams_delta_init(struct platform_device *pdev) ams_delta_dir_input(priv, true); /* Initialize the NAND controller object embedded in ams_delta_nand. */ + priv->base.ops = &ams_delta_ops; nand_controller_init(&priv->base); this->controller = &priv->base; diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index ea69ac6e6d7a..1eb5008e7453 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -995,6 +995,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) static const struct nand_controller_ops fsmc_nand_controller_ops = { .attach_chip = fsmc_nand_attach_chip, + .exec_op = fsmc_exec_op, }; /* @@ -1082,7 +1083,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand_set_flash_node(nand, pdev->dev.of_node); mtd->dev.parent = &pdev->dev; - nand->exec_op = fsmc_exec_op; /* * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index b62728d5884b..ac66b458566f 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -95,16 +95,25 @@ void nand_decode_ext_id(struct nand_chip *chip); void panic_nand_wait(struct nand_chip *chip, unsigned long timeo); void sanitize_string(uint8_t *s, size_t len); +static inline bool nand_has_exec_op(struct nand_chip *chip) +{ + if (!chip->controller || !chip->controller->ops || + !chip->controller->ops->exec_op) + return false; + + return true; +} + static inline int nand_exec_op(struct nand_chip *chip, const struct nand_operation *op) { - if (!chip->exec_op) + if (!nand_has_exec_op(chip)) return -ENOTSUPP; if (WARN_ON(op->cs >= chip->numchips)) return -EINVAL; - return chip->exec_op(chip, op, false); + return chip->controller->ops->exec_op(chip, op, false); } /* BBT functions */ diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index ba7a45fb1905..2e8257fe7d00 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2505,6 +2505,7 @@ static int marvell_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops marvell_nand_controller_ops = { .attach_chip = marvell_nand_attach_chip, + .exec_op = marvell_nfc_exec_op, }; static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, @@ -2627,7 +2628,6 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, chip->controller = &nfc->controller; nand_set_flash_node(chip, np); - chip->exec_op = marvell_nfc_exec_op; if (!of_property_read_bool(np, "marvell,nand-keep-config")) chip->setup_data_interface = marvell_nfc_setup_data_interface; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index cef6633fdce9..eabef6a3857e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -678,7 +678,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) u8 status = 0; int ret; - if (!chip->exec_op) + if (!nand_has_exec_op(chip)) return -ENOTSUPP; /* Wait tWB before polling the STATUS reg. */ @@ -1117,7 +1117,7 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { if (mtd->writesize > 512) return nand_lp_exec_read_page_op(chip, page, offset_in_page, buf, @@ -1156,7 +1156,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, if (len && !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1211,7 +1211,7 @@ int nand_change_read_column_op(struct nand_chip *chip, if (mtd->writesize <= 512) return -ENOTSUPP; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[2] = {}; @@ -1270,7 +1270,7 @@ int nand_read_oob_op(struct nand_chip *chip, unsigned int page, if (offset_in_oob + len > mtd->oobsize) return -EINVAL; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return nand_read_page_op(chip, page, mtd->writesize + offset_in_oob, buf, len); @@ -1383,7 +1383,7 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, false); @@ -1410,7 +1410,7 @@ int nand_prog_page_end_op(struct nand_chip *chip) int ret; u8 status; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1469,7 +1469,7 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, true); } else { @@ -1517,7 +1517,7 @@ int nand_change_write_column_op(struct nand_chip *chip, if (mtd->writesize <= 512) return -ENOTSUPP; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[2]; @@ -1572,7 +1572,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, if (len && !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1611,7 +1611,7 @@ EXPORT_SYMBOL_GPL(nand_readid_op); */ int nand_status_op(struct nand_chip *chip, u8 *status) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1648,7 +1648,7 @@ EXPORT_SYMBOL_GPL(nand_status_op); */ int nand_exit_status_op(struct nand_chip *chip) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READ0, 0), }; @@ -1680,7 +1680,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) int ret; u8 status; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); u8 addrs[3] = { page, page >> 8, page >> 16 }; @@ -1739,7 +1739,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, const u8 *params = data; int i, ret; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1786,7 +1786,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, u8 *params = data; int i; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1812,7 +1812,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, unsigned int delay_ns) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms), PSEC_TO_NSEC(delay_ns)), @@ -1843,7 +1843,7 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, */ int nand_reset_op(struct nand_chip *chip) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { const struct nand_sdr_timings *sdr = nand_get_sdr_timings(&chip->data_interface); struct nand_op_instr instrs[] = { @@ -1880,7 +1880,7 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, if (!len || !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_DATA_IN(len, buf, 0), }; @@ -1924,7 +1924,7 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf, if (!len || !buf) return -EINVAL; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_DATA_OUT(len, buf, 0), }; @@ -4417,13 +4417,14 @@ static void nand_shutdown(struct mtd_info *mtd) /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { - nand_legacy_set_defaults(chip); - + /* If no controller is provided, use the dummy one. */ if (!chip->controller) { chip->controller = &chip->dummy_controller; nand_controller_init(chip->controller); } + nand_legacy_set_defaults(chip); + if (!chip->buf_align) chip->buf_align = 1; } @@ -5025,10 +5026,6 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, if (!mtd->name && mtd->dev.parent) mtd->name = dev_name(mtd->dev.parent); - ret = nand_legacy_check_hooks(chip); - if (ret) - return ret; - /* * Start with chips->numchips = maxchips to let nand_select_target() do * its job. chip->numchips will be adjusted after. @@ -5038,6 +5035,10 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, /* Set the default functions */ nand_set_defaults(chip); + ret = nand_legacy_check_hooks(chip); + if (ret) + return ret; + /* Read the flash type */ ret = nand_detect(chip, table); if (ret) { diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 1e4499d01e14..343f477362d1 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -80,7 +80,7 @@ static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) { - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_CMD(cmd, 0), }; @@ -98,7 +98,7 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) { u16 column = ((u16)addr << 8) | addr; - if (chip->exec_op) { + if (nand_has_exec_op(chip)) { struct nand_op_instr instrs[] = { NAND_OP_ADDR(1, &addr, 0), NAND_OP_8BIT_DATA_OUT(1, &val, 0), diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index 4596a538b967..47364237861e 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -577,7 +577,7 @@ void nand_legacy_set_defaults(struct nand_chip *chip) { unsigned int busw = chip->options & NAND_BUSWIDTH_16; - if (chip->exec_op) + if (nand_has_exec_op(chip)) return; /* check for proper chip_delay setup, set 20us if not */ @@ -621,7 +621,7 @@ int nand_legacy_check_hooks(struct nand_chip *chip) * ->legacy.cmdfunc() is legacy and will only be used if ->exec_op() is * not populated. */ - if (chip->exec_op) + if (nand_has_exec_op(chip)) return 0; /* diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 590393d93ffc..2fe6de09f4ff 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -1050,6 +1050,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops tegra_nand_controller_ops = { .attach_chip = &tegra_nand_attach_chip, + .exec_op = tegra_nand_exec_op, }; static int tegra_nand_chips_init(struct device *dev, @@ -1112,7 +1113,6 @@ static int tegra_nand_chips_init(struct device *dev, mtd->name = "tegra_nand"; chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; - chip->exec_op = tegra_nand_exec_op; chip->setup_data_interface = tegra_nand_setup_data_interface; ret = nand_scan(chip, 1); diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index 49a174e30211..0fa7cac4ce14 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -812,6 +812,8 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops vf610_nfc_controller_ops = { .attach_chip = vf610_nfc_attach_chip, + .exec_op = vf610_nfc_exec_op, + }; static int vf610_nfc_probe(struct platform_device *pdev) @@ -879,8 +881,6 @@ static int vf610_nfc_probe(struct platform_device *pdev) goto err_disable_clk; } - chip->exec_op = vf610_nfc_exec_op; - chip->options |= NAND_NO_SUBPAGE_WRITE; init_completion(&nfc->cmd_done); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 40b74fb1792d..297b40c56403 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -240,49 +240,6 @@ struct nand_id { int len; }; -/** - * struct nand_controller_ops - Controller operations - * - * @attach_chip: this method is called after the NAND detection phase after - * flash ID and MTD fields such as erase size, page size and OOB - * size have been set up. ECC requirements are available if - * provided by the NAND chip or device tree. Typically used to - * choose the appropriate ECC configuration and allocate - * associated resources. - * This hook is optional. - * @detach_chip: free all resources allocated/claimed in - * nand_controller_ops->attach_chip(). - * This hook is optional. - */ -struct nand_controller_ops { - int (*attach_chip)(struct nand_chip *chip); - void (*detach_chip)(struct nand_chip *chip); -}; - -/** - * struct nand_controller - Structure used to describe a NAND controller - * - * @lock: protection lock - * @active: the mtd device which holds the controller currently - * @wq: wait queue to sleep on if a NAND operation is in - * progress used instead of the per chip wait queue - * when a hw controller is available. - * @ops: NAND controller operations. - */ -struct nand_controller { - spinlock_t lock; - struct nand_chip *active; - wait_queue_head_t wq; - const struct nand_controller_ops *ops; -}; - -static inline void nand_controller_init(struct nand_controller *nfc) -{ - nfc->active = NULL; - spin_lock_init(&nfc->lock); - init_waitqueue_head(&nfc->wq); -} - /** * struct nand_ecc_step_info - ECC step information of ECC engine * @stepsize: data bytes per ECC step @@ -897,6 +854,55 @@ struct nand_operation { int nand_op_parser_exec_op(struct nand_chip *chip, const struct nand_op_parser *parser, const struct nand_operation *op, bool check_only); +/** + * struct nand_controller_ops - Controller operations + * + * @attach_chip: this method is called after the NAND detection phase after + * flash ID and MTD fields such as erase size, page size and OOB + * size have been set up. ECC requirements are available if + * provided by the NAND chip or device tree. Typically used to + * choose the appropriate ECC configuration and allocate + * associated resources. + * This hook is optional. + * @detach_chip: free all resources allocated/claimed in + * nand_controller_ops->attach_chip(). + * This hook is optional. + * @exec_op: controller specific method to execute NAND operations. + * This method replaces chip->legacy.cmdfunc(), + * chip->legacy.{read,write}_{buf,byte,word}(), + * chip->legacy.dev_ready() and chip->legacy.waifunc(). + */ +struct nand_controller_ops { + int (*attach_chip)(struct nand_chip *chip); + void (*detach_chip)(struct nand_chip *chip); + int (*exec_op)(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only); +}; + +/** + * struct nand_controller - Structure used to describe a NAND controller + * + * @lock: protection lock + * @active: the mtd device which holds the controller currently + * @wq: wait queue to sleep on if a NAND operation is in + * progress used instead of the per chip wait queue + * when a hw controller is available. + * @ops: NAND controller operations. + */ +struct nand_controller { + spinlock_t lock; + struct nand_chip *active; + wait_queue_head_t wq; + const struct nand_controller_ops *ops; +}; + +static inline void nand_controller_init(struct nand_controller *nfc) +{ + nfc->active = NULL; + spin_lock_init(&nfc->lock); + init_waitqueue_head(&nfc->wq); +} /** * struct nand_legacy - NAND chip legacy fields/hooks @@ -956,10 +962,6 @@ struct nand_legacy { * you're modifying an existing driver that is using those * fields/hooks, you should consider reworking the driver * avoid using them. - * @exec_op: controller specific method to execute NAND operations. - * This method replaces ->cmdfunc(), - * ->legacy.{read,write}_{buf,byte,word}(), - * ->legacy.dev_ready() and ->waifunc(). * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure @@ -1041,9 +1043,6 @@ struct nand_chip { struct nand_legacy legacy; - int (*exec_op)(struct nand_chip *chip, - const struct nand_operation *op, - bool check_only); int (*setup_read_retry)(struct nand_chip *chip, int retry_mode); int (*setup_data_interface)(struct nand_chip *chip, int chipnr, const struct nand_data_interface *conf); -- cgit v1.2.3 From 7a08dbaedd365fa4eb7c9cd504c075e3336eb0c6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sun, 11 Nov 2018 08:55:24 +0100 Subject: mtd: rawnand: Move ->setup_data_interface() to nand_controller_ops ->setup_data_interface() is a controller specific method and should thus be placed in nand_controller_ops. In order to make that work with controllers that support keeping pre-configured timings we need to add a new NAND_KEEP_TIMINGS flag to inform the core it should skip the timings selection step. Signed-off-by: Boris Brezillon Tested-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/atmel/nand-controller.c | 5 +++-- drivers/mtd/nand/raw/denali.c | 3 ++- drivers/mtd/nand/raw/fsmc_nand.c | 7 ++++--- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- drivers/mtd/nand/raw/internals.h | 12 ++++++++++++ drivers/mtd/nand/raw/marvell_nand.c | 3 ++- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/mxc_nand.c | 12 +++++++++++- drivers/mtd/nand/raw/nand_base.c | 14 ++++++++------ drivers/mtd/nand/raw/nand_legacy.c | 2 +- drivers/mtd/nand/raw/s3c2410.c | 5 +++-- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- drivers/mtd/nand/raw/tango_nand.c | 2 +- drivers/mtd/nand/raw/tegra_nand.c | 2 +- include/linux/mtd/rawnand.h | 20 ++++++++++++++------ 15 files changed, 65 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index d5c58eb040d8..dcd3bd73e549 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1479,8 +1479,8 @@ static void atmel_nand_init(struct atmel_nand_controller *nc, chip->legacy.write_buf = atmel_nand_write_buf; chip->legacy.select_chip = atmel_nand_select_chip; - if (nc->mck && nc->caps->ops->setup_data_interface) - chip->setup_data_interface = atmel_nand_setup_data_interface; + if (!nc->mck || !nc->caps->ops->setup_data_interface) + chip->options |= NAND_KEEP_TIMINGS; /* Some NANDs require a longer delay than the default one (20us). */ chip->legacy.chip_delay = 40; @@ -1908,6 +1908,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops atmel_nand_controller_ops = { .attach_chip = atmel_nand_attach_chip, + .setup_data_interface = atmel_nand_setup_data_interface, }; static int atmel_nand_controller_init(struct atmel_nand_controller *nc, diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 64895ca68c8d..bad3b8ad5e0a 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1316,6 +1316,7 @@ static void denali_detach_chip(struct nand_chip *chip) static const struct nand_controller_ops denali_controller_ops = { .attach_chip = denali_attach_chip, .detach_chip = denali_detach_chip, + .setup_data_interface = denali_setup_data_interface, }; int denali_init(struct denali_nand_info *denali) @@ -1372,7 +1373,7 @@ int denali_init(struct denali_nand_info *denali) /* clk rate info is needed for setup_data_interface */ if (denali->clk_rate && denali->clk_x_rate) - chip->setup_data_interface = denali_setup_data_interface; + chip->options |= NAND_KEEP_TIMINGS; chip->dummy_controller.ops = &denali_controller_ops; ret = nand_scan(chip, denali->max_banks); diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 1eb5008e7453..61927c4c2650 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -996,6 +996,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) static const struct nand_controller_ops fsmc_nand_controller_ops = { .attach_chip = fsmc_nand_attach_chip, .exec_op = fsmc_exec_op, + .setup_data_interface = fsmc_setup_data_interface, }; /* @@ -1108,10 +1109,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } } - if (host->dev_timings) + if (host->dev_timings) { fsmc_nand_setup(host, host->dev_timings); - else - nand->setup_data_interface = fsmc_setup_data_interface; + nand->options |= NAND_KEEP_TIMINGS; + } if (AMBA_REV_BITS(host->pid) >= 8) { nand->ecc.read_page = fsmc_read_page_hwecc; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c461d5efabc0..25f9fe79796a 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1889,6 +1889,7 @@ static int gpmi_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops gpmi_nand_controller_ops = { .attach_chip = gpmi_nand_attach_chip, + .setup_data_interface = gpmi_setup_data_interface, }; static int gpmi_nand_init(struct gpmi_nand_data *this) @@ -1908,7 +1909,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) nand_set_controller_data(chip, this); nand_set_flash_node(chip, this->pdev->dev.of_node); chip->legacy.select_chip = gpmi_select_chip; - chip->setup_data_interface = gpmi_setup_data_interface; chip->legacy.cmd_ctrl = gpmi_cmd_ctrl; chip->legacy.dev_ready = gpmi_dev_ready; chip->legacy.read_byte = gpmi_read_byte; diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index ac66b458566f..fbf6ca015cd7 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -116,6 +116,18 @@ static inline int nand_exec_op(struct nand_chip *chip, return chip->controller->ops->exec_op(chip, op, false); } +static inline bool nand_has_setup_data_iface(struct nand_chip *chip) +{ + if (!chip->controller || !chip->controller->ops || + !chip->controller->ops->setup_data_interface) + return false; + + if (chip->options & NAND_KEEP_TIMINGS) + return false; + + return true; +} + /* BBT functions */ int nand_markbad_bbt(struct nand_chip *chip, loff_t offs); int nand_isreserved_bbt(struct nand_chip *chip, loff_t offs); diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 2e8257fe7d00..b7b4d9b14da1 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2506,6 +2506,7 @@ static int marvell_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops marvell_nand_controller_ops = { .attach_chip = marvell_nand_attach_chip, .exec_op = marvell_nfc_exec_op, + .setup_data_interface = marvell_nfc_setup_data_interface, }; static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, @@ -2629,7 +2630,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, nand_set_flash_node(chip, np); if (!of_property_read_bool(np, "marvell,nand-keep-config")) - chip->setup_data_interface = marvell_nfc_setup_data_interface; + chip->options |= NAND_KEEP_TIMINGS; mtd = nand_to_mtd(chip); mtd->dev.parent = dev; diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index ce124f8c02cd..b6b4602f5132 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1288,6 +1288,7 @@ static int mtk_nfc_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops mtk_nfc_controller_ops = { .attach_chip = mtk_nfc_attach_chip, + .setup_data_interface = mtk_nfc_setup_data_interface, }; static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, @@ -1339,7 +1340,6 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, nand->legacy.read_byte = mtk_nfc_read_byte; nand->legacy.read_buf = mtk_nfc_read_buf; nand->legacy.cmd_ctrl = mtk_nfc_cmd_ctrl; - nand->setup_data_interface = mtk_nfc_setup_data_interface; /* set default mode in case dt entry is missing */ nand->ecc.mode = NAND_ECC_HW; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index c00b1d408a04..9b75d894cb74 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1738,8 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip) return 0; } +static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->setup_data_interface(chip, chipnr, conf); +} + static const struct nand_controller_ops mxcnd_controller_ops = { .attach_chip = mxcnd_attach_chip, + .setup_data_interface = mxcnd_setup_data_interface, }; static int mxcnd_probe(struct platform_device *pdev) @@ -1800,7 +1809,8 @@ static int mxcnd_probe(struct platform_device *pdev) if (err < 0) return err; - this->setup_data_interface = host->devtype_data->setup_data_interface; + if (!host->devtype_data->setup_data_interface) + this->options |= NAND_KEEP_TIMINGS; if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index eabef6a3857e..3fc5c00f8dba 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -807,7 +807,7 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) { int ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* @@ -825,7 +825,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) */ onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); - ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface); + ret = chip->controller->ops->setup_data_interface(chip, chipnr, + &chip->data_interface); if (ret) pr_err("Failed to configure data interface to SDR timing mode 0\n"); @@ -852,7 +853,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) }; int ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* Change the mode on the chip side (if supported by the NAND chip) */ @@ -866,7 +867,8 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) } /* Change the mode on the controller side */ - ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface); + ret = chip->controller->ops->setup_data_interface(chip, chipnr, + &chip->data_interface); if (ret) return ret; @@ -921,7 +923,7 @@ static int nand_init_data_interface(struct nand_chip *chip) { int modes, mode, ret; - if (!chip->setup_data_interface) + if (!nand_has_setup_data_iface(chip)) return 0; /* @@ -947,7 +949,7 @@ static int nand_init_data_interface(struct nand_chip *chip) * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the * controller supports the requested timings. */ - ret = chip->setup_data_interface(chip, + ret = chip->controller->ops->setup_data_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, &chip->data_interface); if (!ret) { diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index 47364237861e..43575943f13b 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -364,7 +364,7 @@ static void nand_ccs_delay(struct nand_chip *chip) * Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ - if (chip->setup_data_interface) + if (nand_has_setup_data_iface(chip)) ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); else ndelay(500); diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index a8905463701a..adc7a196e383 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -876,8 +876,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, * let's keep behavior unchanged for legacy boards booting via pdata and * auto-detect timings only when booting with a device tree. */ - if (np) - chip->setup_data_interface = s3c2410_nand_setup_data_interface; + if (!np) + chip->options |= NAND_KEEP_TIMINGS; switch (info->cpu_type) { case TYPE_S3C2410: @@ -1011,6 +1011,7 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops s3c24xx_nand_controller_ops = { .attach_chip = s3c2410_nand_attach_chip, + .setup_data_interface = s3c2410_nand_setup_data_interface, }; static const struct of_device_id s3c24xx_nand_dt_ids[] = { diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index e489a6ff57d7..a5c83cbe4897 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1847,6 +1847,7 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand) static const struct nand_controller_ops sunxi_nand_controller_ops = { .attach_chip = sunxi_nand_attach_chip, + .setup_data_interface = sunxi_nfc_setup_data_interface, }; static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, @@ -1927,7 +1928,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, nand->legacy.read_buf = sunxi_nfc_read_buf; nand->legacy.write_buf = sunxi_nfc_write_buf; nand->legacy.read_byte = sunxi_nfc_read_byte; - nand->setup_data_interface = sunxi_nfc_setup_data_interface; mtd = nand_to_mtd(nand); mtd->dev.parent = dev; diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index ebca4579c033..cb3beda88789 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -530,6 +530,7 @@ static int tango_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops tango_controller_ops = { .attach_chip = tango_attach_chip, + .setup_data_interface = tango_set_timings, }; static int chip_init(struct device *dev, struct device_node *np) @@ -570,7 +571,6 @@ static int chip_init(struct device *dev, struct device_node *np) chip->legacy.select_chip = tango_select_chip; chip->legacy.cmd_ctrl = tango_cmd_ctrl; chip->legacy.dev_ready = tango_dev_ready; - chip->setup_data_interface = tango_set_timings; chip->options = NAND_USE_BOUNCE_BUFFER | NAND_NO_SUBPAGE_WRITE | NAND_WAIT_TCCS; diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 2fe6de09f4ff..13be32c38194 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -1051,6 +1051,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops tegra_nand_controller_ops = { .attach_chip = &tegra_nand_attach_chip, .exec_op = tegra_nand_exec_op, + .setup_data_interface = tegra_nand_setup_data_interface, }; static int tegra_nand_chips_init(struct device *dev, @@ -1113,7 +1114,6 @@ static int tegra_nand_chips_init(struct device *dev, mtd->name = "tegra_nand"; chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER; - chip->setup_data_interface = tegra_nand_setup_data_interface; ret = nand_scan(chip, 1); if (ret) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 297b40c56403..f50f40643895 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -203,6 +203,13 @@ enum nand_ecc_algo { */ #define NAND_IS_BOOT_MEDIUM 0x00400000 +/* + * Do not try to tweak the timings at runtime. This is needed when the + * controller initializes the timings on itself or when it relies on + * configuration done by the bootloader. + */ +#define NAND_KEEP_TIMINGS 0x00800000 + /* Cell info constants */ #define NAND_CI_CHIPNR_MSK 0x03 #define NAND_CI_CELLTYPE_MSK 0x0C @@ -871,6 +878,11 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * This method replaces chip->legacy.cmdfunc(), * chip->legacy.{read,write}_{buf,byte,word}(), * chip->legacy.dev_ready() and chip->legacy.waifunc(). + * @setup_data_interface: 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 + * only checked. + * This hook is optional. */ struct nand_controller_ops { int (*attach_chip)(struct nand_chip *chip); @@ -878,6 +890,8 @@ struct nand_controller_ops { int (*exec_op)(struct nand_chip *chip, const struct nand_operation *op, bool check_only); + int (*setup_data_interface)(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf); }; /** @@ -1019,10 +1033,6 @@ struct nand_legacy { * cur_cs < numchips. NAND Controller drivers should not * modify this value, but they're allowed to read it. * @read_retries: [INTERN] the number of read retry modes supported - * @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 - * only checked. * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -1044,8 +1054,6 @@ struct nand_chip { struct nand_legacy legacy; int (*setup_read_retry)(struct nand_chip *chip, int retry_mode); - int (*setup_data_interface)(struct nand_chip *chip, int chipnr, - const struct nand_data_interface *conf); unsigned int options; unsigned int bbt_options; -- cgit v1.2.3 From 7b6a9b28ecf2fd2e2f5dcdb6d4fa8044b48bdb74 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 20 Nov 2018 10:02:39 +0100 Subject: mtd: rawnand: Deprecate the dummy_controller field We try to force NAND controller drivers to properly separate the NAND controller object from the NAND chip one, so let's deprecate the dummy controller object embedded in nand_chip to encourage them to create their own instance. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/cafe_nand.c | 2 +- drivers/mtd/nand/raw/davinci_nand.c | 2 +- drivers/mtd/nand/raw/denali.c | 2 +- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- drivers/mtd/nand/raw/hisi504_nand.c | 2 +- drivers/mtd/nand/raw/jz4740_nand.c | 2 +- drivers/mtd/nand/raw/lpc32xx_mlc.c | 2 +- drivers/mtd/nand/raw/lpc32xx_slc.c | 2 +- drivers/mtd/nand/raw/mxc_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 4 ++-- drivers/mtd/nand/raw/nandsim.c | 2 +- drivers/mtd/nand/raw/sh_flctl.c | 2 +- drivers/mtd/nand/raw/sm_common.c | 2 +- include/linux/mtd/rawnand.h | 6 +++--- 14 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index a85f5fa5c66d..b1c0cd6b49da 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -780,7 +780,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->usedma = 0; /* Scan to find existence of the device */ - cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops; + cafe->nand.legacy.dummy_controller.ops = &cafe_nand_controller_ops; err = nand_scan(&cafe->nand, 2); if (err) goto out_irq; diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index f430aeb917e8..27bafa5e1ca1 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -801,7 +801,7 @@ static int nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - info->chip.dummy_controller.ops = &davinci_nand_controller_ops; + info->chip.legacy.dummy_controller.ops = &davinci_nand_controller_ops; ret = nand_scan(&info->chip, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index e1c3099d705a..eebac35304c6 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1325,7 +1325,7 @@ int denali_init(struct denali_nand_info *denali) if (denali->clk_rate && denali->clk_x_rate) chip->options |= NAND_KEEP_TIMINGS; - chip->dummy_controller.ops = &denali_controller_ops; + chip->legacy.dummy_controller.ops = &denali_controller_ops; ret = nand_scan(chip, denali->max_banks); if (ret) goto disable_irq; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 25f9fe79796a..ed405c9434fe 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1931,7 +1931,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) if (ret) goto err_out; - chip->dummy_controller.ops = &gpmi_nand_controller_ops; + chip->legacy.dummy_controller.ops = &gpmi_nand_controller_ops; ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); if (ret) goto err_out; diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index e41c13499fd5..f3f9aa160cff 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -799,7 +799,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) return ret; } - chip->dummy_controller.ops = &hisi_nfc_controller_ops; + chip->legacy.dummy_controller.ops = &hisi_nfc_controller_ops; ret = nand_scan(chip, max_chips); if (ret) return ret; diff --git a/drivers/mtd/nand/raw/jz4740_nand.c b/drivers/mtd/nand/raw/jz4740_nand.c index 0bcfdd3d66a8..f92ae5aa2a54 100644 --- a/drivers/mtd/nand/raw/jz4740_nand.c +++ b/drivers/mtd/nand/raw/jz4740_nand.c @@ -428,7 +428,7 @@ static int jz_nand_probe(struct platform_device *pdev) chip->legacy.chip_delay = 50; chip->legacy.cmd_ctrl = jz_nand_cmd_ctrl; chip->legacy.select_chip = jz_nand_select_chip; - chip->dummy_controller.ops = &jz_nand_controller_ops; + chip->legacy.dummy_controller.ops = &jz_nand_controller_ops; if (nand->busy_gpio) chip->legacy.dev_ready = jz_nand_dev_ready; diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index abbb655fe154..086964f8d424 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -799,7 +799,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) * Scan to find existence of the device and get the type of NAND device: * SMALL block or LARGE block. */ - nand_chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + nand_chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops; res = nand_scan(nand_chip, 1); if (res) goto free_irq; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index f2f2cdbb9d04..a2c5fdc875bd 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -924,7 +924,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* Find NAND device */ - chip->dummy_controller.ops = &lpc32xx_nand_controller_ops; + chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops; res = nand_scan(chip, 1); if (res) goto release_dma; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 9b75d894cb74..59554c187e01 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1891,7 +1891,7 @@ static int mxcnd_probe(struct platform_device *pdev) } /* Scan the NAND device */ - this->dummy_controller.ops = &mxcnd_controller_ops; + this->legacy.dummy_controller.ops = &mxcnd_controller_ops; err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1); if (err) goto escan; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 3fc5c00f8dba..cca4b24d2ffa 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4419,9 +4419,9 @@ static void nand_shutdown(struct mtd_info *mtd) /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { - /* If no controller is provided, use the dummy one. */ + /* If no controller is provided, use the dummy, legacy one. */ if (!chip->controller) { - chip->controller = &chip->dummy_controller; + chip->controller = &chip->legacy.dummy_controller; nand_controller_init(chip->controller); } diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index c452819f6123..2b3047d53558 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -2304,7 +2304,7 @@ static int __init ns_init_module(void) if ((retval = parse_gravepages()) != 0) goto error; - chip->dummy_controller.ops = &ns_controller_ops; + chip->legacy.dummy_controller.ops = &ns_controller_ops; retval = nand_scan(chip, 1); if (retval) { NS_ERR("Could not scan NAND Simulator device\n"); diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 7ab50bc6ad3a..cf6b1be1cf9c 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1183,7 +1183,7 @@ static int flctl_probe(struct platform_device *pdev) flctl_setup_dma(flctl); - nand->dummy_controller.ops = &flctl_nand_controller_ops; + nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops; ret = nand_scan(nand, 1); if (ret) goto err_chip; diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c index 6f063ef57640..409d036858dc 100644 --- a/drivers/mtd/nand/raw/sm_common.c +++ b/drivers/mtd/nand/raw/sm_common.c @@ -194,7 +194,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) chip->options |= NAND_SKIP_BBTSCAN; /* Scan for card properties */ - chip->dummy_controller.ops = &sm_controller_ops; + chip->legacy.dummy_controller.ops = &sm_controller_ops; flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids; ret = nand_scan_with_ids(chip, 1, flash_ids); if (ret) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index f50f40643895..33e240acdc6d 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -941,6 +941,8 @@ static inline void nand_controller_init(struct nand_controller *nfc) * @get_features: get the NAND chip features * @chip_delay: chip dependent delay for transferring data from array to read * regs (tR). + * @dummy_controller: dummy controller implementation for drivers that can + * only control a single chip * * If you look at this structure you're already wrong. These fields/hooks are * all deprecated. @@ -966,6 +968,7 @@ struct nand_legacy { int (*get_features)(struct nand_chip *chip, int feature_addr, u8 *subfeature_para); int chip_delay; + struct nand_controller dummy_controller; }; /** @@ -980,8 +983,6 @@ struct nand_legacy { * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure * @buf_align: minimum buffer alignment required by a platform - * @dummy_controller: dummy controller implementation for drivers that can - * only control a single chip * @state: [INTERN] the current state of the NAND device * @oob_poi: "poison value buffer," used for laying out OOB data * before writing @@ -1094,7 +1095,6 @@ struct nand_chip { struct nand_ecc_ctrl ecc; unsigned long buf_align; - struct nand_controller dummy_controller; uint8_t *bbt; struct nand_bbt_descr *bbt_td; -- cgit v1.2.3 From c93c613214ac70c87beab5422a60077bf126b855 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Wed, 28 Nov 2018 21:07:25 +0800 Subject: mtd: spinand: add support for GigaDevice GD5FxGQ4xA Add support for GigaDevice GD5F1G/2G/4GQ4xA SPI NAND. Signed-off-by: Chuanhong Guo Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/gigadevice.c | 148 ++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/gigadevice.c (limited to 'include') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index be5f73512ece..753125082640 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o macronix.o micron.o toshiba.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 87bdf2a7b724..479c2f2cf17f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -764,6 +764,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, &toshiba_spinand_manufacturer, diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c new file mode 100644 index 000000000000..e4141c20947a --- /dev/null +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: + * Chuanhong Guo + */ + +#include +#include +#include + +#define SPINAND_MFR_GIGADEVICE 0xC8 +#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) +#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has one byte reserved for bad block mark */ + region->offset = 1; + region->length = 7; + } + return 0; +} + +static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: + /* 1-7 bits are flipped. return the maximum. */ + return 7; + + case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: + return 8; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { + .ecc = gd5fxgq4xa_ooblayout_ecc, + .free = gd5fxgq4xa_ooblayout_free, +}; + +static const struct spinand_info gigadevice_spinand_table[] = { + SPINAND_INFO("GD5F1GQ4xA", 0xF1, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4xA", 0xF2, + NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4xA", 0xF4, + NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, + gd5fxgq4xa_ecc_get_status)), +}; + +static int gigadevice_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * For GD NANDs, There is an address byte needed to shift in before IDs + * are read out, so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_GIGADEVICE) + return 0; + + ret = spinand_match_and_init(spinand, gigadevice_spinand_table, + ARRAY_SIZE(gigadevice_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { + .detect = gigadevice_spinand_detect, +}; + +const struct spinand_manufacturer gigadevice_spinand_manufacturer = { + .id = SPINAND_MFR_GIGADEVICE, + .name = "GigaDevice", + .ops = &gigadevice_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 816c4b00abca..b92e2aa955b6 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -194,6 +194,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer toshiba_spinand_manufacturer; -- cgit v1.2.3 From 0005aad094538e1c290b1cdb5b940e4a16f405b0 Mon Sep 17 00:00:00 2001 From: Yogesh Narayan Gaur Date: Fri, 12 Oct 2018 02:23:08 +0000 Subject: mtd: spi-nor: add macros related to MICRON flash Some MICRON related macros in spi-nor domain were ST. Rename entries related to STMicroelectronics under macro SNOR_MFR_ST. Added entry of MFR Id for Micron flashes, 0x002C. Signed-off-by: Yogesh Gaur Reviewed-by: Tudor Ambarus Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 9 ++++++--- include/linux/mtd/cfi.h | 1 + include/linux/mtd/spi-nor.h | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 5ca4aaf560da..33cc51cea9f9 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -284,6 +284,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, u8 cmd; switch (JEDEC_MFR(info)) { + case SNOR_MFR_ST: case SNOR_MFR_MICRON: /* Some Micron need WREN command; all will accept it */ need_wren = true; @@ -1391,7 +1392,7 @@ static const struct flash_info spi_nor_ids[] = { { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, - /* Micron */ + /* Micron <--> ST Micro */ { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, @@ -3324,6 +3325,7 @@ static int spi_nor_init_params(struct spi_nor *nor, params->quad_enable = macronix_quad_enable; break; + case SNOR_MFR_ST: case SNOR_MFR_MICRON: break; @@ -3774,8 +3776,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_resume = spi_nor_resume; /* NOR protection support for STmicro/Micron chips and similar */ - if (JEDEC_MFR(info) == SNOR_MFR_MICRON || - info->flags & SPI_NOR_HAS_LOCK) { + if (JEDEC_MFR(info) == SNOR_MFR_ST || + JEDEC_MFR(info) == SNOR_MFR_MICRON || + info->flags & SPI_NOR_HAS_LOCK) { nor->flash_lock = stm_lock; nor->flash_unlock = stm_unlock; nor->flash_is_locked = stm_is_locked; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 9b57a9b1b081..cbf77168658c 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -377,6 +377,7 @@ struct cfi_fixup { #define CFI_MFR_SHARP 0x00B0 #define CFI_MFR_SST 0x00BF #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_MICRON 0x002C /* Micron */ #define CFI_MFR_TOSHIBA 0x0098 #define CFI_MFR_WINBOND 0x00DA diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 7f0c7303575e..8b1acf68b7ac 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -23,7 +23,8 @@ #define SNOR_MFR_ATMEL CFI_MFR_ATMEL #define SNOR_MFR_GIGADEVICE 0xc8 #define SNOR_MFR_INTEL CFI_MFR_INTEL -#define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */ +#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro */ +#define SNOR_MFR_MICRON CFI_MFR_MICRON /* Micron */ #define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX #define SNOR_MFR_SPANSION CFI_MFR_AMD #define SNOR_MFR_SST CFI_MFR_SST -- cgit v1.2.3 From 548ed6847f5303e4f33ecd6de5670cac15bfe6ac Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 6 Dec 2018 11:37:34 +0100 Subject: mtd: spi-nor: Add the SNOR_F_4B_OPCODES flag Some flash_info entries have the SPI_NOR_4B_OPCODES flag set to let the core know that the flash supports 4B opcode. While this solution works fine for id-based caps detection, it doesn't work that well when relying on SFDP-based caps detection. Let's add an SNOR_F_4B_OPCODES flag so that the SFDP parsing code can set it when appropriate. Signed-off-by: Boris Brezillon Reviewed-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++---------- include/linux/mtd/spi-nor.h | 1 + 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1423bbaa9762..320264d4fde1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -3365,6 +3365,7 @@ static int spi_nor_init_params(struct spi_nor *nor, if (spi_nor_parse_sfdp(nor, &sfdp_params)) { nor->addr_width = 0; + nor->flags &= ~SNOR_F_4B_OPCODES; /* restore previous erase map */ memcpy(&nor->erase_map, &prev_map, sizeof(nor->erase_map)); @@ -3665,9 +3666,7 @@ static int spi_nor_init(struct spi_nor *nor) } } - if ((nor->addr_width == 4) && - (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && - !(nor->info->flags & SPI_NOR_4B_OPCODES)) { + if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { /* * If the RESET# pin isn't hooked up properly, or the system * otherwise doesn't perform a reset command in the boot @@ -3699,10 +3698,8 @@ static void spi_nor_resume(struct mtd_info *mtd) void spi_nor_restore(struct spi_nor *nor) { /* restore the addressing mode */ - if ((nor->addr_width == 4) && - (JEDEC_MFR(nor->info) != SNOR_MFR_SPANSION) && - !(nor->info->flags & SPI_NOR_4B_OPCODES) && - (nor->flags & SNOR_F_BROKEN_RESET)) + if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && + nor->flags & SNOR_F_BROKEN_RESET) set_4byte(nor, nor->info, 0); } EXPORT_SYMBOL_GPL(spi_nor_restore); @@ -3858,13 +3855,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } else if (mtd->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ nor->addr_width = 4; - if (JEDEC_MFR(info) == SNOR_MFR_SPANSION || - info->flags & SPI_NOR_4B_OPCODES) - spi_nor_set_4byte_opcodes(nor, info); } else { nor->addr_width = 3; } + if (info->flags & SPI_NOR_4B_OPCODES || + (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M)) + nor->flags |= SNOR_F_4B_OPCODES; + + if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES) + spi_nor_set_4byte_opcodes(nor, info); + if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { dev_err(dev, "address width is too large: %u\n", nor->addr_width); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 8b1acf68b7ac..981d628305a2 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -237,6 +237,7 @@ enum spi_nor_option_flags { SNOR_F_READY_XSR_RDY = BIT(4), SNOR_F_USE_CLSR = BIT(5), SNOR_F_BROKEN_RESET = BIT(6), + SNOR_F_4B_OPCODES = BIT(7), }; /** -- cgit v1.2.3 From e9f3a2bcc3742960e28c8d37165406c6c55500b9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 6 Dec 2018 11:41:20 +0100 Subject: mtd: spi-nor: Add an SPDX tag to spi-nor.{c,h} Add SPDX tags to replace the license boiler-plate and fix the MODULE_LICENSE() definition in spi-nor.c to match the license text (GPL v2). Interestingly, spi-nor.h and spi-nor.c do not use the same license (GPL v2+ for spi-nor.h, GPL v2 for spi-nor.c). Signed-off-by: Boris Brezillon Reviewed-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 7 ++----- include/linux/mtd/spi-nor.h | 6 +----- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index f6beb0ee15b0..8c8c4fe2be22 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Based on m25p80.c, by Mike Lavender (mike@steroidmicros.com), with * influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c * * Copyright (C) 2005, Intec Automation Inc. * Copyright (C) 2014, Freescale Semiconductor, Inc. - * - * This code is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include @@ -3970,7 +3967,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } EXPORT_SYMBOL_GPL(spi_nor_scan); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Huang Shijie "); MODULE_AUTHOR("Mike Lavender"); MODULE_DESCRIPTION("framework for SPI NOR"); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 981d628305a2..5f177aa39f68 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -1,10 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2014 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __LINUX_MTD_SPI_NOR_H -- cgit v1.2.3 From 816873eaeec63ba2e58bbd514d15a7efc6e572f7 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Thu, 6 Dec 2018 14:43:39 +0000 Subject: mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Add support for SFDP (JESD216B) 4-byte Address Instruction Table. This table is optional but when available, we parse it to get the 4-byte address op codes supported by the memory. Using these op codes is stateless as opposed to entering the 4-byte address mode or setting the Base Address Register (BAR). Flashes that have the 4BAIT table declared can now support SPINOR_OP_PP_1_1_4_4B and SPINOR_OP_PP_1_4_4_4B opcodes. Tested on MX25L25673G. Signed-off-by: Cyrille Pitchen [tudor.ambarus@microchip.com: - rework erase and page program logic, - pass DMA-able buffer to spi_nor_read_sfdp(), - introduce SPI_NOR_HAS_4BAIT - various minor updates.] Signed-off-by: Tudor Ambarus Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 193 +++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/spi-nor.h | 1 + 2 files changed, 193 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 4896b9aaa6fa..69ed5f2b2c8c 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -121,6 +121,7 @@ struct sfdp_parameter_header { #define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ #define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ +#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ #define SFDP_SIGNATURE 0x50444653U #define SFDP_JESD216_MAJOR 1 @@ -3239,6 +3240,191 @@ out: return ret; } +#define SFDP_4BAIT_DWORD_MAX 2 + +struct sfdp_4bait { + /* The hardware capability. */ + u32 hwcaps; + + /* + * The bit in DWORD1 of the 4BAIT tells us whether + * the associated 4-byte address op code is supported. + */ + u32 supported_bit; +}; + +/** + * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table + * @nor: pointer to a 'struct spi_nor'. + * @param_header: pointer to the 'struct sfdp_parameter_header' describing + * the 4-Byte Address Instruction Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_4bait(struct spi_nor *nor, + const struct sfdp_parameter_header *param_header, + struct spi_nor_flash_parameter *params) +{ + static const struct sfdp_4bait reads[] = { + { SNOR_HWCAPS_READ, BIT(0) }, + { SNOR_HWCAPS_READ_FAST, BIT(1) }, + { SNOR_HWCAPS_READ_1_1_2, BIT(2) }, + { SNOR_HWCAPS_READ_1_2_2, BIT(3) }, + { SNOR_HWCAPS_READ_1_1_4, BIT(4) }, + { SNOR_HWCAPS_READ_1_4_4, BIT(5) }, + { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, + { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, + { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, + }; + static const struct sfdp_4bait programs[] = { + { SNOR_HWCAPS_PP, BIT(6) }, + { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, + { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, + }; + static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { + { 0u /* not used */, BIT(9) }, + { 0u /* not used */, BIT(10) }, + { 0u /* not used */, BIT(11) }, + { 0u /* not used */, BIT(12) }, + }; + struct spi_nor_pp_command *params_pp = params->page_programs; + struct spi_nor_erase_map *map = &nor->erase_map; + struct spi_nor_erase_type *erase_type = map->erase_type; + u32 *dwords; + size_t len; + u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask; + int i, ret; + + if (param_header->major != SFDP_JESD216_MAJOR || + param_header->length < SFDP_4BAIT_DWORD_MAX) + return -EINVAL; + + /* Read the 4-byte Address Instruction Table. */ + len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX; + + /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(param_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + return ret; + + /* Fix endianness of the 4BAIT DWORDs. */ + for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++) + dwords[i] = le32_to_cpu(dwords[i]); + + /* + * Compute the subset of (Fast) Read commands for which the 4-byte + * version is supported. + */ + discard_hwcaps = 0; + read_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(reads); i++) { + const struct sfdp_4bait *read = &reads[i]; + + discard_hwcaps |= read->hwcaps; + if ((params->hwcaps.mask & read->hwcaps) && + (dwords[0] & read->supported_bit)) + read_hwcaps |= read->hwcaps; + } + + /* + * Compute the subset of Page Program commands for which the 4-byte + * version is supported. + */ + pp_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(programs); i++) { + const struct sfdp_4bait *program = &programs[i]; + + /* + * The 4 Byte Address Instruction (Optional) Table is the only + * SFDP table that indicates support for Page Program Commands. + * Bypass the params->hwcaps.mask and consider 4BAIT the biggest + * authority for specifying Page Program support. + */ + discard_hwcaps |= program->hwcaps; + if (dwords[0] & program->supported_bit) + pp_hwcaps |= program->hwcaps; + } + + /* + * Compute the subset of Sector Erase commands for which the 4-byte + * version is supported. + */ + erase_mask = 0; + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + const struct sfdp_4bait *erase = &erases[i]; + + if (dwords[0] & erase->supported_bit) + erase_mask |= BIT(i); + } + + /* Replicate the sort done for the map's erase types in BFPT. */ + erase_mask = spi_nor_sort_erase_mask(map, erase_mask); + + /* + * We need at least one 4-byte op code per read, program and erase + * operation; the .read(), .write() and .erase() hooks share the + * nor->addr_width value. + */ + if (!read_hwcaps || !pp_hwcaps || !erase_mask) + goto out; + + /* + * Discard all operations from the 4-byte instruction set which are + * not supported by this memory. + */ + params->hwcaps.mask &= ~discard_hwcaps; + params->hwcaps.mask |= (read_hwcaps | pp_hwcaps); + + /* Use the 4-byte address instruction set. */ + for (i = 0; i < SNOR_CMD_READ_MAX; i++) { + struct spi_nor_read_command *read_cmd = ¶ms->reads[i]; + + read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode); + } + + /* 4BAIT is the only SFDP table that indicates page program support. */ + if (pp_hwcaps & SNOR_HWCAPS_PP) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], + SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4_4B, + SNOR_PROTO_1_1_4); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4_4B, + SNOR_PROTO_1_4_4); + + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + if (erase_mask & BIT(i)) + erase_type[i].opcode = (dwords[1] >> + erase_type[i].idx * 8) & 0xFF; + else + spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); + } + + /* + * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes() + * later because we already did the conversion to 4byte opcodes. Also, + * this latest function implements a legacy quirk for the erase size of + * Spansion memory. However this quirk is no longer needed with new + * SFDP compliant memories. + */ + nor->addr_width = 4; + nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; + + /* fall through */ +out: + kfree(dwords); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -3336,6 +3522,10 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_smpt(nor, param_header); break; + case SFDP_4BAIT_ID: + err = spi_nor_parse_4bait(nor, param_header, params); + break; + default: break; } @@ -3925,7 +4115,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, (JEDEC_MFR(info) == SNOR_MFR_SPANSION && mtd->size > SZ_16M)) nor->flags |= SNOR_F_4B_OPCODES; - if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES) + if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + !(nor->flags & SNOR_F_HAS_4BAIT)) spi_nor_set_4byte_opcodes(nor); if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 5f177aa39f68..fa2d89e38e40 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -234,6 +234,7 @@ enum spi_nor_option_flags { SNOR_F_USE_CLSR = BIT(5), SNOR_F_BROKEN_RESET = BIT(6), SNOR_F_4B_OPCODES = BIT(7), + SNOR_F_HAS_4BAIT = BIT(8), }; /** -- cgit v1.2.3