From 596cf083da34c2007f8ec760c8b077f6f28ee655 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:29:59 +0200 Subject: mtd: Fallback to ->_read/write_oob() when ->_read/write() is missing Some MTD sublayers/drivers are implementing ->_read/write_oob() and provide dummy wrappers for their ->_read/write() implementations. Let the core handle this case instead of duplicating the logic. Signed-off-by: Boris Brezillon Acked-by: Robert Jarzmik Acked-by: Brian Norris Reviewed-by: Miquel Raynal Tested-by: Ladislav Michl Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- drivers/mtd/mtdcore.c | 31 +++++++++++++++++++-- drivers/mtd/mtdpart.c | 6 ++-- drivers/mtd/nand/nand_base.c | 56 -------------------------------------- drivers/mtd/onenand/onenand_base.c | 2 -- 4 files changed, 33 insertions(+), 62 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6217be2352..60ad28efd4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -937,7 +937,20 @@ int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, * representing the maximum number of bitflips that were corrected on * any one ecc region (if applicable; zero otherwise). */ - ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (mtd->_read) { + ret_code = mtd->_read(mtd, from, len, retlen, buf); + } else if (mtd->_read_oob) { + struct mtd_oob_ops ops = { + .len = len, + .datbuf = buf, + }; + + ret_code = mtd->_read_oob(mtd, from, &ops); + *retlen = ops.retlen; + } else { + return -ENOTSUPP; + } + if (unlikely(ret_code < 0)) return ret_code; if (mtd->ecc_strength == 0) @@ -952,10 +965,24 @@ int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, *retlen = 0; if (to < 0 || to > mtd->size || len > mtd->size - to) return -EINVAL; - if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) + if ((!mtd->_write && !mtd->_write_oob) || + !(mtd->flags & MTD_WRITEABLE)) return -EROFS; if (!len) return 0; + + if (!mtd->_write) { + struct mtd_oob_ops ops = { + .len = len, + .datbuf = (u8 *)buf, + }; + int ret; + + ret = mtd->_write_oob(mtd, to, &ops); + *retlen = ops.retlen; + return ret; + } + return mtd->_write(mtd, to, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_write); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f87c962205..ccbb1757ea 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -417,8 +417,10 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.dev.parent = master->dev.parent; #endif - slave->mtd._read = part_read; - slave->mtd._write = part_write; + if (master->_read) + slave->mtd._read = part_read; + if (master->_write) + slave->mtd._write = part_write; if (master->_panic_write) slave->mtd._panic_write = part_panic_write; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9094f857c1..92daebe120 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1863,33 +1863,6 @@ read_retry: return max_bitflips; } -/** - * nand_read - [MTD Interface] MTD compatibility function for nand_do_read_ecc - * @mtd: MTD device structure - * @from: offset to read from - * @len: number of bytes to read - * @retlen: pointer to variable to store the number of read bytes - * @buf: the databuffer to put data - * - * Get hold of the chip and call nand_do_read. - */ -static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, uint8_t *buf) -{ - struct mtd_oob_ops ops; - int ret; - - nand_get_device(mtd, FL_READING); - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = buf; - ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_read_ops(mtd, from, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; -} - /** * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function * @mtd: mtd info structure @@ -2674,33 +2647,6 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } -/** - * nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * NAND write with ECC. - */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) -{ - struct mtd_oob_ops ops; - int ret; - - nand_get_device(mtd, FL_WRITING); - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = (uint8_t *)buf; - ops.mode = MTD_OPS_PLACE_OOB; - ret = nand_do_write_ops(mtd, to, &ops); - *retlen = ops.retlen; - nand_release_device(mtd); - return ret; -} - /** * nand_do_write_oob - [MTD Interface] NAND write out-of-band * @mtd: MTD device structure @@ -4620,8 +4566,6 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; mtd->_erase = nand_erase; - mtd->_read = nand_read; - mtd->_write = nand_write; mtd->_panic_write = panic_nand_write; mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 86b1640357..371e2ecaa7 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2656,8 +2656,6 @@ int onenand_probe(struct mtd_info *mtd) mtd->flags = MTD_CAP_NANDFLASH; mtd->_erase = onenand_erase; - mtd->_read = onenand_read; - mtd->_write = onenand_write; mtd->_read_oob = onenand_read_oob; mtd->_write_oob = onenand_write_oob; mtd->_sync = onenand_sync; -- cgit v1.2.3 From 5f50d82d8918b711717b4bbd96c6f348eb6e2a2c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 16 Aug 2018 17:30:00 +0200 Subject: mtd: Uninline mtd_write_oob and move it to mtdcore.c There's no reason for having mtd_write_oob inlined in mtd.h header. Move it to mtdcore.c where it belongs. Signed-off-by: Ezequiel Garcia Acked-by: Boris Brezillon Signed-off-by: Jacek Anaszewski Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 12 ++++++++++++ include/linux/mtd/mtd.h | 12 +----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 60ad28efd4..ad61406dac 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1031,6 +1031,18 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) } EXPORT_SYMBOL_GPL(mtd_read_oob); +int mtd_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + ops->retlen = ops->oobretlen = 0; + if (!mtd->_write_oob) + return -EOPNOTSUPP; + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + return mtd->_write_oob(mtd, to, ops); +} +EXPORT_SYMBOL_GPL(mtd_write_oob); + /** * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section * @mtd: MTD device structure diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 823e535b82..eb39f38887 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -351,17 +351,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); - -static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - return mtd->_write_oob(mtd, to, ops); -} +int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf); -- cgit v1.2.3 From 8fad769f1eab88a2bcf0b714694158968f58715a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:01 +0200 Subject: mtd: Add sanity checks in mtd_write/read_oob() Unlike what's done in mtd_read/write(), there are no checks to make sure the parameters passed to mtd_read/write_oob() are consistent, which forces implementers of ->_read/write_oob() to do it, which in turn leads to code duplication and possibly errors in the logic. Do general sanity checks, like ops fields consistency and range checking. Signed-off-by: Boris Brezillon Cc: Peter Pan Signed-off-by: Richard Weinberger [Miquel: squashed the fix about the chip's size check] Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ad61406dac..9a3efe95df 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1010,12 +1010,50 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, } EXPORT_SYMBOL_GPL(mtd_panic_write); +static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, + struct mtd_oob_ops *ops) +{ + /* + * Some users are setting ->datbuf or ->oobbuf to NULL, but are leaving + * ->len or ->ooblen uninitialized. Force ->len and ->ooblen to 0 in + * this case. + */ + if (!ops->datbuf) + ops->len = 0; + + if (!ops->oobbuf) + ops->ooblen = 0; + + if (offs < 0 || offs + ops->len > mtd->size) + return -EINVAL; + + if (ops->ooblen) { + u64 maxooblen; + + if (ops->ooboffs >= mtd_oobavail(mtd, ops)) + return -EINVAL; + + maxooblen = ((mtd_div_by_ws(mtd->size, mtd) - + mtd_div_by_ws(offs, mtd)) * + mtd_oobavail(mtd, ops)) - ops->ooboffs; + if (ops->ooblen > maxooblen) + return -EINVAL; + } + + return 0; +} + int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int ret_code; ops->retlen = ops->oobretlen = 0; if (!mtd->_read_oob) return -EOPNOTSUPP; + + ret_code = mtd_check_oob_ops(mtd, from, ops); + if (ret_code) + return ret_code; + /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics * similar to mtd->_read(), returning a non-negative integer @@ -1034,11 +1072,18 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + int ret; + ops->retlen = ops->oobretlen = 0; if (!mtd->_write_oob) return -EOPNOTSUPP; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; + + ret = mtd_check_oob_ops(mtd, to, ops); + if (ret) + return ret; + return mtd->_write_oob(mtd, to, ops); } EXPORT_SYMBOL_GPL(mtd_write_oob); -- cgit v1.2.3 From ca040d8512f4d93a7eb83f7b8fec8f4b8b1d3192 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:02 +0200 Subject: mtd: Fallback to ->_read/write() when ->_read/write_oob() is missing Some MTD sublayers/drivers are implementing ->_read/write() and not ->_read/write_oob(). While for NAND devices both are usually valid, for NOR devices, using the _oob variant has no real meaning. But, as the MTD layer is supposed to hide as much as possible the flash complexity to the user, there is no reason to error out while it is just a matter of rewritting things internally. Add a fallback on mtd->_read() (resp. mtd->_write()) when the user calls mtd_read_oob() (resp. mtd_write_oob()) while mtd->_read_oob() (resp. mtd->_write_oob) is not implemented. There is already a fallback on the _oob variant if the former is used. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 9a3efe95df..fb1d68d5e2 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1047,20 +1047,27 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { int ret_code; ops->retlen = ops->oobretlen = 0; - if (!mtd->_read_oob) - return -EOPNOTSUPP; ret_code = mtd_check_oob_ops(mtd, from, ops); if (ret_code) return ret_code; + /* Check the validity of a potential fallback on mtd->_read */ + if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf)) + return -EOPNOTSUPP; + + if (mtd->_read_oob) + ret_code = mtd->_read_oob(mtd, from, ops); + else + ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen, + ops->datbuf); + /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics * similar to mtd->_read(), returning a non-negative integer * representing max bitflips. In other cases, mtd->_read_oob() may * return -EUCLEAN. In all cases, perform similar logic to mtd_read(). */ - ret_code = mtd->_read_oob(mtd, from, ops); if (unlikely(ret_code < 0)) return ret_code; if (mtd->ecc_strength == 0) @@ -1075,8 +1082,7 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, int ret; ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; + if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; @@ -1084,7 +1090,15 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, if (ret) return ret; - return mtd->_write_oob(mtd, to, ops); + /* Check the validity of a potential fallback on mtd->_write */ + if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf)) + return -EOPNOTSUPP; + + if (mtd->_write_oob) + return mtd->_write_oob(mtd, to, ops); + else + return mtd->_write(mtd, to, ops->len, &ops->retlen, + ops->datbuf); } EXPORT_SYMBOL_GPL(mtd_write_oob); -- cgit v1.2.3 From 9bfc3fde9c64320f7165560a64888fad09bc0fee Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 16 Aug 2018 17:30:03 +0200 Subject: mtd: add get/set of_node/flash_node helpers We are going to begin using the mtd->dev.of_node field for MTD device nodes, so let's add helpers for it. Also, we'll be making some conversions on spi_nor (and nand_chip eventually) too, so get that ready with their own helpers. Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/linux/mtd/mtd.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index eb39f38887..272c646f9d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -21,6 +21,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_DM) +#include +#endif #define MAX_MTD_DEVICES 32 #endif @@ -306,6 +309,31 @@ struct mtd_info { int usecount; }; +#if IS_ENABLED(CONFIG_DM) +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ + mtd->dev->node.np = np; +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return mtd->dev->node.np; +} +#else +struct device_node; + +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return NULL; +} +#endif + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, -- cgit v1.2.3 From d02f1d36ec6fe6bfadd77fa71b1df228010ddaa8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:05 +0200 Subject: mtd: move definitions to enlarge their range Some helpers might be useful in a future 'mtd' U-Boot command to parse MTD device list. Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.h | 6 ------ include/linux/mtd/mtd.h | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index 7b0353399a..1d181a1045 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -5,7 +5,6 @@ extern struct mutex mtd_table_mutex; -struct mtd_info *__mtd_next_device(int i); int add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); @@ -16,8 +15,3 @@ int parse_mtd_partitions(struct mtd_info *master, const char * const *types, int __init init_mtdchar(void); void __exit cleanup_mtdchar(void); - -#define mtd_for_each_device(mtd) \ - for ((mtd) = __mtd_next_device(0); \ - (mtd) != NULL; \ - (mtd) = __mtd_next_device(mtd->index + 1)) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 272c646f9d..b8c2c3fd59 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -533,6 +533,12 @@ int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +struct mtd_info *__mtd_next_device(int i); +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) + int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, loff_t *maxsize, int devtype, uint64_t chipsize); int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, -- cgit v1.2.3 From ce9bdc87436ef91129876c9b16fcf5111eea69aa Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:06 +0200 Subject: mtd: move all flash categories inside MTD submenu There is no reason to have NAND, SPI flashes and UBI sections outside of the MTD submenu in Kconfig. Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- drivers/mtd/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 41f8883ec2..9341d518f3 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -59,10 +59,10 @@ config RENESAS_RPC_HF This enables access to Hyperflash memory through the Renesas RCar Gen3 RPC controller. -endmenu - source "drivers/mtd/nand/Kconfig" source "drivers/mtd/spi/Kconfig" source "drivers/mtd/ubi/Kconfig" + +endmenu -- cgit v1.2.3 From a430fa06a4ac50e785fdbfb7f43c3cb14b35619c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:07 +0200 Subject: mtd: move NAND files into a raw/ subdirectory NAND flavors, like serial and parallel, have a lot in common and would benefit to share code. Let's move raw (parallel) NAND specific code in a raw/ subdirectory, to ease the addition of a core file in nand/ and the introduction of a spi/ subdirectory specific to SPI NANDs. Signed-off-by: Miquel Raynal --- MAINTAINERS | 6 +- Makefile | 2 +- README | 6 +- arch/arm/mach-uniphier/board_late_init.c | 2 +- common/spl/Kconfig | 2 +- common/spl/spl_spi.c | 2 +- doc/README.SPL | 4 +- doc/README.arm-relocation | 2 +- doc/README.nand | 6 +- doc/README.zynq | 2 +- drivers/Makefile | 2 +- drivers/mtd/Makefile | 2 + drivers/mtd/nand/Kconfig | 298 +- drivers/mtd/nand/Makefile | 75 - drivers/mtd/nand/am335x_spl_bch.c | 225 -- drivers/mtd/nand/arasan_nfc.c | 1270 -------- drivers/mtd/nand/atmel_nand.c | 1511 ---------- drivers/mtd/nand/atmel_nand_ecc.h | 203 -- drivers/mtd/nand/davinci_nand.c | 833 ------ drivers/mtd/nand/denali.c | 1371 --------- drivers/mtd/nand/denali.h | 325 --- drivers/mtd/nand/denali_dt.c | 122 - drivers/mtd/nand/denali_spl.c | 228 -- drivers/mtd/nand/fsl_elbc_nand.c | 810 ------ drivers/mtd/nand/fsl_elbc_spl.c | 167 -- drivers/mtd/nand/fsl_ifc_nand.c | 1064 ------- drivers/mtd/nand/fsl_ifc_spl.c | 306 -- drivers/mtd/nand/fsl_upm.c | 184 -- drivers/mtd/nand/fsmc_nand.c | 518 ---- drivers/mtd/nand/kb9202_nand.c | 133 - drivers/mtd/nand/kirkwood_nand.c | 91 - drivers/mtd/nand/kmeter1_nand.c | 122 - drivers/mtd/nand/lpc32xx_nand_mlc.c | 761 ----- drivers/mtd/nand/lpc32xx_nand_slc.c | 597 ---- drivers/mtd/nand/mxc_nand.c | 1307 --------- drivers/mtd/nand/mxc_nand.h | 208 -- drivers/mtd/nand/mxc_nand_spl.c | 350 --- drivers/mtd/nand/mxs_nand.c | 1302 --------- drivers/mtd/nand/mxs_nand.h | 73 - drivers/mtd/nand/mxs_nand_dt.c | 94 - drivers/mtd/nand/mxs_nand_spl.c | 264 -- drivers/mtd/nand/nand.c | 175 -- drivers/mtd/nand/nand_base.c | 4619 ------------------------------ drivers/mtd/nand/nand_bbt.c | 1373 --------- drivers/mtd/nand/nand_bch.c | 231 -- drivers/mtd/nand/nand_ecc.c | 174 -- drivers/mtd/nand/nand_ids.c | 209 -- drivers/mtd/nand/nand_plat.c | 64 - drivers/mtd/nand/nand_spl_load.c | 41 - drivers/mtd/nand/nand_spl_loaders.c | 104 - drivers/mtd/nand/nand_spl_simple.c | 240 -- drivers/mtd/nand/nand_timings.c | 334 --- drivers/mtd/nand/nand_util.c | 904 ------ drivers/mtd/nand/omap_elm.c | 193 -- drivers/mtd/nand/omap_gpmc.c | 1037 ------- drivers/mtd/nand/pxa3xx_nand.c | 1828 ------------ drivers/mtd/nand/pxa3xx_nand.h | 64 - drivers/mtd/nand/raw/Kconfig | 297 ++ drivers/mtd/nand/raw/Makefile | 77 + drivers/mtd/nand/raw/am335x_spl_bch.c | 225 ++ drivers/mtd/nand/raw/arasan_nfc.c | 1270 ++++++++ drivers/mtd/nand/raw/atmel_nand.c | 1511 ++++++++++ drivers/mtd/nand/raw/atmel_nand_ecc.h | 203 ++ drivers/mtd/nand/raw/davinci_nand.c | 833 ++++++ drivers/mtd/nand/raw/denali.c | 1371 +++++++++ drivers/mtd/nand/raw/denali.h | 325 +++ drivers/mtd/nand/raw/denali_dt.c | 122 + drivers/mtd/nand/raw/denali_spl.c | 228 ++ drivers/mtd/nand/raw/fsl_elbc_nand.c | 810 ++++++ drivers/mtd/nand/raw/fsl_elbc_spl.c | 167 ++ drivers/mtd/nand/raw/fsl_ifc_nand.c | 1064 +++++++ drivers/mtd/nand/raw/fsl_ifc_spl.c | 306 ++ drivers/mtd/nand/raw/fsl_upm.c | 184 ++ drivers/mtd/nand/raw/fsmc_nand.c | 518 ++++ drivers/mtd/nand/raw/kb9202_nand.c | 133 + drivers/mtd/nand/raw/kirkwood_nand.c | 91 + drivers/mtd/nand/raw/kmeter1_nand.c | 122 + drivers/mtd/nand/raw/lpc32xx_nand_mlc.c | 761 +++++ drivers/mtd/nand/raw/lpc32xx_nand_slc.c | 597 ++++ drivers/mtd/nand/raw/mxc_nand.c | 1307 +++++++++ drivers/mtd/nand/raw/mxc_nand.h | 208 ++ drivers/mtd/nand/raw/mxc_nand_spl.c | 350 +++ drivers/mtd/nand/raw/mxs_nand.c | 1302 +++++++++ drivers/mtd/nand/raw/mxs_nand.h | 73 + drivers/mtd/nand/raw/mxs_nand_dt.c | 94 + drivers/mtd/nand/raw/mxs_nand_spl.c | 264 ++ drivers/mtd/nand/raw/nand.c | 175 ++ drivers/mtd/nand/raw/nand_base.c | 4619 ++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/nand_bbt.c | 1373 +++++++++ drivers/mtd/nand/raw/nand_bch.c | 231 ++ drivers/mtd/nand/raw/nand_ecc.c | 174 ++ drivers/mtd/nand/raw/nand_ids.c | 209 ++ drivers/mtd/nand/raw/nand_plat.c | 64 + drivers/mtd/nand/raw/nand_spl_load.c | 41 + drivers/mtd/nand/raw/nand_spl_loaders.c | 104 + drivers/mtd/nand/raw/nand_spl_simple.c | 240 ++ drivers/mtd/nand/raw/nand_timings.c | 334 +++ drivers/mtd/nand/raw/nand_util.c | 904 ++++++ drivers/mtd/nand/raw/omap_elm.c | 193 ++ drivers/mtd/nand/raw/omap_gpmc.c | 1037 +++++++ drivers/mtd/nand/raw/pxa3xx_nand.c | 1828 ++++++++++++ drivers/mtd/nand/raw/pxa3xx_nand.h | 64 + drivers/mtd/nand/raw/sunxi_nand.c | 1850 ++++++++++++ drivers/mtd/nand/raw/sunxi_nand_spl.c | 548 ++++ drivers/mtd/nand/raw/tegra_nand.c | 1002 +++++++ drivers/mtd/nand/raw/tegra_nand.h | 240 ++ drivers/mtd/nand/raw/vf610_nfc.c | 768 +++++ drivers/mtd/nand/raw/zynq_nand.c | 1254 ++++++++ drivers/mtd/nand/sunxi_nand.c | 1850 ------------ drivers/mtd/nand/sunxi_nand_spl.c | 548 ---- drivers/mtd/nand/tegra_nand.c | 1002 ------- drivers/mtd/nand/tegra_nand.h | 240 -- drivers/mtd/nand/vf610_nfc.c | 768 ----- drivers/mtd/nand/zynq_nand.c | 1254 -------- include/configs/MPC8313ERDB.h | 2 +- 115 files changed, 32087 insertions(+), 32082 deletions(-) delete mode 100644 drivers/mtd/nand/am335x_spl_bch.c delete mode 100644 drivers/mtd/nand/arasan_nfc.c delete mode 100644 drivers/mtd/nand/atmel_nand.c delete mode 100644 drivers/mtd/nand/atmel_nand_ecc.h delete mode 100644 drivers/mtd/nand/davinci_nand.c delete mode 100644 drivers/mtd/nand/denali.c delete mode 100644 drivers/mtd/nand/denali.h delete mode 100644 drivers/mtd/nand/denali_dt.c delete mode 100644 drivers/mtd/nand/denali_spl.c delete mode 100644 drivers/mtd/nand/fsl_elbc_nand.c delete mode 100644 drivers/mtd/nand/fsl_elbc_spl.c delete mode 100644 drivers/mtd/nand/fsl_ifc_nand.c delete mode 100644 drivers/mtd/nand/fsl_ifc_spl.c delete mode 100644 drivers/mtd/nand/fsl_upm.c delete mode 100644 drivers/mtd/nand/fsmc_nand.c delete mode 100644 drivers/mtd/nand/kb9202_nand.c delete mode 100644 drivers/mtd/nand/kirkwood_nand.c delete mode 100644 drivers/mtd/nand/kmeter1_nand.c delete mode 100644 drivers/mtd/nand/lpc32xx_nand_mlc.c delete mode 100644 drivers/mtd/nand/lpc32xx_nand_slc.c delete mode 100644 drivers/mtd/nand/mxc_nand.c delete mode 100644 drivers/mtd/nand/mxc_nand.h delete mode 100644 drivers/mtd/nand/mxc_nand_spl.c delete mode 100644 drivers/mtd/nand/mxs_nand.c delete mode 100644 drivers/mtd/nand/mxs_nand.h delete mode 100644 drivers/mtd/nand/mxs_nand_dt.c delete mode 100644 drivers/mtd/nand/mxs_nand_spl.c delete mode 100644 drivers/mtd/nand/nand.c delete mode 100644 drivers/mtd/nand/nand_base.c delete mode 100644 drivers/mtd/nand/nand_bbt.c delete mode 100644 drivers/mtd/nand/nand_bch.c delete mode 100644 drivers/mtd/nand/nand_ecc.c delete mode 100644 drivers/mtd/nand/nand_ids.c delete mode 100644 drivers/mtd/nand/nand_plat.c delete mode 100644 drivers/mtd/nand/nand_spl_load.c delete mode 100644 drivers/mtd/nand/nand_spl_loaders.c delete mode 100644 drivers/mtd/nand/nand_spl_simple.c delete mode 100644 drivers/mtd/nand/nand_timings.c delete mode 100644 drivers/mtd/nand/nand_util.c delete mode 100644 drivers/mtd/nand/omap_elm.c delete mode 100644 drivers/mtd/nand/omap_gpmc.c delete mode 100644 drivers/mtd/nand/pxa3xx_nand.c delete mode 100644 drivers/mtd/nand/pxa3xx_nand.h create mode 100644 drivers/mtd/nand/raw/Kconfig create mode 100644 drivers/mtd/nand/raw/Makefile create mode 100644 drivers/mtd/nand/raw/am335x_spl_bch.c create mode 100644 drivers/mtd/nand/raw/arasan_nfc.c create mode 100644 drivers/mtd/nand/raw/atmel_nand.c create mode 100644 drivers/mtd/nand/raw/atmel_nand_ecc.h create mode 100644 drivers/mtd/nand/raw/davinci_nand.c create mode 100644 drivers/mtd/nand/raw/denali.c create mode 100644 drivers/mtd/nand/raw/denali.h create mode 100644 drivers/mtd/nand/raw/denali_dt.c create mode 100644 drivers/mtd/nand/raw/denali_spl.c create mode 100644 drivers/mtd/nand/raw/fsl_elbc_nand.c create mode 100644 drivers/mtd/nand/raw/fsl_elbc_spl.c create mode 100644 drivers/mtd/nand/raw/fsl_ifc_nand.c create mode 100644 drivers/mtd/nand/raw/fsl_ifc_spl.c create mode 100644 drivers/mtd/nand/raw/fsl_upm.c create mode 100644 drivers/mtd/nand/raw/fsmc_nand.c create mode 100644 drivers/mtd/nand/raw/kb9202_nand.c create mode 100644 drivers/mtd/nand/raw/kirkwood_nand.c create mode 100644 drivers/mtd/nand/raw/kmeter1_nand.c create mode 100644 drivers/mtd/nand/raw/lpc32xx_nand_mlc.c create mode 100644 drivers/mtd/nand/raw/lpc32xx_nand_slc.c create mode 100644 drivers/mtd/nand/raw/mxc_nand.c create mode 100644 drivers/mtd/nand/raw/mxc_nand.h create mode 100644 drivers/mtd/nand/raw/mxc_nand_spl.c create mode 100644 drivers/mtd/nand/raw/mxs_nand.c create mode 100644 drivers/mtd/nand/raw/mxs_nand.h create mode 100644 drivers/mtd/nand/raw/mxs_nand_dt.c create mode 100644 drivers/mtd/nand/raw/mxs_nand_spl.c create mode 100644 drivers/mtd/nand/raw/nand.c create mode 100644 drivers/mtd/nand/raw/nand_base.c create mode 100644 drivers/mtd/nand/raw/nand_bbt.c create mode 100644 drivers/mtd/nand/raw/nand_bch.c create mode 100644 drivers/mtd/nand/raw/nand_ecc.c create mode 100644 drivers/mtd/nand/raw/nand_ids.c create mode 100644 drivers/mtd/nand/raw/nand_plat.c create mode 100644 drivers/mtd/nand/raw/nand_spl_load.c create mode 100644 drivers/mtd/nand/raw/nand_spl_loaders.c create mode 100644 drivers/mtd/nand/raw/nand_spl_simple.c create mode 100644 drivers/mtd/nand/raw/nand_timings.c create mode 100644 drivers/mtd/nand/raw/nand_util.c create mode 100644 drivers/mtd/nand/raw/omap_elm.c create mode 100644 drivers/mtd/nand/raw/omap_gpmc.c create mode 100644 drivers/mtd/nand/raw/pxa3xx_nand.c create mode 100644 drivers/mtd/nand/raw/pxa3xx_nand.h create mode 100644 drivers/mtd/nand/raw/sunxi_nand.c create mode 100644 drivers/mtd/nand/raw/sunxi_nand_spl.c create mode 100644 drivers/mtd/nand/raw/tegra_nand.c create mode 100644 drivers/mtd/nand/raw/tegra_nand.h create mode 100644 drivers/mtd/nand/raw/vf610_nfc.c create mode 100644 drivers/mtd/nand/raw/zynq_nand.c delete mode 100644 drivers/mtd/nand/sunxi_nand.c delete mode 100644 drivers/mtd/nand/sunxi_nand_spl.c delete mode 100644 drivers/mtd/nand/tegra_nand.c delete mode 100644 drivers/mtd/nand/tegra_nand.h delete mode 100644 drivers/mtd/nand/vf610_nfc.c delete mode 100644 drivers/mtd/nand/zynq_nand.c diff --git a/MAINTAINERS b/MAINTAINERS index 64fb41ece7..20a0a5e12b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -298,7 +298,7 @@ F: drivers/i2c/i2c-cdns.c F: drivers/i2c/muxes/pca954x.c F: drivers/i2c/zynq_i2c.c F: drivers/mmc/zynq_sdhci.c -F: drivers/mtd/nand/zynq_nand.c +F: drivers/mtd/nand/raw/zynq_nand.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c F: drivers/serial/serial_zynq.c @@ -322,7 +322,7 @@ F: drivers/i2c/i2c-cdns.c F: drivers/i2c/muxes/pca954x.c F: drivers/i2c/zynq_i2c.c F: drivers/mmc/zynq_sdhci.c -F: drivers/mtd/nand/zynq_nand.c +F: drivers/mtd/nand/raw/zynq_nand.c F: drivers/net/phy/xilinx_phy.c F: drivers/net/zynq_gem.c F: drivers/serial/serial_zynq.c @@ -467,7 +467,7 @@ NAND FLASH #M: Scott Wood S: Orphaned (Since 2018-07) T: git git://git.denx.de/u-boot-nand-flash.git -F: drivers/mtd/nand/ +F: drivers/mtd/nand/raw/ NDS32 M: Macpaul Lin diff --git a/Makefile b/Makefile index e38966edba..807e803f99 100644 --- a/Makefile +++ b/Makefile @@ -689,7 +689,7 @@ libs-y += drivers/dma/ libs-y += drivers/gpio/ libs-y += drivers/i2c/ libs-y += drivers/mtd/ -libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/ +libs-$(CONFIG_CMD_NAND) += drivers/mtd/nand/raw/ libs-y += drivers/mtd/onenand/ libs-$(CONFIG_CMD_UBI) += drivers/mtd/ubi/ libs-y += drivers/mtd/spi/ diff --git a/README b/README index 09822a317d..35305a61a3 100644 --- a/README +++ b/README @@ -3256,8 +3256,8 @@ Low Level (hardware related) configuration options: a 16 bit bus. Not all NAND drivers use this symbol. Example of drivers that use it: - - drivers/mtd/nand/ndfc.c - - drivers/mtd/nand/mxc_nand.c + - drivers/mtd/nand/raw/ndfc.c + - drivers/mtd/nand/raw/mxc_nand.c - CONFIG_SYS_NDFC_EBC0_CFG Sets the EBC0_CFG register for the NDFC. If not defined @@ -3374,7 +3374,7 @@ Low Level (hardware related) configuration options: - CONFIG_SYS_NAND_NO_SUBPAGE_WRITE Option to disable subpage write in NAND driver driver that uses this: - drivers/mtd/nand/davinci_nand.c + drivers/mtd/nand/raw/davinci_nand.c Freescale QE/FMAN Firmware Support: ----------------------------------- diff --git a/arch/arm/mach-uniphier/board_late_init.c b/arch/arm/mach-uniphier/board_late_init.c index 8ffb9a8d3c..1b871c62ce 100644 --- a/arch/arm/mach-uniphier/board_late_init.c +++ b/arch/arm/mach-uniphier/board_late_init.c @@ -12,7 +12,7 @@ #include #include #include -#include <../drivers/mtd/nand/denali.h> +#include <../drivers/mtd/nand/raw/denali.h> #include "init.h" diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 280496fbe0..18dbc238bf 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -487,7 +487,7 @@ config SPL_NAND_SUPPORT help Enable support for NAND (Negative AND) flash in SPL. NAND flash can be used to allow SPL to load U-Boot from supported devices. - This enables the drivers in drivers/mtd/nand as part of an SPL + This enables the drivers in drivers/mtd/nand/raw as part of an SPL build. config SPL_NET_SUPPORT diff --git a/common/spl/spl_spi.c b/common/spl/spl_spi.c index ba60a3a3c5..0016d49739 100644 --- a/common/spl/spl_spi.c +++ b/common/spl/spl_spi.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2011 OMICRON electronics GmbH * - * based on drivers/mtd/nand/nand_spl_load.c + * based on drivers/mtd/nand/raw/nand_spl_load.c * * Copyright (C) 2011 * Heiko Schocher, DENX Software Engineering, hs@denx.de. diff --git a/doc/README.SPL b/doc/README.SPL index 3ba313caa8..fc1ca1ad4f 100644 --- a/doc/README.SPL +++ b/doc/README.SPL @@ -57,11 +57,11 @@ CONFIG_SPL_FAT_SUPPORT (fs/fat/libfat.o) CONFIG_SPL_EXT_SUPPORT CONFIG_SPL_LIBGENERIC_SUPPORT (lib/libgeneric.o) CONFIG_SPL_POWER_SUPPORT (drivers/power/libpower.o) -CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/libnand.o) +CONFIG_SPL_NAND_SUPPORT (drivers/mtd/nand/raw/libnand.o) CONFIG_SPL_DRIVERS_MISC_SUPPORT (drivers/misc) CONFIG_SPL_DMA_SUPPORT (drivers/dma/libdma.o) CONFIG_SPL_POST_MEM_SUPPORT (post/drivers/memory.o) -CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/nand_spl_load.o) +CONFIG_SPL_NAND_LOAD (drivers/mtd/nand/raw/nand_spl_load.o) CONFIG_SPL_SPI_LOAD (drivers/mtd/spi/spi_spl_load.o) CONFIG_SPL_RAM_DEVICE (common/spl/spl.c) CONFIG_SPL_WATCHDOG_SUPPORT (drivers/watchdog/libwatchdog.o) diff --git a/doc/README.arm-relocation b/doc/README.arm-relocation index 645b3746c8..d2a7e8122e 100644 --- a/doc/README.arm-relocation +++ b/doc/README.arm-relocation @@ -84,7 +84,7 @@ Relocation with SPL (example for the tx25 booting from NAND Flash): - cpu copies the first page from NAND to 0xbb000000 (IMX_NFC_BASE) and start with code execution on this address. -- The First page contains u-boot code from drivers/mtd/nand/mxc_nand_spl.c +- The First page contains u-boot code from drivers/mtd/nand/raw/mxc_nand_spl.c which inits the dram, cpu registers, reloacte itself to CONFIG_SPL_TEXT_BASE and loads the "real" u-boot to CONFIG_SYS_NAND_U_BOOT_DST and starts execution @CONFIG_SYS_NAND_U_BOOT_START diff --git a/doc/README.nand b/doc/README.nand index cda11b43ff..ec461b2dc9 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -116,7 +116,7 @@ Configuration Options: The maximum number of NAND chips per device to be supported. CONFIG_SYS_NAND_SELF_INIT - Traditionally, glue code in drivers/mtd/nand/nand.c has driven + Traditionally, glue code in drivers/mtd/nand/raw/nand.c has driven the initialization process -- it provides the mtd and nand structs, calls a board init function for a specific device, calls nand_scan(), and registers with mtd. @@ -125,7 +125,7 @@ Configuration Options: run code between nand_scan_ident() and nand_scan_tail(), or other deviations from the "normal" flow. - If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/nand.c + If a board defines CONFIG_SYS_NAND_SELF_INIT, drivers/mtd/nand/raw/nand.c will make one call to board_nand_init(), with no arguments. That function is responsible for calling a driver init function for each NAND device on the board, that performs all initialization @@ -280,7 +280,7 @@ NOTE: ===== The Disk On Chip driver is currently broken and has been for some time. -There is a driver in drivers/mtd/nand, taken from Linux, that works with +There is a driver in drivers/mtd/nand/raw, taken from Linux, that works with the current NAND system but has not yet been adapted to the u-boot environment. diff --git a/doc/README.zynq b/doc/README.zynq index 5b0672c503..da977b2016 100644 --- a/doc/README.zynq +++ b/doc/README.zynq @@ -63,7 +63,7 @@ bootmode strings at runtime. spi - drivers/spi/zynq_spi.c qspi - drivers/spi/zynq_qspi.c i2c - drivers/i2c/zynq_i2c.c - nand - drivers/mtd/nand/zynq_nand.c + nand - drivers/mtd/nand/raw/zynq_nand.c - Done proper cleanups on board configurations - Added basic FDT support for zynq boards - d-cache support for zynq_gem.c diff --git a/drivers/Makefile b/drivers/Makefile index 23ea609b09..fe8d40dddb 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DRIVERS_MISC_SUPPORT) += misc/ sysreset/ firmware/ obj-$(CONFIG_$(SPL_TPL_)I2C_SUPPORT) += i2c/ obj-$(CONFIG_$(SPL_TPL_)LED) += led/ obj-$(CONFIG_$(SPL_TPL_)MMC_SUPPORT) += mmc/ -obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/ +obj-$(CONFIG_$(SPL_TPL_)NAND_SUPPORT) += mtd/nand/raw/ obj-$(CONFIG_$(SPL_TPL_)PHY) += phy/ obj-$(CONFIG_$(SPL_TPL_)PINCTRL) += pinctrl/ obj-$(CONFIG_$(SPL_TPL_)RAM) += ram/ diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 9cec06510c..cdf515256a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -18,3 +18,5 @@ obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o + +obj-y += nand/ diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1e4ea7bdd4..6d53734718 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,297 +1 @@ - -menuconfig NAND - bool "NAND Device Support" -if NAND - -config SYS_NAND_SELF_INIT - bool - help - This option, if enabled, provides more flexible and linux-like - NAND initialization process. - -config NAND_ATMEL - bool "Support Atmel NAND controller" - imply SYS_NAND_USE_FLASH_BBT - help - Enable this driver for NAND flash platforms using an Atmel NAND - controller. - -config NAND_DAVINCI - bool "Support TI Davinci NAND controller" - help - Enable this driver for NAND flash controllers available in TI Davinci - and Keystone2 platforms - -config NAND_DENALI - bool - select SYS_NAND_SELF_INIT - imply CMD_NAND - -config NAND_DENALI_DT - bool "Support Denali NAND controller as a DT device" - select NAND_DENALI - depends on OF_CONTROL && DM - help - Enable the driver for NAND flash on platforms using a Denali NAND - controller as a DT device. - -config NAND_DENALI_SPARE_AREA_SKIP_BYTES - int "Number of bytes skipped in OOB area" - depends on NAND_DENALI - range 0 63 - help - This option specifies the number of bytes to skip from the beginning - of OOB area before last ECC sector data starts. This is potentially - used to preserve the bad block marker in the OOB area. - -config NAND_LPC32XX_SLC - bool "Support LPC32XX_SLC controller" - help - Enable the LPC32XX SLC NAND controller. - -config NAND_OMAP_GPMC - bool "Support OMAP GPMC NAND controller" - depends on ARCH_OMAP2PLUS - help - Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms. - GPMC controller is used for parallel NAND flash devices, and can - do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8 - and BCH16 ECC algorithms. - -config NAND_OMAP_GPMC_PREFETCH - bool "Enable GPMC Prefetch" - depends on NAND_OMAP_GPMC - default y - help - On OMAP platforms that use the GPMC controller - (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that - uses the prefetch mode to speed up read operations. - -config NAND_OMAP_ELM - bool "Enable ELM driver for OMAPxx and AMxx platforms." - depends on NAND_OMAP_GPMC && !OMAP34XX - help - ELM controller is used for ECC error detection (not ECC calculation) - of BCH4, BCH8 and BCH16 ECC algorithms. - Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, - thus such SoC platforms need to depend on software library for ECC error - detection. However ECC calculation on such plaforms would still be - done by GPMC controller. - -config NAND_VF610_NFC - bool "Support for Freescale NFC for VF610" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - Enables support for NAND Flash Controller on some Freescale - processors like the VF610, MCF54418 or Kinetis K70. - The driver supports a maximum 2k page size. The driver - currently does not support hardware ECC. - -choice - prompt "Hardware ECC strength" - depends on NAND_VF610_NFC - default SYS_NAND_VF610_NFC_45_ECC_BYTES - help - Select the ECC strength used in the hardware BCH ECC block. - -config SYS_NAND_VF610_NFC_45_ECC_BYTES - bool "24-error correction (45 ECC bytes)" - -config SYS_NAND_VF610_NFC_60_ECC_BYTES - bool "32-error correction (60 ECC bytes)" - -endchoice - -config NAND_PXA3XX - bool "Support for NAND on PXA3xx and Armada 370/XP/38x" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables the driver for the NAND flash device found on - PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). - -config NAND_SUNXI - bool "Support for NAND on Allwinner SoCs" - default ARCH_SUNXI - depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I - select SYS_NAND_SELF_INIT - select SYS_NAND_U_BOOT_LOCATIONS - select SPL_NAND_SUPPORT - imply CMD_NAND - ---help--- - Enable support for NAND. This option enables the standard and - SPL drivers. - The SPL driver only supports reading from the NAND using DMA - transfers. - -if NAND_SUNXI - -config NAND_SUNXI_SPL_ECC_STRENGTH - int "Allwinner NAND SPL ECC Strength" - default 64 - -config NAND_SUNXI_SPL_ECC_SIZE - int "Allwinner NAND SPL ECC Step Size" - default 1024 - -config NAND_SUNXI_SPL_USABLE_PAGE_SIZE - int "Allwinner NAND SPL Usable Page Size" - default 1024 - -endif - -config NAND_ARASAN - bool "Configure Arasan Nand" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables Nand driver support for Arasan nand flash - controller. This uses the hardware ECC for read and - write operations. - -config NAND_MXC - bool "MXC NAND support" - depends on CPU_ARM926EJS || CPU_ARM1136 || MX5 - imply CMD_NAND - help - This enables the NAND driver for the NAND flash controller on the - i.MX27 / i.MX31 / i.MX5 rocessors. - -config NAND_MXS - bool "MXS NAND support" - depends on MX23 || MX28 || MX6 || MX7 - select SYS_NAND_SELF_INIT - imply CMD_NAND - select APBH_DMA - select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 - select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 - help - This enables NAND driver for the NAND flash controller on the - MXS processors. - -if NAND_MXS - -config NAND_MXS_DT - bool "Support MXS NAND controller as a DT device" - depends on OF_CONTROL && MTD - help - Enable the driver for MXS NAND flash on platforms using - device tree. - -config NAND_MXS_USE_MINIMUM_ECC - bool "Use minimum ECC strength supported by the controller" - default false - -endif - -config NAND_ZYNQ - bool "Support for Zynq Nand controller" - select SYS_NAND_SELF_INIT - imply CMD_NAND - help - This enables Nand driver support for Nand flash controller - found on Zynq SoC. - -config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS - bool "Enable use of 1st stage bootloader timing for NAND" - depends on NAND_ZYNQ - help - This flag prevent U-boot reconfigure NAND flash controller and reuse - the NAND timing from 1st stage bootloader. - -comment "Generic NAND options" - -config SYS_NAND_BLOCK_SIZE - hex "NAND chip eraseblock size" - depends on ARCH_SUNXI - help - Number of data bytes in one eraseblock for the NAND chip on the - board. This is the multiple of NAND_PAGE_SIZE and the number of - pages. - -config SYS_NAND_PAGE_SIZE - hex "NAND chip page size" - depends on ARCH_SUNXI - help - Number of data bytes in one page for the NAND chip on the - board, not including the OOB area. - -config SYS_NAND_OOBSIZE - hex "NAND chip OOB size" - depends on ARCH_SUNXI - help - Number of bytes in the Out-Of-Band area for the NAND chip on - the board. - -# Enhance depends when converting drivers to Kconfig which use this config -# option (mxc_nand, ndfc, omap_gpmc). -config SYS_NAND_BUSWIDTH_16BIT - bool "Use 16-bit NAND interface" - depends on NAND_VF610_NFC || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI - help - Indicates that NAND device has 16-bit wide data-bus. In absence of this - config, bus-width of NAND device is assumed to be either 8-bit and later - determined by reading ONFI params. - Above config is useful when NAND device's bus-width information cannot - be determined from on-chip ONFI params, like in following scenarios: - - SPL boot does not support reading of ONFI parameters. This is done to - keep SPL code foot-print small. - - In current U-Boot flow using nand_init(), driver initialization - happens in board_nand_init() which is called before any device probe - (nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are - not available while configuring controller. So a static CONFIG_NAND_xx - is needed to know the device's bus-width in advance. - -if SPL - -config SYS_NAND_U_BOOT_LOCATIONS - bool "Define U-boot binaries locations in NAND" - help - Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. - This option should not be enabled when compiling U-boot for boards - defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/.h - file. - -config SYS_NAND_U_BOOT_OFFS - hex "Location in NAND to read U-Boot from" - default 0x800000 if NAND_SUNXI - depends on SYS_NAND_U_BOOT_LOCATIONS - help - Set the offset from the start of the nand where u-boot should be - loaded from. - -config SYS_NAND_U_BOOT_OFFS_REDUND - hex "Location in NAND to read U-Boot from" - default SYS_NAND_U_BOOT_OFFS - depends on SYS_NAND_U_BOOT_LOCATIONS - help - Set the offset from the start of the nand where the redundant u-boot - should be loaded from. - -config SPL_NAND_AM33XX_BCH - bool "Enables SPL-NAND driver which supports ELM based" - depends on NAND_OMAP_GPMC && !OMAP34XX - default y - help - Hardware ECC correction. This is useful for platforms which have ELM - hardware engine and use NAND boot mode. - Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, - so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling - SPL-NAND driver with software ECC correction support. - -config SPL_NAND_DENALI - bool "Support Denali NAND controller for SPL" - help - This is a small implementation of the Denali NAND controller - for use on SPL. - -config SPL_NAND_SIMPLE - bool "Use simple SPL NAND driver" - depends on !SPL_NAND_AM33XX_BCH - help - Support for NAND boot using simple NAND drivers that - expose the cmd_ctrl() interface. -endif - -endif # if NAND +source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index c61e3f3839..69f40d1563 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,77 +1,2 @@ # SPDX-License-Identifier: GPL-2.0+ -# -# (C) Copyright 2006 -# Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifdef CONFIG_SPL_BUILD - -ifdef CONFIG_SPL_NAND_DRIVERS -NORMAL_DRIVERS=y -endif - -obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o -obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o -obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o -obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o -obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o -obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o -obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o -obj-$(CONFIG_SPL_NAND_INIT) += nand.o -ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) -obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o -endif - -else # not spl - -NORMAL_DRIVERS=y - -obj-y += nand.o -obj-y += nand_bbt.o -obj-y += nand_ids.o -obj-y += nand_util.o -obj-y += nand_ecc.o -obj-y += nand_base.o -obj-y += nand_timings.o - -endif # not spl - -ifdef NORMAL_DRIVERS - -obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o - -obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o -obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o -obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o -obj-$(CONFIG_NAND_DENALI) += denali.o -obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o -obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o -obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o -obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o -obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o -obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o -obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o -obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o -obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o -obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o -obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o -obj-$(CONFIG_NAND_MXC) += mxc_nand.o -obj-$(CONFIG_NAND_MXS) += mxs_nand.o -obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o -obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o -obj-$(CONFIG_NAND_SPEAR) += spr_nand.o -obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o -obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o -obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o -obj-$(CONFIG_NAND_PLAT) += nand_plat.o -obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o -obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o - -else # minimal SPL drivers - -obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o -obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o -obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o -obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o -obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o - -endif # drivers diff --git a/drivers/mtd/nand/am335x_spl_bch.c b/drivers/mtd/nand/am335x_spl_bch.c deleted file mode 100644 index ba2f33a96e..0000000000 --- a/drivers/mtd/nand/am335x_spl_bch.c +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2012 - * Konstantin Kozhevnikov, Cogent Embedded - * - * based on nand_spl_simple code - * - * (C) Copyright 2006-2008 - * Stefan Roese, DENX Software Engineering, sr@denx.de. - */ - -#include -#include -#include -#include - -static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; -static struct mtd_info *mtd; -static struct nand_chip nand_chip; - -#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) - - -/* - * NAND command for large page NAND devices (2k) - */ -static int nand_command(int block, int page, uint32_t offs, - u8 cmd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; - void (*hwctrl)(struct mtd_info *mtd, int cmd, - unsigned int ctrl) = this->cmd_ctrl; - - while (!this->dev_ready(mtd)) - ; - - /* Emulate NAND_CMD_READOOB */ - if (cmd == NAND_CMD_READOOB) { - offs += CONFIG_SYS_NAND_PAGE_SIZE; - cmd = NAND_CMD_READ0; - } - - /* Begin command latch cycle */ - hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - - if (cmd == NAND_CMD_RESET) { - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Apply this short delay always to ensure that we do wait - * tWB in any case on any machine. - */ - ndelay(150); - - while (!this->dev_ready(mtd)) - ; - return 0; - } - - /* Shift the offset from byte addressing to word addressing. */ - if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) - offs >>= 1; - - /* Set ALE and clear CLE to start address cycle */ - /* Column address */ - hwctrl(mtd, offs & 0xff, - NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ - hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ - /* Row address */ - if (cmd != NAND_CMD_RNDOUT) { - hwctrl(mtd, (page_addr & 0xff), - NAND_CTRL_ALE); /* A[19:12] */ - hwctrl(mtd, ((page_addr >> 8) & 0xff), - NAND_CTRL_ALE); /* A[27:20] */ -#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE - /* One more address cycle for devices > 128MiB */ - hwctrl(mtd, (page_addr >> 16) & 0x0f, - NAND_CTRL_ALE); /* A[31:28] */ -#endif - } - - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - - /* - * Program and erase have their own busy handlers status, sequential - * in and status need no delay. - */ - switch (cmd) { - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - return 0; - - case NAND_CMD_RNDOUT: - /* No ready / busy check necessary */ - hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | - NAND_CTRL_CHANGE); - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - return 0; - - case NAND_CMD_READ0: - /* Latch in address */ - hwctrl(mtd, NAND_CMD_READSTART, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - } - - /* - * Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. - */ - ndelay(150); - - while (!this->dev_ready(mtd)) - ; - - return 0; -} - -static int nand_is_bad_block(int block) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, - NAND_CMD_READOOB); - - /* - * Read one byte (or two if it's a 16 bit chip). - */ - if (this->options & NAND_BUSWIDTH_16) { - if (readw(this->IO_ADDR_R) != 0xffff) - return 1; - } else { - if (readb(this->IO_ADDR_R) != 0xff) - return 1; - } - - return 0; -} - -static int nand_read_page(int block, int page, void *dst) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ecc_calc[ECCTOTAL]; - u_char ecc_code[ECCTOTAL]; - u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; - int i; - int eccsize = CONFIG_SYS_NAND_ECCSIZE; - int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = ECCSTEPS; - uint8_t *p = dst; - uint32_t data_pos = 0; - uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; - uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; - - nand_command(block, page, 0, NAND_CMD_READ0); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - this->ecc.hwctl(mtd, NAND_ECC_READ); - nand_command(block, page, data_pos, NAND_CMD_RNDOUT); - - this->read_buf(mtd, p, eccsize); - - nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); - - this->read_buf(mtd, oob, eccbytes); - this->ecc.calculate(mtd, p, &ecc_calc[i]); - - data_pos += eccsize; - oob_pos += eccbytes; - oob += eccbytes; - } - - /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < ECCTOTAL; i++) - ecc_code[i] = oob_data[nand_ecc_pos[i]]; - - eccsteps = ECCSTEPS; - p = dst; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - /* No chance to do something with the possible error message - * from correct_data(). We just hope that all possible errors - * are corrected by this routine. - */ - this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - } - - return 0; -} - -/* nand_init() - initialize data to make nand usable by SPL */ -void nand_init(void) -{ - /* - * Init board specific nand support - */ - mtd = nand_to_mtd(&nand_chip); - nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = - (void __iomem *)CONFIG_SYS_NAND_BASE; - board_nand_init(&nand_chip); - - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, 0); - - /* NAND chip may require reset after power-on */ - nand_command(0, 0, 0, NAND_CMD_RESET); -} - -/* Unselect after operation */ -void nand_deselect(void) -{ - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, -1); -} - -#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/arasan_nfc.c b/drivers/mtd/nand/arasan_nfc.c deleted file mode 100644 index 41db9f8bb9..0000000000 --- a/drivers/mtd/nand/arasan_nfc.c +++ /dev/null @@ -1,1270 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Arasan NAND Flash Controller Driver - * - * Copyright (C) 2014 - 2015 Xilinx, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct arasan_nand_info { - void __iomem *nand_base; - u32 page; - bool on_die_ecc_enabled; -}; - -struct nand_regs { - u32 pkt_reg; - u32 memadr_reg1; - u32 memadr_reg2; - u32 cmd_reg; - u32 pgm_reg; - u32 intsts_enr; - u32 intsig_enr; - u32 intsts_reg; - u32 rdy_busy; - u32 cms_sysadr_reg; - u32 flash_sts_reg; - u32 tmg_reg; - u32 buf_dataport; - u32 ecc_reg; - u32 ecc_errcnt_reg; - u32 ecc_sprcmd_reg; - u32 errcnt_1bitreg; - u32 errcnt_2bitreg; - u32 errcnt_3bitreg; - u32 errcnt_4bitreg; - u32 dma_sysadr0_reg; - u32 dma_bufbdry_reg; - u32 cpu_rls_reg; - u32 errcnt_5bitreg; - u32 errcnt_6bitreg; - u32 errcnt_7bitreg; - u32 errcnt_8bitreg; - u32 data_if_reg; -}; - -#define arasan_nand_base ((struct nand_regs __iomem *)ARASAN_NAND_BASEADDR) - -struct arasan_nand_command_format { - u8 cmd1; - u8 cmd2; - u8 addr_cycles; - u32 pgm; -}; - -#define ONDIE_ECC_FEATURE_ADDR 0x90 -#define ENABLE_ONDIE_ECC 0x08 - -#define ARASAN_PROG_RD_MASK 0x00000001 -#define ARASAN_PROG_BLK_ERS_MASK 0x00000004 -#define ARASAN_PROG_RD_ID_MASK 0x00000040 -#define ARASAN_PROG_RD_STS_MASK 0x00000008 -#define ARASAN_PROG_PG_PROG_MASK 0x00000010 -#define ARASAN_PROG_RD_PARAM_PG_MASK 0x00000080 -#define ARASAN_PROG_RST_MASK 0x00000100 -#define ARASAN_PROG_GET_FTRS_MASK 0x00000200 -#define ARASAN_PROG_SET_FTRS_MASK 0x00000400 -#define ARASAN_PROG_CHNG_ROWADR_END_MASK 0x00400000 - -#define ARASAN_NAND_CMD_ECC_ON_MASK 0x80000000 -#define ARASAN_NAND_CMD_CMD12_MASK 0xFFFF -#define ARASAN_NAND_CMD_PG_SIZE_MASK 0x3800000 -#define ARASAN_NAND_CMD_PG_SIZE_SHIFT 23 -#define ARASAN_NAND_CMD_CMD2_SHIFT 8 -#define ARASAN_NAND_CMD_ADDR_CYCL_MASK 0x70000000 -#define ARASAN_NAND_CMD_ADDR_CYCL_SHIFT 28 - -#define ARASAN_NAND_MEM_ADDR1_PAGE_MASK 0xFFFF0000 -#define ARASAN_NAND_MEM_ADDR1_COL_MASK 0xFFFF -#define ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT 16 -#define ARASAN_NAND_MEM_ADDR2_PAGE_MASK 0xFF -#define ARASAN_NAND_MEM_ADDR2_CS_MASK 0xC0000000 -#define ARASAN_NAND_MEM_ADDR2_BCH_MASK 0xE000000 -#define ARASAN_NAND_MEM_ADDR2_BCH_SHIFT 25 - -#define ARASAN_NAND_INT_STS_ERR_EN_MASK 0x10 -#define ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK 0x08 -#define ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK 0x02 -#define ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK 0x01 -#define ARASAN_NAND_INT_STS_XFR_CMPLT_MASK 0x04 - -#define ARASAN_NAND_PKT_REG_PKT_CNT_MASK 0xFFF000 -#define ARASAN_NAND_PKT_REG_PKT_SIZE_MASK 0x7FF -#define ARASAN_NAND_PKT_REG_PKT_CNT_SHFT 12 - -#define ARASAN_NAND_ROW_ADDR_CYCL_MASK 0x0F -#define ARASAN_NAND_COL_ADDR_CYCL_MASK 0xF0 -#define ARASAN_NAND_COL_ADDR_CYCL_SHIFT 4 - -#define ARASAN_NAND_ECC_SIZE_SHIFT 16 -#define ARASAN_NAND_ECC_BCH_SHIFT 27 - -#define ARASAN_NAND_PKTSIZE_1K 1024 -#define ARASAN_NAND_PKTSIZE_512 512 - -#define ARASAN_NAND_POLL_TIMEOUT 1000000 -#define ARASAN_NAND_INVALID_ADDR_CYCL 0xFF - -#define ERR_ADDR_CYCLE -1 -#define READ_BUFF_SIZE 0x4000 - -static struct arasan_nand_command_format *curr_cmd; - -enum addr_cycles { - NAND_ADDR_CYCL_NONE, - NAND_ADDR_CYCL_ONE, - NAND_ADDR_CYCL_ROW, - NAND_ADDR_CYCL_COL, - NAND_ADDR_CYCL_BOTH, -}; - -static struct arasan_nand_command_format arasan_nand_commands[] = { - {NAND_CMD_READ0, NAND_CMD_READSTART, NAND_ADDR_CYCL_BOTH, - ARASAN_PROG_RD_MASK}, - {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, NAND_ADDR_CYCL_COL, - ARASAN_PROG_RD_MASK}, - {NAND_CMD_READID, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, - ARASAN_PROG_RD_ID_MASK}, - {NAND_CMD_STATUS, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, - ARASAN_PROG_RD_STS_MASK}, - {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, NAND_ADDR_CYCL_BOTH, - ARASAN_PROG_PG_PROG_MASK}, - {NAND_CMD_RNDIN, NAND_CMD_NONE, NAND_ADDR_CYCL_COL, - ARASAN_PROG_CHNG_ROWADR_END_MASK}, - {NAND_CMD_ERASE1, NAND_CMD_ERASE2, NAND_ADDR_CYCL_ROW, - ARASAN_PROG_BLK_ERS_MASK}, - {NAND_CMD_RESET, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, - ARASAN_PROG_RST_MASK}, - {NAND_CMD_PARAM, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, - ARASAN_PROG_RD_PARAM_PG_MASK}, - {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, - ARASAN_PROG_GET_FTRS_MASK}, - {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, - ARASAN_PROG_SET_FTRS_MASK}, - {NAND_CMD_NONE, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, 0}, -}; - -struct arasan_ecc_matrix { - u32 pagesize; - u32 ecc_codeword_size; - u8 eccbits; - u8 bch; - u8 bchval; - u16 eccaddr; - u16 eccsize; -}; - -static const struct arasan_ecc_matrix ecc_matrix[] = { - {512, 512, 1, 0, 0, 0x20D, 0x3}, - {512, 512, 4, 1, 3, 0x209, 0x7}, - {512, 512, 8, 1, 2, 0x203, 0xD}, - /* - * 2K byte page - */ - {2048, 512, 1, 0, 0, 0x834, 0xC}, - {2048, 512, 4, 1, 3, 0x826, 0x1A}, - {2048, 512, 8, 1, 2, 0x80c, 0x34}, - {2048, 512, 12, 1, 1, 0x822, 0x4E}, - {2048, 512, 16, 1, 0, 0x808, 0x68}, - {2048, 1024, 24, 1, 4, 0x81c, 0x54}, - /* - * 4K byte page - */ - {4096, 512, 1, 0, 0, 0x1068, 0x18}, - {4096, 512, 4, 1, 3, 0x104c, 0x34}, - {4096, 512, 8, 1, 2, 0x1018, 0x68}, - {4096, 512, 12, 1, 1, 0x1044, 0x9C}, - {4096, 512, 16, 1, 0, 0x1010, 0xD0}, - {4096, 1024, 24, 1, 4, 0x1038, 0xA8}, - /* - * 8K byte page - */ - {8192, 512, 1, 0, 0, 0x20d0, 0x30}, - {8192, 512, 4, 1, 3, 0x2098, 0x68}, - {8192, 512, 8, 1, 2, 0x2030, 0xD0}, - {8192, 512, 12, 1, 1, 0x2088, 0x138}, - {8192, 512, 16, 1, 0, 0x2020, 0x1A0}, - {8192, 1024, 24, 1, 4, 0x2070, 0x150}, - /* - * 16K byte page - */ - {16384, 512, 1, 0, 0, 0x4460, 0x60}, - {16384, 512, 4, 1, 3, 0x43f0, 0xD0}, - {16384, 512, 8, 1, 2, 0x4320, 0x1A0}, - {16384, 512, 12, 1, 1, 0x4250, 0x270}, - {16384, 512, 16, 1, 0, 0x4180, 0x340}, - {16384, 1024, 24, 1, 4, 0x4220, 0x2A0} -}; - -static struct nand_ecclayout ondie_nand_oob_64 = { - .eccbytes = 32, - - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 24, 25, 26, 27, 28, 29, 30, 31, - 40, 41, 42, 43, 44, 45, 46, 47, - 56, 57, 58, 59, 60, 61, 62, 63 - }, - - .oobfree = { - { .offset = 4, .length = 4 }, - { .offset = 20, .length = 4 }, - { .offset = 36, .length = 4 }, - { .offset = 52, .length = 4 } - } -}; - -/* - * bbt decriptors for chips with on-die ECC and - * chips with 64-byte OOB - */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 4, - .len = 4, - .veroffs = 20, - .maxblocks = 4, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 4, - .len = 4, - .veroffs = 20, - .maxblocks = 4, - .pattern = mirror_pattern -}; - -static u8 buf_data[READ_BUFF_SIZE]; -static u32 buf_index; - -static struct nand_ecclayout nand_oob; - -static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; - -static void arasan_nand_select_chip(struct mtd_info *mtd, int chip) -{ -} - -static void arasan_nand_enable_ecc(void) -{ - u32 reg_val; - - reg_val = readl(&arasan_nand_base->cmd_reg); - reg_val |= ARASAN_NAND_CMD_ECC_ON_MASK; - - writel(reg_val, &arasan_nand_base->cmd_reg); -} - -static u8 arasan_nand_get_addrcycle(struct mtd_info *mtd) -{ - u8 addrcycles; - struct nand_chip *chip = mtd_to_nand(mtd); - - switch (curr_cmd->addr_cycles) { - case NAND_ADDR_CYCL_NONE: - addrcycles = 0; - break; - case NAND_ADDR_CYCL_ONE: - addrcycles = 1; - break; - case NAND_ADDR_CYCL_ROW: - addrcycles = chip->onfi_params.addr_cycles & - ARASAN_NAND_ROW_ADDR_CYCL_MASK; - break; - case NAND_ADDR_CYCL_COL: - addrcycles = (chip->onfi_params.addr_cycles & - ARASAN_NAND_COL_ADDR_CYCL_MASK) >> - ARASAN_NAND_COL_ADDR_CYCL_SHIFT; - break; - case NAND_ADDR_CYCL_BOTH: - addrcycles = chip->onfi_params.addr_cycles & - ARASAN_NAND_ROW_ADDR_CYCL_MASK; - addrcycles += (chip->onfi_params.addr_cycles & - ARASAN_NAND_COL_ADDR_CYCL_MASK) >> - ARASAN_NAND_COL_ADDR_CYCL_SHIFT; - break; - default: - addrcycles = ARASAN_NAND_INVALID_ADDR_CYCL; - break; - } - return addrcycles; -} - -static int arasan_nand_read_page(struct mtd_info *mtd, u8 *buf, u32 size) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct arasan_nand_info *nand = nand_get_controller_data(chip); - u32 reg_val, i, pktsize, pktnum; - u32 *bufptr = (u32 *)buf; - u32 timeout; - u32 rdcount = 0; - u8 addr_cycles; - - if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K) - pktsize = ARASAN_NAND_PKTSIZE_1K; - else - pktsize = ARASAN_NAND_PKTSIZE_512; - - if (size % pktsize) - pktnum = size/pktsize + 1; - else - pktnum = size/pktsize; - - reg_val = readl(&arasan_nand_base->intsts_enr); - reg_val |= ARASAN_NAND_INT_STS_ERR_EN_MASK | - ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK; - writel(reg_val, &arasan_nand_base->intsts_enr); - - reg_val = readl(&arasan_nand_base->pkt_reg); - reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | - ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); - reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | - pktsize; - writel(reg_val, &arasan_nand_base->pkt_reg); - - if (!nand->on_die_ecc_enabled) { - arasan_nand_enable_ecc(); - addr_cycles = arasan_nand_get_addrcycle(mtd); - if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) - return ERR_ADDR_CYCLE; - - writel((NAND_CMD_RNDOUTSTART << ARASAN_NAND_CMD_CMD2_SHIFT) | - NAND_CMD_RNDOUT | (addr_cycles << - ARASAN_NAND_CMD_ADDR_CYCL_SHIFT), - &arasan_nand_base->ecc_sprcmd_reg); - } - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (rdcount < pktnum) { - timeout = ARASAN_NAND_POLL_TIMEOUT; - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) { - puts("arasan_read_page: timedout:Buff RDY\n"); - return -ETIMEDOUT; - } - - rdcount++; - - if (pktnum == rdcount) { - reg_val = readl(&arasan_nand_base->intsts_enr); - reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; - writel(reg_val, &arasan_nand_base->intsts_enr); - } else { - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_enr); - } - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_reg); - - for (i = 0; i < pktsize/4; i++) - bufptr[i] = readl(&arasan_nand_base->buf_dataport); - - - bufptr += pktsize/4; - - if (rdcount >= pktnum) - break; - - writel(ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_enr); - } - - timeout = ARASAN_NAND_POLL_TIMEOUT; - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) { - puts("arasan rd_page timedout:Xfer CMPLT\n"); - return -ETIMEDOUT; - } - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); - - if (!nand->on_die_ecc_enabled) { - if (readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK) { - printf("arasan rd_page:sbiterror\n"); - return -1; - } - - if (readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_ERR_EN_MASK) { - mtd->ecc_stats.failed++; - printf("arasan rd_page:multibiterror\n"); - return -1; - } - } - - return 0; -} - -static int arasan_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, int oob_required, int page) -{ - int status; - - status = arasan_nand_read_page(mtd, buf, (mtd->writesize)); - - if (oob_required) - chip->ecc.read_oob(mtd, chip, page); - - return status; -} - -static void arasan_nand_fill_tx(const u8 *buf, int len) -{ - u32 __iomem *nand = &arasan_nand_base->buf_dataport; - - if (((unsigned long)buf & 0x3) != 0) { - if (((unsigned long)buf & 0x1) != 0) { - if (len) { - writeb(*buf, nand); - buf += 1; - len--; - } - } - - if (((unsigned long)buf & 0x3) != 0) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - } - } - - while (len >= 4) { - writel(*(u32 *)buf, nand); - buf += 4; - len -= 4; - } - - if (len) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - - if (len) - writeb(*buf, nand); - } -} - -static int arasan_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, int oob_required, - int page) -{ - u32 reg_val, i, pktsize, pktnum; - const u32 *bufptr = (const u32 *)buf; - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - u32 size = mtd->writesize; - u32 rdcount = 0; - u8 column_addr_cycles; - struct arasan_nand_info *nand = nand_get_controller_data(chip); - - if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K) - pktsize = ARASAN_NAND_PKTSIZE_1K; - else - pktsize = ARASAN_NAND_PKTSIZE_512; - - if (size % pktsize) - pktnum = size/pktsize + 1; - else - pktnum = size/pktsize; - - reg_val = readl(&arasan_nand_base->pkt_reg); - reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | - ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); - reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | pktsize; - writel(reg_val, &arasan_nand_base->pkt_reg); - - if (!nand->on_die_ecc_enabled) { - arasan_nand_enable_ecc(); - column_addr_cycles = (chip->onfi_params.addr_cycles & - ARASAN_NAND_COL_ADDR_CYCL_MASK) >> - ARASAN_NAND_COL_ADDR_CYCL_SHIFT; - writel((NAND_CMD_RNDIN | (column_addr_cycles << 28)), - &arasan_nand_base->ecc_sprcmd_reg); - } - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (rdcount < pktnum) { - timeout = ARASAN_NAND_POLL_TIMEOUT; - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) { - udelay(1); - timeout--; - } - - if (!timeout) { - puts("arasan_write_page: timedout:Buff RDY\n"); - return -ETIMEDOUT; - } - - rdcount++; - - if (pktnum == rdcount) { - reg_val = readl(&arasan_nand_base->intsts_enr); - reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; - writel(reg_val, &arasan_nand_base->intsts_enr); - } else { - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_enr); - } - - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_reg); - - for (i = 0; i < pktsize/4; i++) - writel(bufptr[i], &arasan_nand_base->buf_dataport); - - bufptr += pktsize/4; - - if (rdcount >= pktnum) - break; - - writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_enr); - } - - timeout = ARASAN_NAND_POLL_TIMEOUT; - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) { - puts("arasan write_page timedout:Xfer CMPLT\n"); - return -ETIMEDOUT; - } - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); - - if (oob_required) - chip->ecc.write_oob(mtd, chip, nand->page); - - return 0; -} - -static int arasan_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, (mtd->oobsize)); - - return 0; -} - -static int arasan_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int status = 0; - const u8 *buf = chip->oob_poi; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, mtd->oobsize); - - return status; -} - -static int arasan_nand_reset(struct arasan_nand_command_format *curr_cmd) -{ - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - u32 cmd_reg = 0; - - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - cmd_reg = readl(&arasan_nand_base->cmd_reg); - cmd_reg &= ~ARASAN_NAND_CMD_CMD12_MASK; - - cmd_reg |= curr_cmd->cmd1 | - (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); - writel(cmd_reg, &arasan_nand_base->cmd_reg); - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) { - printf("ERROR:%s timedout\n", __func__); - return -ETIMEDOUT; - } - - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); - - return 0; -} - -static u8 arasan_nand_page(struct mtd_info *mtd) -{ - u8 page_val = 0; - - switch (mtd->writesize) { - case 512: - page_val = 0; - break; - case 2048: - page_val = 1; - break; - case 4096: - page_val = 2; - break; - case 8192: - page_val = 3; - break; - case 16384: - page_val = 4; - break; - case 1024: - page_val = 5; - break; - default: - printf("%s:Pagesize>16K\n", __func__); - break; - } - - return page_val; -} - -static int arasan_nand_send_wrcmd(struct arasan_nand_command_format *curr_cmd, - int column, int page_addr, struct mtd_info *mtd) -{ - u32 reg_val, page; - u8 page_val, addr_cycles; - - writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->cmd_reg); - reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; - reg_val |= curr_cmd->cmd1 | - (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); - if (curr_cmd->cmd1 == NAND_CMD_SEQIN) { - reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK; - page_val = arasan_nand_page(mtd); - reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT); - } - - reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; - addr_cycles = arasan_nand_get_addrcycle(mtd); - - if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) - return ERR_ADDR_CYCLE; - - reg_val |= (addr_cycles << - ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); - writel(reg_val, &arasan_nand_base->cmd_reg); - - if (page_addr == -1) - page_addr = 0; - - page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & - ARASAN_NAND_MEM_ADDR1_PAGE_MASK; - column &= ARASAN_NAND_MEM_ADDR1_COL_MASK; - writel(page|column, &arasan_nand_base->memadr_reg1); - - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; - reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); - writel(reg_val, &arasan_nand_base->memadr_reg2); - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; - writel(reg_val, &arasan_nand_base->memadr_reg2); - - return 0; -} - -static void arasan_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - u32 reg_val; - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - - reg_val = readl(&arasan_nand_base->pkt_reg); - reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | - ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); - - reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | len; - writel(reg_val, &arasan_nand_base->pkt_reg); - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) { - udelay(1); - timeout--; - } - - if (!timeout) - puts("ERROR:arasan_nand_write_buf timedout:Buff RDY\n"); - - reg_val = readl(&arasan_nand_base->intsts_enr); - reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; - writel(reg_val, &arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, - &arasan_nand_base->intsts_reg); - - arasan_nand_fill_tx(buf, len); - - timeout = ARASAN_NAND_POLL_TIMEOUT; - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) - puts("ERROR:arasan_nand_write_buf timedout:Xfer CMPLT\n"); - - writel(readl(&arasan_nand_base->intsts_enr) | - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - writel(readl(&arasan_nand_base->intsts_reg) | - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); -} - -static int arasan_nand_erase(struct arasan_nand_command_format *curr_cmd, - int column, int page_addr, struct mtd_info *mtd) -{ - u32 reg_val, page; - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - u8 row_addr_cycles; - - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->cmd_reg); - reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; - reg_val |= curr_cmd->cmd1 | - (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); - row_addr_cycles = arasan_nand_get_addrcycle(mtd); - - if (row_addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) - return ERR_ADDR_CYCLE; - - reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; - reg_val |= (row_addr_cycles << - ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); - - writel(reg_val, &arasan_nand_base->cmd_reg); - - page = (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & - ARASAN_NAND_MEM_ADDR1_COL_MASK; - column = page_addr & ARASAN_NAND_MEM_ADDR1_COL_MASK; - writel(column | (page << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT), - &arasan_nand_base->memadr_reg1); - - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; - reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); - writel(reg_val, &arasan_nand_base->memadr_reg2); - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; - writel(reg_val, &arasan_nand_base->memadr_reg2); - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - if (!timeout) { - printf("ERROR:%s timedout:Xfer CMPLT\n", __func__); - return -ETIMEDOUT; - } - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); - - return 0; -} - -static int arasan_nand_read_status(struct arasan_nand_command_format *curr_cmd, - int column, int page_addr, struct mtd_info *mtd) -{ - u32 reg_val; - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - u8 addr_cycles; - - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->cmd_reg); - reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; - reg_val |= curr_cmd->cmd1 | - (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); - addr_cycles = arasan_nand_get_addrcycle(mtd); - - if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) - return ERR_ADDR_CYCLE; - - reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; - reg_val |= (addr_cycles << - ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); - - writel(reg_val, &arasan_nand_base->cmd_reg); - - reg_val = readl(&arasan_nand_base->pkt_reg); - reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | - ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); - reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | 1; - writel(reg_val, &arasan_nand_base->pkt_reg); - - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; - writel(reg_val, &arasan_nand_base->memadr_reg2); - - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - - if (!timeout) { - printf("ERROR:%s: timedout:Xfer CMPLT\n", __func__); - return -ETIMEDOUT; - } - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); - - return 0; -} - -static int arasan_nand_send_rdcmd(struct arasan_nand_command_format *curr_cmd, - int column, int page_addr, struct mtd_info *mtd) -{ - u32 reg_val, addr_cycles, page; - u8 page_val; - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_enr); - - reg_val = readl(&arasan_nand_base->cmd_reg); - reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; - reg_val |= curr_cmd->cmd1 | - (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); - - if (curr_cmd->cmd1 == NAND_CMD_RNDOUT || - curr_cmd->cmd1 == NAND_CMD_READ0) { - reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK; - page_val = arasan_nand_page(mtd); - reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT); - } - - reg_val &= ~ARASAN_NAND_CMD_ECC_ON_MASK; - - reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; - - addr_cycles = arasan_nand_get_addrcycle(mtd); - - if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) - return ERR_ADDR_CYCLE; - - reg_val |= (addr_cycles << 28); - writel(reg_val, &arasan_nand_base->cmd_reg); - - if (page_addr == -1) - page_addr = 0; - - page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & - ARASAN_NAND_MEM_ADDR1_PAGE_MASK; - column &= ARASAN_NAND_MEM_ADDR1_COL_MASK; - writel(page | column, &arasan_nand_base->memadr_reg1); - - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; - reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); - writel(reg_val, &arasan_nand_base->memadr_reg2); - - reg_val = readl(&arasan_nand_base->memadr_reg2); - reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; - writel(reg_val, &arasan_nand_base->memadr_reg2); - buf_index = 0; - - return 0; -} - -static void arasan_nand_read_buf(struct mtd_info *mtd, u8 *buf, int size) -{ - u32 reg_val, i; - u32 *bufptr = (u32 *)buf; - u32 timeout = ARASAN_NAND_POLL_TIMEOUT; - - reg_val = readl(&arasan_nand_base->pkt_reg); - reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | - ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); - reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | size; - writel(reg_val, &arasan_nand_base->pkt_reg); - - writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) { - udelay(1); - timeout--; - } - - if (!timeout) - puts("ERROR:arasan_nand_read_buf timedout:Buff RDY\n"); - - reg_val = readl(&arasan_nand_base->intsts_enr); - reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; - writel(reg_val, &arasan_nand_base->intsts_enr); - - writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, - &arasan_nand_base->intsts_reg); - - buf_index = 0; - for (i = 0; i < size / 4; i++) - bufptr[i] = readl(&arasan_nand_base->buf_dataport); - - if (size & 0x03) - bufptr[i] = readl(&arasan_nand_base->buf_dataport); - - timeout = ARASAN_NAND_POLL_TIMEOUT; - - while (!(readl(&arasan_nand_base->intsts_reg) & - ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { - udelay(1); - timeout--; - } - - if (!timeout) - puts("ERROR:arasan_nand_read_buf timedout:Xfer CMPLT\n"); - - reg_val = readl(&arasan_nand_base->intsts_enr); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - reg_val = readl(&arasan_nand_base->intsts_reg); - writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_reg); -} - -static u8 arasan_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u32 size; - u8 val; - struct nand_onfi_params *p; - - if (buf_index == 0) { - p = &chip->onfi_params; - if (curr_cmd->cmd1 == NAND_CMD_READID) - size = 4; - else if (curr_cmd->cmd1 == NAND_CMD_PARAM) - size = sizeof(struct nand_onfi_params); - else if (curr_cmd->cmd1 == NAND_CMD_RNDOUT) - size = le16_to_cpu(p->ext_param_page_length) * 16; - else if (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES) - size = 4; - else if (curr_cmd->cmd1 == NAND_CMD_STATUS) - return readb(&arasan_nand_base->flash_sts_reg); - else - size = 8; - chip->read_buf(mtd, &buf_data[0], size); - } - - val = *(&buf_data[0] + buf_index); - buf_index++; - - return val; -} - -static void arasan_nand_cmd_function(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - u32 i, ret = 0; - struct nand_chip *chip = mtd_to_nand(mtd); - struct arasan_nand_info *nand = nand_get_controller_data(chip); - - curr_cmd = NULL; - writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, - &arasan_nand_base->intsts_enr); - - if ((command == NAND_CMD_READOOB) && - (mtd->writesize > 512)) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* Get the command format */ - for (i = 0; (arasan_nand_commands[i].cmd1 != NAND_CMD_NONE || - arasan_nand_commands[i].cmd2 != NAND_CMD_NONE); i++) { - if (command == arasan_nand_commands[i].cmd1) { - curr_cmd = &arasan_nand_commands[i]; - break; - } - } - - if (curr_cmd == NULL) { - printf("Unsupported Command; 0x%x\n", command); - return; - } - - if (curr_cmd->cmd1 == NAND_CMD_RESET) - ret = arasan_nand_reset(curr_cmd); - - if ((curr_cmd->cmd1 == NAND_CMD_READID) || - (curr_cmd->cmd1 == NAND_CMD_PARAM) || - (curr_cmd->cmd1 == NAND_CMD_RNDOUT) || - (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES) || - (curr_cmd->cmd1 == NAND_CMD_READ0)) - ret = arasan_nand_send_rdcmd(curr_cmd, column, page_addr, mtd); - - if ((curr_cmd->cmd1 == NAND_CMD_SET_FEATURES) || - (curr_cmd->cmd1 == NAND_CMD_SEQIN)) { - nand->page = page_addr; - ret = arasan_nand_send_wrcmd(curr_cmd, column, page_addr, mtd); - } - - if (curr_cmd->cmd1 == NAND_CMD_ERASE1) - ret = arasan_nand_erase(curr_cmd, column, page_addr, mtd); - - if (curr_cmd->cmd1 == NAND_CMD_STATUS) - ret = arasan_nand_read_status(curr_cmd, column, page_addr, mtd); - - if (ret != 0) - printf("ERROR:%s:command:0x%x\n", __func__, curr_cmd->cmd1); -} - -static void arasan_check_ondie(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct arasan_nand_info *nand = nand_get_controller_data(nand_chip); - u8 maf_id, dev_id; - u8 get_feature[4]; - u8 set_feature[4] = {ENABLE_ONDIE_ECC, 0x00, 0x00, 0x00}; - u32 i; - - /* Send the command for reading device ID */ - nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0, -1); - - /* Read manufacturer and device IDs */ - maf_id = nand_chip->read_byte(mtd); - dev_id = nand_chip->read_byte(mtd); - - if ((maf_id == NAND_MFR_MICRON) && - ((dev_id == 0xf1) || (dev_id == 0xa1) || (dev_id == 0xb1) || - (dev_id == 0xaa) || (dev_id == 0xba) || (dev_id == 0xda) || - (dev_id == 0xca) || (dev_id == 0xac) || (dev_id == 0xbc) || - (dev_id == 0xdc) || (dev_id == 0xcc) || (dev_id == 0xa3) || - (dev_id == 0xb3) || (dev_id == 0xd3) || (dev_id == 0xc3))) { - nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, - ONDIE_ECC_FEATURE_ADDR, -1); - - nand_chip->write_buf(mtd, &set_feature[0], 4); - nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, - ONDIE_ECC_FEATURE_ADDR, -1); - - for (i = 0; i < 4; i++) - get_feature[i] = nand_chip->read_byte(mtd); - - if (get_feature[0] & ENABLE_ONDIE_ECC) - nand->on_die_ecc_enabled = true; - else - printf("%s: Unable to enable OnDie ECC\n", __func__); - - /* Use the BBT pattern descriptors */ - nand_chip->bbt_td = &bbt_main_descr; - nand_chip->bbt_md = &bbt_mirror_descr; - } -} - -static int arasan_nand_ecc_init(struct mtd_info *mtd) -{ - int found = -1; - u32 regval, eccpos_start, i, eccaddr; - struct nand_chip *nand_chip = mtd_to_nand(mtd); - - for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) { - if ((ecc_matrix[i].pagesize == mtd->writesize) && - (ecc_matrix[i].ecc_codeword_size >= - nand_chip->ecc_step_ds)) { - if (ecc_matrix[i].eccbits >= - nand_chip->ecc_strength_ds) { - found = i; - break; - } - found = i; - } - } - - if (found < 0) - return 1; - - eccaddr = mtd->writesize + mtd->oobsize - - ecc_matrix[found].eccsize; - - regval = eccaddr | - (ecc_matrix[found].eccsize << ARASAN_NAND_ECC_SIZE_SHIFT) | - (ecc_matrix[found].bch << ARASAN_NAND_ECC_BCH_SHIFT); - writel(regval, &arasan_nand_base->ecc_reg); - - if (ecc_matrix[found].bch) { - regval = readl(&arasan_nand_base->memadr_reg2); - regval &= ~ARASAN_NAND_MEM_ADDR2_BCH_MASK; - regval |= (ecc_matrix[found].bchval << - ARASAN_NAND_MEM_ADDR2_BCH_SHIFT); - writel(regval, &arasan_nand_base->memadr_reg2); - } - - nand_oob.eccbytes = ecc_matrix[found].eccsize; - eccpos_start = mtd->oobsize - nand_oob.eccbytes; - - for (i = 0; i < nand_oob.eccbytes; i++) - nand_oob.eccpos[i] = eccpos_start + i; - - nand_oob.oobfree[0].offset = 2; - nand_oob.oobfree[0].length = eccpos_start - 2; - - nand_chip->ecc.size = ecc_matrix[found].ecc_codeword_size; - nand_chip->ecc.strength = ecc_matrix[found].eccbits; - nand_chip->ecc.bytes = ecc_matrix[found].eccsize; - nand_chip->ecc.layout = &nand_oob; - - return 0; -} - -static int arasan_nand_init(struct nand_chip *nand_chip, int devnum) -{ - struct arasan_nand_info *nand; - struct mtd_info *mtd; - int err = -1; - - nand = calloc(1, sizeof(struct arasan_nand_info)); - if (!nand) { - printf("%s: failed to allocate\n", __func__); - return err; - } - - nand->nand_base = arasan_nand_base; - mtd = nand_to_mtd(nand_chip); - nand_set_controller_data(nand_chip, nand); - - /* Set the driver entry points for MTD */ - nand_chip->cmdfunc = arasan_nand_cmd_function; - nand_chip->select_chip = arasan_nand_select_chip; - nand_chip->read_byte = arasan_nand_read_byte; - - /* Buffer read/write routines */ - nand_chip->read_buf = arasan_nand_read_buf; - nand_chip->write_buf = arasan_nand_write_buf; - nand_chip->bbt_options = NAND_BBT_USE_FLASH; - - writel(0x0, &arasan_nand_base->cmd_reg); - writel(0x0, &arasan_nand_base->pgm_reg); - - /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { - printf("%s: nand_scan_ident failed\n", __func__); - goto fail; - } - - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.hwctl = NULL; - nand_chip->ecc.read_page = arasan_nand_read_page_hwecc; - nand_chip->ecc.write_page = arasan_nand_write_page_hwecc; - nand_chip->ecc.read_oob = arasan_nand_read_oob; - nand_chip->ecc.write_oob = arasan_nand_write_oob; - - arasan_check_ondie(mtd); - - /* - * If on die supported, then give priority to on-die ecc and use - * it instead of controller ecc. - */ - if (nand->on_die_ecc_enabled) { - nand_chip->ecc.strength = 1; - nand_chip->ecc.size = mtd->writesize; - nand_chip->ecc.bytes = 0; - nand_chip->ecc.layout = &ondie_nand_oob_64; - } else { - if (arasan_nand_ecc_init(mtd)) { - printf("%s: nand_ecc_init failed\n", __func__); - goto fail; - } - } - - if (nand_scan_tail(mtd)) { - printf("%s: nand_scan_tail failed\n", __func__); - goto fail; - } - - if (nand_register(devnum, mtd)) { - printf("Nand Register Fail\n"); - goto fail; - } - - return 0; -fail: - free(nand); - return err; -} - -void board_nand_init(void) -{ - struct nand_chip *nand = &nand_chip[0]; - - if (arasan_nand_init(nand, 0)) - puts("NAND init failed\n"); -} diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c deleted file mode 100644 index a5b76e1aa0..0000000000 --- a/drivers/mtd/nand/atmel_nand.c +++ /dev/null @@ -1,1511 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2007-2008 - * Stelian Pop - * Lead Tech Design - * - * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas - * - * Add Programmable Multibit ECC support for various AT91 SoC - * (C) Copyright 2012 ATMEL, Hong Xu - */ - -#include -#include -#include - -#include -#include -#include -#include - -#ifdef CONFIG_ATMEL_NAND_HWECC - -/* Register access macros */ -#define ecc_readl(add, reg) \ - readl(add + ATMEL_ECC_##reg) -#define ecc_writel(add, reg, value) \ - writel((value), add + ATMEL_ECC_##reg) - -#include "atmel_nand_ecc.h" /* Hardware ECC registers */ - -#ifdef CONFIG_ATMEL_NAND_HW_PMECC - -#ifdef CONFIG_SPL_BUILD -#undef CONFIG_SYS_NAND_ONFI_DETECTION -#endif - -struct atmel_nand_host { - struct pmecc_regs __iomem *pmecc; - struct pmecc_errloc_regs __iomem *pmerrloc; - void __iomem *pmecc_rom_base; - - u8 pmecc_corr_cap; - u16 pmecc_sector_size; - u32 pmecc_index_table_offset; - u32 pmecc_version; - - int pmecc_bytes_per_sector; - int pmecc_sector_number; - int pmecc_degree; /* Degree of remainders */ - int pmecc_cw_len; /* Length of codeword */ - - /* lookup table for alpha_to and index_of */ - void __iomem *pmecc_alpha_to; - void __iomem *pmecc_index_of; - - /* data for pmecc computation */ - int16_t *pmecc_smu; - int16_t *pmecc_partial_syn; - int16_t *pmecc_si; - int16_t *pmecc_lmu; /* polynomal order */ - int *pmecc_mu; - int *pmecc_dmu; - int *pmecc_delta; -}; - -static struct atmel_nand_host pmecc_host; -static struct nand_ecclayout atmel_pmecc_oobinfo; - -/* - * Return number of ecc bytes per sector according to sector size and - * correction capability - * - * Following table shows what at91 PMECC supported: - * Correction Capability Sector_512_bytes Sector_1024_bytes - * ===================== ================ ================= - * 2-bits 4-bytes 4-bytes - * 4-bits 7-bytes 7-bytes - * 8-bits 13-bytes 14-bytes - * 12-bits 20-bytes 21-bytes - * 24-bits 39-bytes 42-bytes - * 32-bits 52-bytes 56-bytes - */ -static int pmecc_get_ecc_bytes(int cap, int sector_size) -{ - int m = 12 + sector_size / 512; - return (m * cap + 7) / 8; -} - -static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, - int oobsize, int ecc_len) -{ - int i; - - layout->eccbytes = ecc_len; - - /* ECC will occupy the last ecc_len bytes continuously */ - for (i = 0; i < ecc_len; i++) - layout->eccpos[i] = oobsize - ecc_len + i; - - layout->oobfree[0].offset = 2; - layout->oobfree[0].length = - oobsize - ecc_len - layout->oobfree[0].offset; -} - -static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) -{ - int table_size; - - table_size = host->pmecc_sector_size == 512 ? - PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024; - - /* the ALPHA lookup table is right behind the INDEX lookup table. */ - return host->pmecc_rom_base + host->pmecc_index_table_offset + - table_size * sizeof(int16_t); -} - -static void pmecc_data_free(struct atmel_nand_host *host) -{ - free(host->pmecc_partial_syn); - free(host->pmecc_si); - free(host->pmecc_lmu); - free(host->pmecc_smu); - free(host->pmecc_mu); - free(host->pmecc_dmu); - free(host->pmecc_delta); -} - -static int pmecc_data_alloc(struct atmel_nand_host *host) -{ - const int cap = host->pmecc_corr_cap; - int size; - - size = (2 * cap + 1) * sizeof(int16_t); - host->pmecc_partial_syn = malloc(size); - host->pmecc_si = malloc(size); - host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t)); - host->pmecc_smu = malloc((cap + 2) * size); - - size = (cap + 1) * sizeof(int); - host->pmecc_mu = malloc(size); - host->pmecc_dmu = malloc(size); - host->pmecc_delta = malloc(size); - - if (host->pmecc_partial_syn && - host->pmecc_si && - host->pmecc_lmu && - host->pmecc_smu && - host->pmecc_mu && - host->pmecc_dmu && - host->pmecc_delta) - return 0; - - /* error happened */ - pmecc_data_free(host); - return -ENOMEM; - -} - -static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - int i; - uint32_t value; - - /* Fill odd syndromes */ - for (i = 0; i < host->pmecc_corr_cap; i++) { - value = pmecc_readl(host->pmecc, rem_port[sector].rem[i / 2]); - if (i & 1) - value >>= 16; - value &= 0xffff; - host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; - } -} - -static void pmecc_substitute(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - int16_t __iomem *alpha_to = host->pmecc_alpha_to; - int16_t __iomem *index_of = host->pmecc_index_of; - int16_t *partial_syn = host->pmecc_partial_syn; - const int cap = host->pmecc_corr_cap; - int16_t *si; - int i, j; - - /* si[] is a table that holds the current syndrome value, - * an element of that table belongs to the field - */ - si = host->pmecc_si; - - memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); - - /* Computation 2t syndromes based on S(x) */ - /* Odd syndromes */ - for (i = 1; i < 2 * cap; i += 2) { - for (j = 0; j < host->pmecc_degree; j++) { - if (partial_syn[i] & (0x1 << j)) - si[i] = readw(alpha_to + i * j) ^ si[i]; - } - } - /* Even syndrome = (Odd syndrome) ** 2 */ - for (i = 2, j = 1; j <= cap; i = ++j << 1) { - if (si[j] == 0) { - si[i] = 0; - } else { - int16_t tmp; - - tmp = readw(index_of + si[j]); - tmp = (tmp * 2) % host->pmecc_cw_len; - si[i] = readw(alpha_to + tmp); - } - } -} - -/* - * This function defines a Berlekamp iterative procedure for - * finding the value of the error location polynomial. - * The input is si[], initialize by pmecc_substitute(). - * The output is smu[][]. - * - * This function is written according to chip datasheet Chapter: - * Find the Error Location Polynomial Sigma(x) of Section: - * Programmable Multibit ECC Control (PMECC). - */ -static void pmecc_get_sigma(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - - int16_t *lmu = host->pmecc_lmu; - int16_t *si = host->pmecc_si; - int *mu = host->pmecc_mu; - int *dmu = host->pmecc_dmu; /* Discrepancy */ - int *delta = host->pmecc_delta; /* Delta order */ - int cw_len = host->pmecc_cw_len; - const int16_t cap = host->pmecc_corr_cap; - const int num = 2 * cap + 1; - int16_t __iomem *index_of = host->pmecc_index_of; - int16_t __iomem *alpha_to = host->pmecc_alpha_to; - int i, j, k; - uint32_t dmu_0_count, tmp; - int16_t *smu = host->pmecc_smu; - - /* index of largest delta */ - int ro; - int largest; - int diff; - - /* Init the Sigma(x) */ - memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu)); - - dmu_0_count = 0; - - /* First Row */ - - /* Mu */ - mu[0] = -1; - - smu[0] = 1; - - /* discrepancy set to 1 */ - dmu[0] = 1; - /* polynom order set to 0 */ - lmu[0] = 0; - /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */ - delta[0] = -1; - - /* Second Row */ - - /* Mu */ - mu[1] = 0; - /* Sigma(x) set to 1 */ - smu[num] = 1; - - /* discrepancy set to S1 */ - dmu[1] = si[1]; - - /* polynom order set to 0 */ - lmu[1] = 0; - - /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */ - delta[1] = 0; - - for (i = 1; i <= cap; i++) { - mu[i + 1] = i << 1; - /* Begin Computing Sigma (Mu+1) and L(mu) */ - /* check if discrepancy is set to 0 */ - if (dmu[i] == 0) { - dmu_0_count++; - - tmp = ((cap - (lmu[i] >> 1) - 1) / 2); - if ((cap - (lmu[i] >> 1) - 1) & 0x1) - tmp += 2; - else - tmp += 1; - - if (dmu_0_count == tmp) { - for (j = 0; j <= (lmu[i] >> 1) + 1; j++) - smu[(cap + 1) * num + j] = - smu[i * num + j]; - - lmu[cap + 1] = lmu[i]; - return; - } - - /* copy polynom */ - for (j = 0; j <= lmu[i] >> 1; j++) - smu[(i + 1) * num + j] = smu[i * num + j]; - - /* copy previous polynom order to the next */ - lmu[i + 1] = lmu[i]; - } else { - ro = 0; - largest = -1; - /* find largest delta with dmu != 0 */ - for (j = 0; j < i; j++) { - if ((dmu[j]) && (delta[j] > largest)) { - largest = delta[j]; - ro = j; - } - } - - /* compute difference */ - diff = (mu[i] - mu[ro]); - - /* Compute degree of the new smu polynomial */ - if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) - lmu[i + 1] = lmu[i]; - else - lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; - - /* Init smu[i+1] with 0 */ - for (k = 0; k < num; k++) - smu[(i + 1) * num + k] = 0; - - /* Compute smu[i+1] */ - for (k = 0; k <= lmu[ro] >> 1; k++) { - int16_t a, b, c; - - if (!(smu[ro * num + k] && dmu[i])) - continue; - a = readw(index_of + dmu[i]); - b = readw(index_of + dmu[ro]); - c = readw(index_of + smu[ro * num + k]); - tmp = a + (cw_len - b) + c; - a = readw(alpha_to + tmp % cw_len); - smu[(i + 1) * num + (k + diff)] = a; - } - - for (k = 0; k <= lmu[i] >> 1; k++) - smu[(i + 1) * num + k] ^= smu[i * num + k]; - } - - /* End Computing Sigma (Mu+1) and L(mu) */ - /* In either case compute delta */ - delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; - - /* Do not compute discrepancy for the last iteration */ - if (i >= cap) - continue; - - for (k = 0; k <= (lmu[i + 1] >> 1); k++) { - tmp = 2 * (i - 1); - if (k == 0) { - dmu[i + 1] = si[tmp + 3]; - } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { - int16_t a, b, c; - a = readw(index_of + - smu[(i + 1) * num + k]); - b = si[2 * (i - 1) + 3 - k]; - c = readw(index_of + b); - tmp = a + c; - tmp %= cw_len; - dmu[i + 1] = readw(alpha_to + tmp) ^ - dmu[i + 1]; - } - } - } -} - -static int pmecc_err_location(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - const int cap = host->pmecc_corr_cap; - const int num = 2 * cap + 1; - int sector_size = host->pmecc_sector_size; - int err_nbr = 0; /* number of error */ - int roots_nbr; /* number of roots */ - int i; - uint32_t val; - int16_t *smu = host->pmecc_smu; - int timeout = PMECC_MAX_TIMEOUT_US; - - pmecc_writel(host->pmerrloc, eldis, PMERRLOC_DISABLE); - - for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { - pmecc_writel(host->pmerrloc, sigma[i], - smu[(cap + 1) * num + i]); - err_nbr++; - } - - val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1); - if (sector_size == 1024) - val |= PMERRLOC_ELCFG_SECTOR_1024; - - pmecc_writel(host->pmerrloc, elcfg, val); - pmecc_writel(host->pmerrloc, elen, - sector_size * 8 + host->pmecc_degree * cap); - - while (--timeout) { - if (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_CALC_DONE) - break; - WATCHDOG_RESET(); - udelay(1); - } - - if (!timeout) { - dev_err(host->dev, "atmel_nand : Timeout to calculate PMECC error location\n"); - return -1; - } - - roots_nbr = (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_ERR_NUM_MASK) - >> 8; - /* Number of roots == degree of smu hence <= cap */ - if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) - return err_nbr - 1; - - /* Number of roots does not match the degree of smu - * unable to correct error */ - return -1; -} - -static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, - int sector_num, int extra_bytes, int err_nbr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - int i = 0; - int byte_pos, bit_pos, sector_size, pos; - uint32_t tmp; - uint8_t err_byte; - - sector_size = host->pmecc_sector_size; - - while (err_nbr) { - tmp = pmecc_readl(host->pmerrloc, el[i]) - 1; - byte_pos = tmp / 8; - bit_pos = tmp % 8; - - if (byte_pos >= (sector_size + extra_bytes)) - BUG(); /* should never happen */ - - if (byte_pos < sector_size) { - err_byte = *(buf + byte_pos); - *(buf + byte_pos) ^= (1 << bit_pos); - - pos = sector_num * host->pmecc_sector_size + byte_pos; - dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", - pos, bit_pos, err_byte, *(buf + byte_pos)); - } else { - /* Bit flip in OOB area */ - tmp = sector_num * host->pmecc_bytes_per_sector - + (byte_pos - sector_size); - err_byte = ecc[tmp]; - ecc[tmp] ^= (1 << bit_pos); - - pos = tmp + nand_chip->ecc.layout->eccpos[0]; - dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", - pos, bit_pos, err_byte, ecc[tmp]); - } - - i++; - err_nbr--; - } - - return; -} - -static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, - u8 *ecc) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - int i, err_nbr, eccbytes; - uint8_t *buf_pos; - - /* SAMA5D4 PMECC IP can correct errors for all 0xff page */ - if (host->pmecc_version >= PMECC_VERSION_SAMA5D4) - goto normal_check; - - eccbytes = nand_chip->ecc.bytes; - for (i = 0; i < eccbytes; i++) - if (ecc[i] != 0xff) - goto normal_check; - /* Erased page, return OK */ - return 0; - -normal_check: - for (i = 0; i < host->pmecc_sector_number; i++) { - err_nbr = 0; - if (pmecc_stat & 0x1) { - buf_pos = buf + i * host->pmecc_sector_size; - - pmecc_gen_syndrome(mtd, i); - pmecc_substitute(mtd); - pmecc_get_sigma(mtd); - - err_nbr = pmecc_err_location(mtd); - if (err_nbr == -1) { - dev_err(host->dev, "PMECC: Too many errors\n"); - mtd->ecc_stats.failed++; - return -EBADMSG; - } else { - pmecc_correct_data(mtd, buf_pos, ecc, i, - host->pmecc_bytes_per_sector, err_nbr); - mtd->ecc_stats.corrected += err_nbr; - } - } - pmecc_stat >>= 1; - } - - return 0; -} - -static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - struct atmel_nand_host *host = nand_get_controller_data(chip); - int eccsize = chip->ecc.size; - uint8_t *oob = chip->oob_poi; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint32_t stat; - int timeout = PMECC_MAX_TIMEOUT_US; - - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); - pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg)) - & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); - - chip->read_buf(mtd, buf, eccsize); - chip->read_buf(mtd, oob, mtd->oobsize); - - while (--timeout) { - if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) - break; - WATCHDOG_RESET(); - udelay(1); - } - - if (!timeout) { - dev_err(host->dev, "atmel_nand : Timeout to read PMECC page\n"); - return -1; - } - - stat = pmecc_readl(host->pmecc, isr); - if (stat != 0) - if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) - return -EBADMSG; - - return 0; -} - -static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - struct atmel_nand_host *host = nand_get_controller_data(chip); - uint32_t *eccpos = chip->ecc.layout->eccpos; - int i, j; - int timeout = PMECC_MAX_TIMEOUT_US; - - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); - - pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) | - PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); - - chip->write_buf(mtd, (u8 *)buf, mtd->writesize); - - while (--timeout) { - if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) - break; - WATCHDOG_RESET(); - udelay(1); - } - - if (!timeout) { - dev_err(host->dev, "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); - goto out; - } - - for (i = 0; i < host->pmecc_sector_number; i++) { - for (j = 0; j < host->pmecc_bytes_per_sector; j++) { - int pos; - - pos = i * host->pmecc_bytes_per_sector + j; - chip->oob_poi[eccpos[pos]] = - pmecc_readb(host->pmecc, ecc_port[i].ecc[j]); - } - } - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); -out: - return 0; -} - -static void atmel_pmecc_core_init(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(nand_chip); - uint32_t val = 0; - struct nand_ecclayout *ecc_layout; - - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); - - switch (host->pmecc_corr_cap) { - case 2: - val = PMECC_CFG_BCH_ERR2; - break; - case 4: - val = PMECC_CFG_BCH_ERR4; - break; - case 8: - val = PMECC_CFG_BCH_ERR8; - break; - case 12: - val = PMECC_CFG_BCH_ERR12; - break; - case 24: - val = PMECC_CFG_BCH_ERR24; - break; - case 32: - val = PMECC_CFG_BCH_ERR32; - break; - } - - if (host->pmecc_sector_size == 512) - val |= PMECC_CFG_SECTOR512; - else if (host->pmecc_sector_size == 1024) - val |= PMECC_CFG_SECTOR1024; - - switch (host->pmecc_sector_number) { - case 1: - val |= PMECC_CFG_PAGE_1SECTOR; - break; - case 2: - val |= PMECC_CFG_PAGE_2SECTORS; - break; - case 4: - val |= PMECC_CFG_PAGE_4SECTORS; - break; - case 8: - val |= PMECC_CFG_PAGE_8SECTORS; - break; - } - - val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE - | PMECC_CFG_AUTO_DISABLE); - pmecc_writel(host->pmecc, cfg, val); - - ecc_layout = nand_chip->ecc.layout; - pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1); - pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]); - pmecc_writel(host->pmecc, eaddr, - ecc_layout->eccpos[ecc_layout->eccbytes - 1]); - /* See datasheet about PMECC Clock Control Register */ - pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ); - pmecc_writel(host->pmecc, idr, 0xff); - pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); -} - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION -/* - * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If - * pmecc_corr_cap or pmecc_sector_size is 0, then set it as - * ONFI ECC parameters. - * @host: point to an atmel_nand_host structure. - * if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits. - * if host->pmecc_sector_size is 0 then set it as the ONFI sector_size. - * @chip: point to an nand_chip structure. - * @cap: store the ONFI ECC correct bits capbility - * @sector_size: in how many bytes that ONFI require to correct @ecc_bits - * - * Return 0 if success. otherwise return the error code. - */ -static int pmecc_choose_ecc(struct atmel_nand_host *host, - struct nand_chip *chip, - int *cap, int *sector_size) -{ - /* Get ECC requirement from ONFI parameters */ - *cap = *sector_size = 0; - if (chip->onfi_version) { - *cap = chip->ecc_strength_ds; - *sector_size = chip->ecc_step_ds; - pr_debug("ONFI params, minimum required ECC: %d bits in %d bytes\n", - *cap, *sector_size); - } - - if (*cap == 0 && *sector_size == 0) { - /* Non-ONFI compliant */ - dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes\n"); - *cap = 2; - *sector_size = 512; - } - - /* If head file doesn't specify then use the one in ONFI parameters */ - if (host->pmecc_corr_cap == 0) { - /* use the most fitable ecc bits (the near bigger one ) */ - if (*cap <= 2) - host->pmecc_corr_cap = 2; - else if (*cap <= 4) - host->pmecc_corr_cap = 4; - else if (*cap <= 8) - host->pmecc_corr_cap = 8; - else if (*cap <= 12) - host->pmecc_corr_cap = 12; - else if (*cap <= 24) - host->pmecc_corr_cap = 24; - else -#ifdef CONFIG_SAMA5D2 - host->pmecc_corr_cap = 32; -#else - host->pmecc_corr_cap = 24; -#endif - } - if (host->pmecc_sector_size == 0) { - /* use the most fitable sector size (the near smaller one ) */ - if (*sector_size >= 1024) - host->pmecc_sector_size = 1024; - else if (*sector_size >= 512) - host->pmecc_sector_size = 512; - else - return -EINVAL; - } - return 0; -} -#endif - -#if defined(NO_GALOIS_TABLE_IN_ROM) -static uint16_t *pmecc_galois_table; -static inline int deg(unsigned int poly) -{ - /* polynomial degree is the most-significant bit index */ - return fls(poly) - 1; -} - -static int build_gf_tables(int mm, unsigned int poly, - int16_t *index_of, int16_t *alpha_to) -{ - unsigned int i, x = 1; - const unsigned int k = 1 << deg(poly); - unsigned int nn = (1 << mm) - 1; - - /* primitive polynomial must be of degree m */ - if (k != (1u << mm)) - return -EINVAL; - - for (i = 0; i < nn; i++) { - alpha_to[i] = x; - index_of[x] = i; - if (i && (x == 1)) - /* polynomial is not primitive (a^i=1 with 0ecc.mode = NAND_ECC_HW; - nand->ecc.calculate = NULL; - nand->ecc.correct = NULL; - nand->ecc.hwctl = NULL; - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION - host->pmecc_corr_cap = host->pmecc_sector_size = 0; - -#ifdef CONFIG_PMECC_CAP - host->pmecc_corr_cap = CONFIG_PMECC_CAP; -#endif -#ifdef CONFIG_PMECC_SECTOR_SIZE - host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; -#endif - /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or - * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size - * from ONFI. - */ - if (pmecc_choose_ecc(host, nand, &cap, §or_size)) { - dev_err(host->dev, "Required ECC %d bits in %d bytes not supported!\n", - cap, sector_size); - return -EINVAL; - } - - if (cap > host->pmecc_corr_cap) - dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n", - host->pmecc_corr_cap, cap); - if (sector_size < host->pmecc_sector_size) - dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n", - host->pmecc_sector_size, sector_size); -#else /* CONFIG_SYS_NAND_ONFI_DETECTION */ - host->pmecc_corr_cap = CONFIG_PMECC_CAP; - host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; -#endif - - cap = host->pmecc_corr_cap; - sector_size = host->pmecc_sector_size; - - /* TODO: need check whether cap & sector_size is validate */ -#if defined(NO_GALOIS_TABLE_IN_ROM) - /* - * As pmecc_rom_base is the begin of the gallois field table, So the - * index offset just set as 0. - */ - host->pmecc_index_table_offset = 0; -#else - if (host->pmecc_sector_size == 512) - host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512; - else - host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024; -#endif - - pr_debug("Initialize PMECC params, cap: %d, sector: %d\n", - cap, sector_size); - - host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; - host->pmerrloc = (struct pmecc_errloc_regs __iomem *) - ATMEL_BASE_PMERRLOC; -#if defined(NO_GALOIS_TABLE_IN_ROM) - pmecc_galois_table = create_lookup_table(host->pmecc_sector_size); - if (!pmecc_galois_table) { - dev_err(host->dev, "out of memory\n"); - return -ENOMEM; - } - - host->pmecc_rom_base = (void __iomem *)pmecc_galois_table; -#else - host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; -#endif - - /* ECC is calculated for the whole page (1 step) */ - nand->ecc.size = mtd->writesize; - - /* set ECC page size and oob layout */ - switch (mtd->writesize) { - case 2048: - case 4096: - case 8192: - host->pmecc_degree = (sector_size == 512) ? - PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; - host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; - host->pmecc_sector_number = mtd->writesize / sector_size; - host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( - cap, sector_size); - host->pmecc_alpha_to = pmecc_get_alpha_to(host); - host->pmecc_index_of = host->pmecc_rom_base + - host->pmecc_index_table_offset; - - nand->ecc.steps = 1; - nand->ecc.bytes = host->pmecc_bytes_per_sector * - host->pmecc_sector_number; - - if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) { - dev_err(host->dev, "too large eccpos entries. max support ecc.bytes is %d\n", - MTD_MAX_ECCPOS_ENTRIES_LARGE); - return -EINVAL; - } - - if (nand->ecc.bytes > mtd->oobsize - PMECC_OOB_RESERVED_BYTES) { - dev_err(host->dev, "No room for ECC bytes\n"); - return -EINVAL; - } - pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, - mtd->oobsize, - nand->ecc.bytes); - nand->ecc.layout = &atmel_pmecc_oobinfo; - break; - case 512: - case 1024: - /* TODO */ - dev_err(host->dev, "Unsupported page size for PMECC, use Software ECC\n"); - default: - /* page size not handled by HW ECC */ - /* switching back to soft ECC */ - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.read_page = NULL; - nand->ecc.postpad = 0; - nand->ecc.prepad = 0; - nand->ecc.bytes = 0; - return 0; - } - - /* Allocate data for PMECC computation */ - if (pmecc_data_alloc(host)) { - dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); - return -ENOMEM; - } - - nand->options |= NAND_NO_SUBPAGE_WRITE; - nand->ecc.read_page = atmel_nand_pmecc_read_page; - nand->ecc.write_page = atmel_nand_pmecc_write_page; - nand->ecc.strength = cap; - - /* Check the PMECC ip version */ - host->pmecc_version = pmecc_readl(host->pmerrloc, version); - dev_dbg(host->dev, "PMECC IP version is: %x\n", host->pmecc_version); - - atmel_pmecc_core_init(mtd); - - return 0; -} - -#else - -/* oob layout for large page size - * bad block info is on bytes 0 and 1 - * the bytes have to be consecutives to avoid - * several NAND_CMD_RNDOUT during read - */ -static struct nand_ecclayout atmel_oobinfo_large = { - .eccbytes = 4, - .eccpos = {60, 61, 62, 63}, - .oobfree = { - {2, 58} - }, -}; - -/* oob layout for small page size - * bad block info is on bytes 4 and 5 - * the bytes have to be consecutives to avoid - * several NAND_CMD_RNDOUT during read - */ -static struct nand_ecclayout atmel_oobinfo_small = { - .eccbytes = 4, - .eccpos = {0, 1, 2, 3}, - .oobfree = { - {6, 10} - }, -}; - -/* - * Calculate HW ECC - * - * function called after a write - * - * mtd: MTD block structure - * dat: raw data (unused) - * ecc_code: buffer for ECC - */ -static int atmel_nand_calculate(struct mtd_info *mtd, - const u_char *dat, unsigned char *ecc_code) -{ - unsigned int ecc_value; - - /* get the first 2 ECC bytes */ - ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR); - - ecc_code[0] = ecc_value & 0xFF; - ecc_code[1] = (ecc_value >> 8) & 0xFF; - - /* get the last 2 ECC bytes */ - ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY; - - ecc_code[2] = ecc_value & 0xFF; - ecc_code[3] = (ecc_value >> 8) & 0xFF; - - return 0; -} - -/* - * HW ECC read page function - * - * mtd: mtd info structure - * chip: nand chip info structure - * buf: buffer to store read data - * oob_required: caller expects OOB data read to chip->oob_poi - */ -static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - uint8_t *ecc_pos; - int stat; - - /* read the page */ - chip->read_buf(mtd, p, eccsize); - - /* move to ECC position if needed */ - if (eccpos[0] != 0) { - /* This only works on large pages - * because the ECC controller waits for - * NAND_CMD_RNDOUTSTART after the - * NAND_CMD_RNDOUT. - * anyway, for small pages, the eccpos[0] == 0 - */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + eccpos[0], -1); - } - - /* the ECC controller needs to read the ECC just after the data */ - ecc_pos = oob + eccpos[0]; - chip->read_buf(mtd, ecc_pos, eccbytes); - - /* check if there's an error */ - stat = chip->ecc.correct(mtd, p, oob, NULL); - - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - - /* get back to oob start (end of page) */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - - /* read the oob */ - chip->read_buf(mtd, oob, mtd->oobsize); - - return 0; -} - -/* - * HW ECC Correction - * - * function called after a read - * - * mtd: MTD block structure - * dat: raw data read from the chip - * read_ecc: ECC from the chip (unused) - * isnull: unused - * - * Detect and correct a 1 bit error for a page - */ -static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *isnull) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - unsigned int ecc_status; - unsigned int ecc_word, ecc_bit; - - /* get the status from the Status Register */ - ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR); - - /* if there's no error */ - if (likely(!(ecc_status & ATMEL_ECC_RECERR))) - return 0; - - /* get error bit offset (4 bits) */ - ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR; - /* get word address (12 bits) */ - ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR; - ecc_word >>= 4; - - /* if there are multiple errors */ - if (ecc_status & ATMEL_ECC_MULERR) { - /* check if it is a freshly erased block - * (filled with 0xff) */ - if ((ecc_bit == ATMEL_ECC_BITADDR) - && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { - /* the block has just been erased, return OK */ - return 0; - } - /* it doesn't seems to be a freshly - * erased block. - * We can't correct so many errors */ - dev_warn(host->dev, "atmel_nand : multiple errors detected." - " Unable to correct.\n"); - return -EBADMSG; - } - - /* if there's a single bit error : we can correct it */ - if (ecc_status & ATMEL_ECC_ECCERR) { - /* there's nothing much to do here. - * the bit error is on the ECC itself. - */ - dev_warn(host->dev, "atmel_nand : one bit error on ECC code." - " Nothing to correct\n"); - return 0; - } - - dev_warn(host->dev, "atmel_nand : one bit error on data." - " (word offset in the page :" - " 0x%x bit offset : 0x%x)\n", - ecc_word, ecc_bit); - /* correct the error */ - if (nand_chip->options & NAND_BUSWIDTH_16) { - /* 16 bits words */ - ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); - } else { - /* 8 bits words */ - dat[ecc_word] ^= (1 << ecc_bit); - } - dev_warn(host->dev, "atmel_nand : error corrected\n"); - return 1; -} - -/* - * Enable HW ECC : unused on most chips - */ -static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) -{ -} - -int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd) -{ - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.calculate = atmel_nand_calculate; - nand->ecc.correct = atmel_nand_correct; - nand->ecc.hwctl = atmel_nand_hwctl; - nand->ecc.read_page = atmel_nand_read_page; - nand->ecc.bytes = 4; - nand->ecc.strength = 4; - - if (nand->ecc.mode == NAND_ECC_HW) { - /* ECC is calculated for the whole page (1 step) */ - nand->ecc.size = mtd->writesize; - - /* set ECC page size and oob layout */ - switch (mtd->writesize) { - case 512: - nand->ecc.layout = &atmel_oobinfo_small; - ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, - ATMEL_ECC_PAGESIZE_528); - break; - case 1024: - nand->ecc.layout = &atmel_oobinfo_large; - ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, - ATMEL_ECC_PAGESIZE_1056); - break; - case 2048: - nand->ecc.layout = &atmel_oobinfo_large; - ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, - ATMEL_ECC_PAGESIZE_2112); - break; - case 4096: - nand->ecc.layout = &atmel_oobinfo_large; - ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, - ATMEL_ECC_PAGESIZE_4224); - break; - default: - /* page size not handled by HW ECC */ - /* switching back to soft ECC */ - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.calculate = NULL; - nand->ecc.correct = NULL; - nand->ecc.hwctl = NULL; - nand->ecc.read_page = NULL; - nand->ecc.postpad = 0; - nand->ecc.prepad = 0; - nand->ecc.bytes = 0; - break; - } - } - - return 0; -} - -#endif /* CONFIG_ATMEL_NAND_HW_PMECC */ - -#endif /* CONFIG_ATMEL_NAND_HWECC */ - -static void at91_nand_hwcontrol(struct mtd_info *mtd, - int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - if (ctrl & NAND_CTRL_CHANGE) { - ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; - IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE - | CONFIG_SYS_NAND_MASK_CLE); - - if (ctrl & NAND_CLE) - IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE; - if (ctrl & NAND_ALE) - IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE; - -#ifdef CONFIG_SYS_NAND_ENABLE_PIN - at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN, - !(ctrl & NAND_NCE)); -#endif - this->IO_ADDR_W = (void *) IO_ADDR_W; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -#ifdef CONFIG_SYS_NAND_READY_PIN -static int at91_nand_ready(struct mtd_info *mtd) -{ - return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN); -} -#endif - -#ifdef CONFIG_SPL_BUILD -/* The following code is for SPL */ -static struct mtd_info *mtd; -static struct nand_chip nand_chip; - -static int nand_command(int block, int page, uint32_t offs, u8 cmd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; - void (*hwctrl)(struct mtd_info *mtd, int cmd, - unsigned int ctrl) = this->cmd_ctrl; - - while (!this->dev_ready(mtd)) - ; - - if (cmd == NAND_CMD_READOOB) { - offs += CONFIG_SYS_NAND_PAGE_SIZE; - cmd = NAND_CMD_READ0; - } - - hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - - if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) - offs >>= 1; - - hwctrl(mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE); - hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); - hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); - hwctrl(mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE); -#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE - hwctrl(mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE); -#endif - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - hwctrl(mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - while (!this->dev_ready(mtd)) - ; - - return 0; -} - -static int nand_is_bad_block(int block) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB); - - if (this->options & NAND_BUSWIDTH_16) { - if (readw(this->IO_ADDR_R) != 0xffff) - return 1; - } else { - if (readb(this->IO_ADDR_R) != 0xff) - return 1; - } - - return 0; -} - -#ifdef CONFIG_SPL_NAND_ECC -static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; -#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) - -static int nand_read_page(int block, int page, void *dst) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ecc_calc[ECCTOTAL]; - u_char ecc_code[ECCTOTAL]; - u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; - int eccsize = CONFIG_SYS_NAND_ECCSIZE; - int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = ECCSTEPS; - int i; - uint8_t *p = dst; - nand_command(block, page, 0, NAND_CMD_READ0); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - if (this->ecc.mode != NAND_ECC_SOFT) - this->ecc.hwctl(mtd, NAND_ECC_READ); - this->read_buf(mtd, p, eccsize); - this->ecc.calculate(mtd, p, &ecc_calc[i]); - } - this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); - - for (i = 0; i < ECCTOTAL; i++) - ecc_code[i] = oob_data[nand_ecc_pos[i]]; - - eccsteps = ECCSTEPS; - p = dst; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - - return 0; -} - -int spl_nand_erase_one(int block, int page) -{ - struct nand_chip *this = mtd_to_nand(mtd); - void (*hwctrl)(struct mtd_info *mtd, int cmd, - unsigned int ctrl) = this->cmd_ctrl; - int page_addr; - - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, 0); - - page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; - hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - /* Row address */ - hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE); - hwctrl(mtd, ((page_addr >> 8) & 0xff), - NAND_CTRL_ALE | NAND_CTRL_CHANGE); -#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE - /* One more address cycle for devices > 128MiB */ - hwctrl(mtd, (page_addr >> 16) & 0x0f, - NAND_CTRL_ALE | NAND_CTRL_CHANGE); -#endif - hwctrl(mtd, NAND_CMD_ERASE2, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - - while (!this->dev_ready(mtd)) - ; - - nand_deselect(); - - return 0; -} -#else -static int nand_read_page(int block, int page, void *dst) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - nand_command(block, page, 0, NAND_CMD_READ0); - atmel_nand_pmecc_read_page(mtd, this, dst, 0, page); - - return 0; -} -#endif /* CONFIG_SPL_NAND_ECC */ - -int at91_nand_wait_ready(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - udelay(this->chip_delay); - - return 1; -} - -int board_nand_init(struct nand_chip *nand) -{ - int ret = 0; - - nand->ecc.mode = NAND_ECC_SOFT; -#ifdef CONFIG_SYS_NAND_DBW_16 - nand->options = NAND_BUSWIDTH_16; - nand->read_buf = nand_read_buf16; -#else - nand->read_buf = nand_read_buf; -#endif - nand->cmd_ctrl = at91_nand_hwcontrol; -#ifdef CONFIG_SYS_NAND_READY_PIN - nand->dev_ready = at91_nand_ready; -#else - nand->dev_ready = at91_nand_wait_ready; -#endif - nand->chip_delay = 20; -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - nand->bbt_options |= NAND_BBT_USE_FLASH; -#endif - -#ifdef CONFIG_ATMEL_NAND_HWECC -#ifdef CONFIG_ATMEL_NAND_HW_PMECC - ret = atmel_pmecc_nand_init_params(nand, mtd); -#endif -#endif - - return ret; -} - -void nand_init(void) -{ - mtd = nand_to_mtd(&nand_chip); - mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE; - mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE; - nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; - nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; - board_nand_init(&nand_chip); - -#ifdef CONFIG_SPL_NAND_ECC - if (nand_chip.ecc.mode == NAND_ECC_SOFT) { - nand_chip.ecc.calculate = nand_calculate_ecc; - nand_chip.ecc.correct = nand_correct_data; - } -#endif - - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, 0); -} - -void nand_deselect(void) -{ - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, -1); -} - -#include "nand_spl_loaders.c" - -#else - -#ifndef CONFIG_SYS_NAND_BASE_LIST -#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } -#endif -static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; -static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; - -int atmel_nand_chip_init(int devnum, ulong base_addr) -{ - int ret; - struct nand_chip *nand = &nand_chip[devnum]; - struct mtd_info *mtd = nand_to_mtd(nand); - - nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; - -#ifdef CONFIG_NAND_ECC_BCH - nand->ecc.mode = NAND_ECC_SOFT_BCH; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif -#ifdef CONFIG_SYS_NAND_DBW_16 - nand->options = NAND_BUSWIDTH_16; -#endif - nand->cmd_ctrl = at91_nand_hwcontrol; -#ifdef CONFIG_SYS_NAND_READY_PIN - nand->dev_ready = at91_nand_ready; -#endif - nand->chip_delay = 75; -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - nand->bbt_options |= NAND_BBT_USE_FLASH; -#endif - - ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); - if (ret) - return ret; - -#ifdef CONFIG_ATMEL_NAND_HWECC -#ifdef CONFIG_ATMEL_NAND_HW_PMECC - ret = atmel_pmecc_nand_init_params(nand, mtd); -#else - ret = atmel_hwecc_nand_init_param(nand, mtd); -#endif - if (ret) - return ret; -#endif - - ret = nand_scan_tail(mtd); - if (!ret) - nand_register(devnum, mtd); - - return ret; -} - -void board_nand_init(void) -{ - int i; - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) - if (atmel_nand_chip_init(i, base_addr[i])) - dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip", - i); -} -#endif /* CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h deleted file mode 100644 index 05eeedb3f8..0000000000 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Error Corrected Code Controller (ECC) - System peripherals regsters. - * Based on AT91SAM9260 datasheet revision B. - */ - -#ifndef ATMEL_NAND_ECC_H -#define ATMEL_NAND_ECC_H - -#define ATMEL_ECC_CR 0x00 /* Control register */ -#define ATMEL_ECC_RST (1 << 0) /* Reset parity */ - -#define ATMEL_ECC_MR 0x04 /* Mode register */ -#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */ -#define ATMEL_ECC_PAGESIZE_528 (0) -#define ATMEL_ECC_PAGESIZE_1056 (1) -#define ATMEL_ECC_PAGESIZE_2112 (2) -#define ATMEL_ECC_PAGESIZE_4224 (3) - -#define ATMEL_ECC_SR 0x08 /* Status register */ -#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */ -#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */ -#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */ - -#define ATMEL_ECC_PR 0x0c /* Parity register */ -#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */ -#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */ - -#define ATMEL_ECC_NPR 0x10 /* NParity register */ -#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ - -/* Register access macros for PMECC */ -#define pmecc_readl(addr, reg) \ - readl(&addr->reg) - -#define pmecc_readb(addr, reg) \ - readb(&addr->reg) - -#define pmecc_writel(addr, reg, value) \ - writel((value), &addr->reg) - -/* PMECC Register Definitions */ -#define PMECC_MAX_SECTOR_NUM 8 -struct pmecc_regs { - u32 cfg; /* 0x00 PMECC Configuration Register */ - u32 sarea; /* 0x04 PMECC Spare Area Size Register */ - u32 saddr; /* 0x08 PMECC Start Address Register */ - u32 eaddr; /* 0x0C PMECC End Address Register */ - u32 clk; /* 0x10 PMECC Clock Control Register */ - u32 ctrl; /* 0x14 PMECC Control Register */ - u32 sr; /* 0x18 PMECC Status Register */ - u32 ier; /* 0x1C PMECC Interrupt Enable Register */ - u32 idr; /* 0x20 PMECC Interrupt Disable Register */ - u32 imr; /* 0x24 PMECC Interrupt Mask Register */ - u32 isr; /* 0x28 PMECC Interrupt Status Register */ - u32 reserved0[5]; /* 0x2C-0x3C Reserved */ - - /* 0x40 + sector_num * (0x40), Redundancy Registers */ - struct { -#ifdef CONFIG_SAMA5D2 - u8 ecc[56]; /* PMECC Generated Redundancy Byte Per Sector */ - u32 reserved1[2]; -#else - u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */ - u32 reserved1[5]; -#endif - } ecc_port[PMECC_MAX_SECTOR_NUM]; - - /* 0x240 + sector_num * (0x40) Remainder Registers */ - struct { -#ifdef CONFIG_SAMA5D2 - u32 rem[16]; -#else - u32 rem[12]; - u32 reserved2[4]; -#endif - } rem_port[PMECC_MAX_SECTOR_NUM]; - u32 reserved3[16]; /* 0x440-0x47C Reserved */ -}; - -/* For PMECC Configuration Register */ -#define PMECC_CFG_BCH_ERR2 (0 << 0) -#define PMECC_CFG_BCH_ERR4 (1 << 0) -#define PMECC_CFG_BCH_ERR8 (2 << 0) -#define PMECC_CFG_BCH_ERR12 (3 << 0) -#define PMECC_CFG_BCH_ERR24 (4 << 0) -#define PMECC_CFG_BCH_ERR32 (5 << 0) - -#define PMECC_CFG_SECTOR512 (0 << 4) -#define PMECC_CFG_SECTOR1024 (1 << 4) - -#define PMECC_CFG_PAGE_1SECTOR (0 << 8) -#define PMECC_CFG_PAGE_2SECTORS (1 << 8) -#define PMECC_CFG_PAGE_4SECTORS (2 << 8) -#define PMECC_CFG_PAGE_8SECTORS (3 << 8) - -#define PMECC_CFG_READ_OP (0 << 12) -#define PMECC_CFG_WRITE_OP (1 << 12) - -#define PMECC_CFG_SPARE_ENABLE (1 << 16) -#define PMECC_CFG_SPARE_DISABLE (0 << 16) - -#define PMECC_CFG_AUTO_ENABLE (1 << 20) -#define PMECC_CFG_AUTO_DISABLE (0 << 20) - -/* For PMECC Clock Control Register */ -#define PMECC_CLK_133MHZ (2 << 0) - -/* For PMECC Control Register */ -#define PMECC_CTRL_RST (1 << 0) -#define PMECC_CTRL_DATA (1 << 1) -#define PMECC_CTRL_USER (1 << 2) -#define PMECC_CTRL_ENABLE (1 << 4) -#define PMECC_CTRL_DISABLE (1 << 5) - -/* For PMECC Status Register */ -#define PMECC_SR_BUSY (1 << 0) -#define PMECC_SR_ENABLE (1 << 4) - -/* PMERRLOC Register Definitions */ -struct pmecc_errloc_regs { - u32 elcfg; /* 0x00 Error Location Configuration Register */ - u32 elprim; /* 0x04 Error Location Primitive Register */ - u32 elen; /* 0x08 Error Location Enable Register */ - u32 eldis; /* 0x0C Error Location Disable Register */ - u32 elsr; /* 0x10 Error Location Status Register */ - u32 elier; /* 0x14 Error Location Interrupt Enable Register */ - u32 elidr; /* 0x08 Error Location Interrupt Disable Register */ - u32 elimr; /* 0x0C Error Location Interrupt Mask Register */ - u32 elisr; /* 0x20 Error Location Interrupt Status Register */ - u32 reserved0; /* 0x24 Reserved */ -#ifdef CONFIG_SAMA5D2 - u32 sigma[33]; /* 0x28-0xA8 Error Location Sigma Registers */ - u32 el[32]; /* 0xAC-0x128 Error Location Registers */ - - /* - * 0x12C-0x1FC: - * Reserved for SAMA5D2. - */ - u32 reserved1[53]; -#else - u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */ - u32 el[24]; /* 0x8C-0xE8 Error Location Registers */ - u32 reserved1[5]; /* 0xEC-0xFC Reserved */ -#endif - - /* - * SAMA5 chip HSMC registers start here. But for 9X5 chip it is just - * reserved. - * - * Offset 0x00-0xF8: - */ - u32 reserved2[63]; - - /* - * Offset 0xFC: - * PMECC version for AT91SAM9X5, AT91SAM9N12. - * HSMC version for SAMA5D3, SAMA5D4. Can refer as PMECC version. - */ - u32 version; -}; - -/* For Error Location Configuration Register */ -#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) -#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) -#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) - -/* For Error Location Disable Register */ -#define PMERRLOC_DISABLE (1 << 0) - -/* For Error Location Interrupt Status Register */ -#ifdef CONFIG_SAMA5D2 -#define PMERRLOC_ERR_NUM_MASK (0x3f << 8) -#else -#define PMERRLOC_ERR_NUM_MASK (0x1f << 8) -#endif - -#define PMERRLOC_CALC_DONE (1 << 0) - -/* PMECC IP version */ -#define PMECC_VERSION_SAMA5D2 0x210 -#define PMECC_VERSION_SAMA5D4 0x113 -#define PMECC_VERSION_SAMA5D3 0x112 -#define PMECC_VERSION_AT91SAM9N12 0x102 -#define PMECC_VERSION_AT91SAM9X5 0x101 - -/* Galois field dimension */ -#define PMECC_GF_DIMENSION_13 13 -#define PMECC_GF_DIMENSION_14 14 - -/* Primitive Polynomial used by PMECC */ -#define PMECC_GF_13_PRIMITIVE_POLY 0x201b -#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 - -#define PMECC_INDEX_TABLE_SIZE_512 0x2000 -#define PMECC_INDEX_TABLE_SIZE_1024 0x4000 - -#define PMECC_MAX_TIMEOUT_US (100 * 1000) - -/* Reserved bytes in oob area */ -#define PMECC_OOB_RESERVED_BYTES 2 - -#endif diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c deleted file mode 100644 index 305e68ad49..0000000000 --- a/drivers/mtd/nand/davinci_nand.c +++ /dev/null @@ -1,833 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * NAND driver for TI DaVinci based boards. - * - * Copyright (C) 2007 Sergey Kubushyn - * - * Based on Linux DaVinci NAND driver by TI. Original copyright follows: - */ - -/* - * - * linux/drivers/mtd/nand/nand_davinci.c - * - * NAND Flash Driver - * - * Copyright (C) 2006 Texas Instruments. - * - * ---------------------------------------------------------------------------- - * - * ---------------------------------------------------------------------------- - * - * Overview: - * This is a device driver for the NAND flash device found on the - * DaVinci board which utilizes the Samsung k9k2g08 part. - * - Modifications: - ver. 1.0: Feb 2005, Vinod/Sudhakar - - - */ - -#include -#include -#include -#include - -/* Definitions for 4-bit hardware ECC */ -#define NAND_TIMEOUT 10240 -#define NAND_ECC_BUSY 0xC -#define NAND_4BITECC_MASK 0x03FF03FF -#define EMIF_NANDFSR_ECC_STATE_MASK 0x00000F00 -#define ECC_STATE_NO_ERR 0x0 -#define ECC_STATE_TOO_MANY_ERRS 0x1 -#define ECC_STATE_ERR_CORR_COMP_P 0x2 -#define ECC_STATE_ERR_CORR_COMP_N 0x3 - -/* - * Exploit the little endianness of the ARM to do multi-byte transfers - * per device read. This can perform over twice as quickly as individual - * byte transfers when buffer alignment is conducive. - * - * NOTE: This only works if the NAND is not connected to the 2 LSBs of - * the address bus. On Davinci EVM platforms this has always been true. - */ -static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - const u32 *nand = chip->IO_ADDR_R; - - /* Make sure that buf is 32 bit aligned */ - if (((int)buf & 0x3) != 0) { - if (((int)buf & 0x1) != 0) { - if (len) { - *buf = readb(nand); - buf += 1; - len--; - } - } - - if (((int)buf & 0x3) != 0) { - if (len >= 2) { - *(u16 *)buf = readw(nand); - buf += 2; - len -= 2; - } - } - } - - /* copy aligned data */ - while (len >= 4) { - *(u32 *)buf = __raw_readl(nand); - buf += 4; - len -= 4; - } - - /* mop up any remaining bytes */ - if (len) { - if (len >= 2) { - *(u16 *)buf = readw(nand); - buf += 2; - len -= 2; - } - - if (len) - *buf = readb(nand); - } -} - -static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - const u32 *nand = chip->IO_ADDR_W; - - /* Make sure that buf is 32 bit aligned */ - if (((int)buf & 0x3) != 0) { - if (((int)buf & 0x1) != 0) { - if (len) { - writeb(*buf, nand); - buf += 1; - len--; - } - } - - if (((int)buf & 0x3) != 0) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - } - } - - /* copy aligned data */ - while (len >= 4) { - __raw_writel(*(u32 *)buf, nand); - buf += 4; - len -= 4; - } - - /* mop up any remaining bytes */ - if (len) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - - if (len) - writeb(*buf, nand); - } -} - -static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W; - - if (ctrl & NAND_CTRL_CHANGE) { - IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); - - if (ctrl & NAND_CLE) - IO_ADDR_W |= MASK_CLE; - if (ctrl & NAND_ALE) - IO_ADDR_W |= MASK_ALE; - this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, IO_ADDR_W); -} - -#ifdef CONFIG_SYS_NAND_HW_ECC - -static u_int32_t nand_davinci_readecc(struct mtd_info *mtd) -{ - u_int32_t ecc = 0; - - ecc = __raw_readl(&(davinci_emif_regs->nandfecc[ - CONFIG_SYS_NAND_CS - 2])); - - return ecc; -} - -static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) -{ - u_int32_t val; - - /* reading the ECC result register resets the ECC calculation */ - nand_davinci_readecc(mtd); - - val = __raw_readl(&davinci_emif_regs->nandfcr); - val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); - val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS); - __raw_writel(val, &davinci_emif_regs->nandfcr); -} - -static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - u_int32_t tmp; - - tmp = nand_davinci_readecc(mtd); - - /* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits - * and shifting. RESERVED bits are 31 to 28 and 15 to 12. */ - tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4); - - /* Invert so that erased block ECC is correct */ - tmp = ~tmp; - - *ecc_code++ = tmp; - *ecc_code++ = tmp >> 8; - *ecc_code++ = tmp >> 16; - - /* NOTE: the above code matches mainline Linux: - * .PQR.stu ==> ~PQRstu - * - * MontaVista/TI kernels encode those bytes differently, use - * complicated (and allegedly sometimes-wrong) correction code, - * and usually shipped with U-Boot that uses software ECC: - * .PQR.stu ==> PsQRtu - * - * If you need MV/TI compatible NAND I/O in U-Boot, it should - * be possible to (a) change the mangling above, (b) reverse - * that mangling in nand_davinci_correct_data() below. - */ - - return 0; -} - -static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) | - (read_ecc[2] << 16); - u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) | - (calc_ecc[2] << 16); - u_int32_t diff = ecc_calc ^ ecc_nand; - - if (diff) { - if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { - /* Correctable error */ - if ((diff >> (12 + 3)) < this->ecc.size) { - uint8_t find_bit = 1 << ((diff >> 12) & 7); - uint32_t find_byte = diff >> (12 + 3); - - dat[find_byte] ^= find_bit; - pr_debug("Correcting single " - "bit ECC error at offset: %d, bit: " - "%d\n", find_byte, find_bit); - return 1; - } else { - return -EBADMSG; - } - } else if (!(diff & (diff - 1))) { - /* Single bit ECC error in the ECC itself, - nothing to fix */ - pr_debug("Single bit ECC error in " "ECC.\n"); - return 1; - } else { - /* Uncorrectable error */ - pr_debug("ECC UNCORRECTED_ERROR 1\n"); - return -EBADMSG; - } - } - return 0; -} -#endif /* CONFIG_SYS_NAND_HW_ECC */ - -#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST -static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { -#if defined(CONFIG_SYS_NAND_PAGE_2K) - .eccbytes = 40, -#ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - }, - .oobfree = { - {2, 4}, {16, 6}, {32, 6}, {48, 6}, - }, -#else - .eccpos = { - 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, - 59, 60, 61, 62, 63, - }, - .oobfree = { - {.offset = 2, .length = 22, }, - }, -#endif /* #ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC */ -#elif defined(CONFIG_SYS_NAND_PAGE_4K) - .eccbytes = 80, - .eccpos = { - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, - 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - }, - .oobfree = { - {.offset = 2, .length = 46, }, - }, -#endif -}; - -#if defined CONFIG_KEYSTONE_RBL_NAND -static struct nand_ecclayout nand_keystone_rbl_4bit_layout_oobfirst = { -#if defined(CONFIG_SYS_NAND_PAGE_2K) - .eccbytes = 40, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - }, - .oobfree = { - {.offset = 2, .length = 4, }, - {.offset = 16, .length = 6, }, - {.offset = 32, .length = 6, }, - {.offset = 48, .length = 6, }, - }, -#elif defined(CONFIG_SYS_NAND_PAGE_4K) - .eccbytes = 80, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - }, - .oobfree = { - {.offset = 2, .length = 4, }, - {.offset = 16, .length = 6, }, - {.offset = 32, .length = 6, }, - {.offset = 48, .length = 6, }, - {.offset = 64, .length = 6, }, - {.offset = 80, .length = 6, }, - {.offset = 96, .length = 6, }, - {.offset = 112, .length = 6, }, - }, -#endif -}; - -#ifdef CONFIG_SYS_NAND_PAGE_2K -#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 11 -#elif defined(CONFIG_SYS_NAND_PAGE_4K) -#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 12 -#endif - -/** - * nand_davinci_write_page - write one page - * @mtd: MTD device structure - * @chip: NAND chip descriptor - * @buf: the data to write - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * @raw: use _raw version of write_page - */ -static int nand_davinci_write_page(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, int data_len, - const uint8_t *buf, int oob_required, - int page, int raw) -{ - int status; - int ret = 0; - struct nand_ecclayout *saved_ecc_layout; - - /* save current ECC layout and assign Keystone RBL ECC layout */ - if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { - saved_ecc_layout = chip->ecc.layout; - chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; - mtd->oobavail = chip->ecc.layout->oobavail; - } - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) { - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required, page); - } else { - status = chip->ecc.write_page(mtd, chip, buf, - oob_required, page); - } - - if (status < 0) { - ret = status; - goto err; - } - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - if (status & NAND_STATUS_FAIL) { - ret = -EIO; - goto err; - } - -err: - /* restore ECC layout */ - if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { - chip->ecc.layout = saved_ecc_layout; - mtd->oobavail = saved_ecc_layout->oobavail; - } - - return ret; -} - -/** - * nand_davinci_read_page_hwecc - hardware ECC based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Not for syndrome calculating ECC controllers which need a special oob layout. - */ -static int nand_davinci_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint32_t *eccpos; - uint8_t *p = buf; - uint8_t *ecc_code = chip->buffers->ecccode; - uint8_t *ecc_calc = chip->buffers->ecccalc; - struct nand_ecclayout *saved_ecc_layout = chip->ecc.layout; - - /* save current ECC layout and assign Keystone RBL ECC layout */ - if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { - chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; - mtd->oobavail = chip->ecc.layout->oobavail; - } - - eccpos = chip->ecc.layout->eccpos; - - /* Read the OOB area first */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - - /* restore ECC layout */ - if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { - chip->ecc.layout = saved_ecc_layout; - mtd->oobavail = saved_ecc_layout->oobavail; - } - - return 0; -} -#endif /* CONFIG_KEYSTONE_RBL_NAND */ - -static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) -{ - u32 val; - - switch (mode) { - case NAND_ECC_WRITE: - case NAND_ECC_READ: - /* - * Start a new ECC calculation for reading or writing 512 bytes - * of data. - */ - val = __raw_readl(&davinci_emif_regs->nandfcr); - val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK; - val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); - val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS); - val |= DAVINCI_NANDFCR_4BIT_ECC_START; - __raw_writel(val, &davinci_emif_regs->nandfcr); - break; - case NAND_ECC_READSYN: - val = __raw_readl(&davinci_emif_regs->nand4bitecc[0]); - break; - default: - break; - } -} - -static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4]) -{ - int i; - - for (i = 0; i < 4; i++) { - ecc[i] = __raw_readl(&davinci_emif_regs->nand4bitecc[i]) & - NAND_4BITECC_MASK; - } - - return 0; -} - -static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd, - const uint8_t *dat, - uint8_t *ecc_code) -{ - unsigned int hw_4ecc[4]; - unsigned int i; - - nand_davinci_4bit_readecc(mtd, hw_4ecc); - - /*Convert 10 bit ecc value to 8 bit */ - for (i = 0; i < 2; i++) { - unsigned int hw_ecc_low = hw_4ecc[i * 2]; - unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1]; - - /* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */ - *ecc_code++ = hw_ecc_low & 0xFF; - - /* - * Take 2 bits as LSB bits from val1 (count1=0) or val5 - * (count1=1) and 6 bits from val2 (count1=0) or - * val5 (count1=1) - */ - *ecc_code++ = - ((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC); - - /* - * Take 4 bits from val2 (count1=0) or val5 (count1=1) and - * 4 bits from val3 (count1=0) or val6 (count1=1) - */ - *ecc_code++ = - ((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0); - - /* - * Take 6 bits from val3(count1=0) or val6 (count1=1) and - * 2 bits from val4 (count1=0) or val7 (count1=1) - */ - *ecc_code++ = - ((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0); - - /* Take 8 bits from val4 (count1=0) or val7 (count1=1) */ - *ecc_code++ = (hw_ecc_hi >> 18) & 0xFF; - } - - return 0; -} - -static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - int i; - unsigned int hw_4ecc[4]; - unsigned int iserror; - unsigned short *ecc16; - unsigned int numerrors, erroraddress, errorvalue; - u32 val; - - /* - * Check for an ECC where all bytes are 0xFF. If this is the case, we - * will assume we are looking at an erased page and we should ignore - * the ECC. - */ - for (i = 0; i < 10; i++) { - if (read_ecc[i] != 0xFF) - break; - } - if (i == 10) - return 0; - - /* Convert 8 bit in to 10 bit */ - ecc16 = (unsigned short *)&read_ecc[0]; - - /* - * Write the parity values in the NAND Flash 4-bit ECC Load register. - * Write each parity value one at a time starting from 4bit_ecc_val8 - * to 4bit_ecc_val1. - */ - - /*Take 2 bits from 8th byte and 8 bits from 9th byte */ - __raw_writel(((ecc16[4]) >> 6) & 0x3FF, - &davinci_emif_regs->nand4biteccload); - - /* Take 4 bits from 7th byte and 6 bits from 8th byte */ - __raw_writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0), - &davinci_emif_regs->nand4biteccload); - - /* Take 6 bits from 6th byte and 4 bits from 7th byte */ - __raw_writel((ecc16[3] >> 2) & 0x3FF, - &davinci_emif_regs->nand4biteccload); - - /* Take 8 bits from 5th byte and 2 bits from 6th byte */ - __raw_writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300), - &davinci_emif_regs->nand4biteccload); - - /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ - __raw_writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC), - &davinci_emif_regs->nand4biteccload); - - /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ - __raw_writel(((ecc16[1]) >> 4) & 0x3FF, - &davinci_emif_regs->nand4biteccload); - - /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ - __raw_writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0), - &davinci_emif_regs->nand4biteccload); - - /* Take 10 bits from 0th and 1st bytes */ - __raw_writel((ecc16[0]) & 0x3FF, - &davinci_emif_regs->nand4biteccload); - - /* - * Perform a dummy read to the EMIF Revision Code and Status register. - * This is required to ensure time for syndrome calculation after - * writing the ECC values in previous step. - */ - - val = __raw_readl(&davinci_emif_regs->nandfsr); - - /* - * Read the syndrome from the NAND Flash 4-Bit ECC 1-4 registers. - * A syndrome value of 0 means no bit errors. If the syndrome is - * non-zero then go further otherwise return. - */ - nand_davinci_4bit_readecc(mtd, hw_4ecc); - - if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3])) - return 0; - - /* - * Clear any previous address calculation by doing a dummy read of an - * error address register. - */ - val = __raw_readl(&davinci_emif_regs->nanderradd1); - - /* - * Set the addr_calc_st bit(bit no 13) in the NAND Flash Control - * register to 1. - */ - __raw_writel(DAVINCI_NANDFCR_4BIT_CALC_START, - &davinci_emif_regs->nandfcr); - - /* - * Wait for the corr_state field (bits 8 to 11) in the - * NAND Flash Status register to be not equal to 0x0, 0x1, 0x2, or 0x3. - * Otherwise ECC calculation has not even begun and the next loop might - * fail because of a false positive! - */ - i = NAND_TIMEOUT; - do { - val = __raw_readl(&davinci_emif_regs->nandfsr); - val &= 0xc00; - i--; - } while ((i > 0) && !val); - - /* - * Wait for the corr_state field (bits 8 to 11) in the - * NAND Flash Status register to be equal to 0x0, 0x1, 0x2, or 0x3. - */ - i = NAND_TIMEOUT; - do { - val = __raw_readl(&davinci_emif_regs->nandfsr); - val &= 0xc00; - i--; - } while ((i > 0) && val); - - iserror = __raw_readl(&davinci_emif_regs->nandfsr); - iserror &= EMIF_NANDFSR_ECC_STATE_MASK; - iserror = iserror >> 8; - - /* - * ECC_STATE_TOO_MANY_ERRS (0x1) means errors cannot be - * corrected (five or more errors). The number of errors - * calculated (err_num field) differs from the number of errors - * searched. ECC_STATE_ERR_CORR_COMP_P (0x2) means error - * correction complete (errors on bit 8 or 9). - * ECC_STATE_ERR_CORR_COMP_N (0x3) means error correction - * complete (error exists). - */ - - if (iserror == ECC_STATE_NO_ERR) { - val = __raw_readl(&davinci_emif_regs->nanderrval1); - return 0; - } else if (iserror == ECC_STATE_TOO_MANY_ERRS) { - val = __raw_readl(&davinci_emif_regs->nanderrval1); - return -EBADMSG; - } - - numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16) - & 0x3) + 1; - - /* Read the error address, error value and correct */ - for (i = 0; i < numerrors; i++) { - if (i > 1) { - erroraddress = - ((__raw_readl(&davinci_emif_regs->nanderradd2) >> - (16 * (i & 1))) & 0x3FF); - erroraddress = ((512 + 7) - erroraddress); - errorvalue = - ((__raw_readl(&davinci_emif_regs->nanderrval2) >> - (16 * (i & 1))) & 0xFF); - } else { - erroraddress = - ((__raw_readl(&davinci_emif_regs->nanderradd1) >> - (16 * (i & 1))) & 0x3FF); - erroraddress = ((512 + 7) - erroraddress); - errorvalue = - ((__raw_readl(&davinci_emif_regs->nanderrval1) >> - (16 * (i & 1))) & 0xFF); - } - /* xor the corrupt data with error value */ - if (erroraddress < 512) - dat[erroraddress] ^= errorvalue; - } - - return numerrors; -} -#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */ - -static int nand_davinci_dev_ready(struct mtd_info *mtd) -{ - return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1; -} - -static void nand_flash_init(void) -{ - /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS! - * Instead, have your board_init() set EMIF timings, based on its - * knowledge of the clocks and what devices are hooked up ... and - * don't even do that unless no UBL handled it. - */ -#ifdef CONFIG_SOC_DM644X - u_int32_t acfg1 = 0x3ffffffc; - - /*------------------------------------------------------------------* - * NAND FLASH CHIP TIMEOUT @ 459 MHz * - * * - * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz * - * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns * - * * - *------------------------------------------------------------------*/ - acfg1 = 0 - | (0 << 31) /* selectStrobe */ - | (0 << 30) /* extWait */ - | (1 << 26) /* writeSetup 10 ns */ - | (3 << 20) /* writeStrobe 40 ns */ - | (1 << 17) /* writeHold 10 ns */ - | (1 << 13) /* readSetup 10 ns */ - | (5 << 7) /* readStrobe 60 ns */ - | (1 << 4) /* readHold 10 ns */ - | (3 << 2) /* turnAround ?? ns */ - | (0 << 0) /* asyncSize 8-bit bus */ - ; - - __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */ - - /* NAND flash on CS2 */ - __raw_writel(0x00000101, &davinci_emif_regs->nandfcr); -#endif -} - -void davinci_nand_init(struct nand_chip *nand) -{ -#if defined CONFIG_KEYSTONE_RBL_NAND - int i; - struct nand_ecclayout *layout; - - layout = &nand_keystone_rbl_4bit_layout_oobfirst; - layout->oobavail = 0; - for (i = 0; layout->oobfree[i].length && - i < ARRAY_SIZE(layout->oobfree); i++) - layout->oobavail += layout->oobfree[i].length; - - nand->write_page = nand_davinci_write_page; - nand->ecc.read_page = nand_davinci_read_page_hwecc; -#endif - nand->chip_delay = 0; -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - nand->bbt_options |= NAND_BBT_USE_FLASH; -#endif -#ifdef CONFIG_SYS_NAND_NO_SUBPAGE_WRITE - nand->options |= NAND_NO_SUBPAGE_WRITE; -#endif -#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT - nand->options |= NAND_BUSWIDTH_16; -#endif -#ifdef CONFIG_SYS_NAND_HW_ECC - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - nand->ecc.calculate = nand_davinci_calculate_ecc; - nand->ecc.correct = nand_davinci_correct_data; - nand->ecc.hwctl = nand_davinci_enable_hwecc; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif /* CONFIG_SYS_NAND_HW_ECC */ -#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST - nand->ecc.mode = NAND_ECC_HW_OOB_FIRST; - nand->ecc.size = 512; - nand->ecc.bytes = 10; - nand->ecc.strength = 4; - nand->ecc.calculate = nand_davinci_4bit_calculate_ecc; - nand->ecc.correct = nand_davinci_4bit_correct_data; - nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc; - nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst; -#endif - /* Set address of hardware control function */ - nand->cmd_ctrl = nand_davinci_hwcontrol; - - nand->read_buf = nand_davinci_read_buf; - nand->write_buf = nand_davinci_write_buf; - - nand->dev_ready = nand_davinci_dev_ready; - - nand_flash_init(); -} - -int board_nand_init(struct nand_chip *chip) __attribute__((weak)); - -int board_nand_init(struct nand_chip *chip) -{ - davinci_nand_init(chip); - return 0; -} diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c deleted file mode 100644 index d1cac063f4..0000000000 --- a/drivers/mtd/nand/denali.c +++ /dev/null @@ -1,1371 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2014 Panasonic Corporation - * Copyright (C) 2013-2014, Altera Corporation - * Copyright (C) 2009-2010, Intel Corporation and its suppliers. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "denali.h" - -static dma_addr_t dma_map_single(void *dev, void *ptr, size_t size, - enum dma_data_direction dir) -{ - unsigned long addr = (unsigned long)ptr; - - size = ALIGN(size, ARCH_DMA_MINALIGN); - - if (dir == DMA_FROM_DEVICE) - invalidate_dcache_range(addr, addr + size); - else - flush_dcache_range(addr, addr + size); - - return addr; -} - -static void dma_unmap_single(void *dev, dma_addr_t addr, size_t size, - enum dma_data_direction dir) -{ - size = ALIGN(size, ARCH_DMA_MINALIGN); - - if (dir != DMA_TO_DEVICE) - invalidate_dcache_range(addr, addr + size); -} - -static int dma_mapping_error(void *dev, dma_addr_t addr) -{ - return 0; -} - -#define DENALI_NAND_NAME "denali-nand" - -/* for Indexed Addressing */ -#define DENALI_INDEXED_CTRL 0x00 -#define DENALI_INDEXED_DATA 0x10 - -#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ -#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ -#define DENALI_MAP10 (2 << 26) /* high-level control plane */ -#define DENALI_MAP11 (3 << 26) /* direct controller access */ - -/* MAP11 access cycle type */ -#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ -#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ -#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ - -/* MAP10 commands */ -#define DENALI_ERASE 0x01 - -#define DENALI_BANK(denali) ((denali)->active_bank << 24) - -#define DENALI_INVALID_BANK -1 -#define DENALI_NR_BANKS 4 - -/* - * The bus interface clock, clk_x, is phase aligned with the core clock. The - * clk_x is an integral multiple N of the core clk. The value N is configured - * at IP delivery time, and its available value is 4, 5, or 6. We need to align - * to the largest value to make it work with any possible configuration. - */ -#define DENALI_CLK_X_MULT 6 - -static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); -} - -/* - * Direct Addressing - the slave address forms the control information (command - * type, bank, block, and page address). The slave data is the actual data to - * be transferred. This mode requires 28 bits of address region allocated. - */ -static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr) -{ - return ioread32(denali->host + addr); -} - -static void denali_direct_write(struct denali_nand_info *denali, u32 addr, - u32 data) -{ - iowrite32(data, denali->host + addr); -} - -/* - * Indexed Addressing - address translation module intervenes in passing the - * control information. This mode reduces the required address range. The - * control information and transferred data are latched by the registers in - * the translation module. - */ -static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr) -{ - iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); - return ioread32(denali->host + DENALI_INDEXED_DATA); -} - -static void denali_indexed_write(struct denali_nand_info *denali, u32 addr, - u32 data) -{ - iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); - iowrite32(data, denali->host + DENALI_INDEXED_DATA); -} - -/* - * Use the configuration feature register to determine the maximum number of - * banks that the hardware supports. - */ -static void denali_detect_max_banks(struct denali_nand_info *denali) -{ - uint32_t features = ioread32(denali->reg + FEATURES); - - denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features); - - /* the encoding changed from rev 5.0 to 5.1 */ - if (denali->revision < 0x0501) - denali->max_banks <<= 1; -} - -static void __maybe_unused denali_enable_irq(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - iowrite32(U32_MAX, denali->reg + INTR_EN(i)); - iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); -} - -static void __maybe_unused denali_disable_irq(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - iowrite32(0, denali->reg + INTR_EN(i)); - iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); -} - -static void denali_clear_irq(struct denali_nand_info *denali, - int bank, uint32_t irq_status) -{ - /* write one to clear bits */ - iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); -} - -static void denali_clear_irq_all(struct denali_nand_info *denali) -{ - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) - denali_clear_irq(denali, i, U32_MAX); -} - -static void __denali_check_irq(struct denali_nand_info *denali) -{ - uint32_t irq_status; - int i; - - for (i = 0; i < DENALI_NR_BANKS; i++) { - irq_status = ioread32(denali->reg + INTR_STATUS(i)); - denali_clear_irq(denali, i, irq_status); - - if (i != denali->active_bank) - continue; - - denali->irq_status |= irq_status; - } -} - -static void denali_reset_irq(struct denali_nand_info *denali) -{ - denali->irq_status = 0; - denali->irq_mask = 0; -} - -static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, - uint32_t irq_mask) -{ - unsigned long time_left = 1000000; - - while (time_left) { - __denali_check_irq(denali); - - if (irq_mask & denali->irq_status) - return denali->irq_status; - udelay(1); - time_left--; - } - - if (!time_left) { - dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", - irq_mask); - return 0; - } - - return denali->irq_status; -} - -static uint32_t denali_check_irq(struct denali_nand_info *denali) -{ - __denali_check_irq(denali); - - return denali->irq_status; -} - -static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - int i; - - for (i = 0; i < len; i++) - buf[i] = denali->host_read(denali, addr); -} - -static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - int i; - - for (i = 0; i < len; i++) - denali->host_write(denali, addr, buf[i]); -} - -static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - uint16_t *buf16 = (uint16_t *)buf; - int i; - - for (i = 0; i < len / 2; i++) - buf16[i] = denali->host_read(denali, addr); -} - -static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); - const uint16_t *buf16 = (const uint16_t *)buf; - int i; - - for (i = 0; i < len / 2; i++) - denali->host_write(denali, addr, buf16[i]); -} - -static uint8_t denali_read_byte(struct mtd_info *mtd) -{ - uint8_t byte; - - denali_read_buf(mtd, &byte, 1); - - return byte; -} - -static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - denali_write_buf(mtd, &byte, 1); -} - -static uint16_t denali_read_word(struct mtd_info *mtd) -{ - uint16_t word; - - denali_read_buf16(mtd, (uint8_t *)&word, 2); - - return word; -} - -static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t type; - - if (ctrl & NAND_CLE) - type = DENALI_MAP11_CMD; - else if (ctrl & NAND_ALE) - type = DENALI_MAP11_ADDR; - else - return; - - /* - * Some commands are followed by chip->dev_ready or chip->waitfunc. - * irq_status must be cleared here to catch the R/B# interrupt later. - */ - if (ctrl & NAND_CTRL_CHANGE) - denali_reset_irq(denali); - - denali->host_write(denali, DENALI_BANK(denali) | type, dat); -} - -static int denali_dev_ready(struct mtd_info *mtd) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - return !!(denali_check_irq(denali) & INTR__INT_ACT); -} - -static int denali_check_erased_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - unsigned long uncor_ecc_flags, - unsigned int max_bitflips) -{ - uint8_t *ecc_code = chip->buffers->ecccode; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int i, ret, stat; - - ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, - chip->ecc.total); - if (ret) - return ret; - - for (i = 0; i < ecc_steps; i++) { - if (!(uncor_ecc_flags & BIT(i))) - continue; - - stat = nand_check_erased_ecc_chunk(buf, ecc_size, - ecc_code, ecc_bytes, - NULL, 0, - chip->ecc.strength); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - - buf += ecc_size; - ecc_code += ecc_bytes; - } - - return max_bitflips; -} - -static int denali_hw_ecc_fixup(struct mtd_info *mtd, - struct denali_nand_info *denali, - unsigned long *uncor_ecc_flags) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int bank = denali->active_bank; - uint32_t ecc_cor; - unsigned int max_bitflips; - - ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); - ecc_cor >>= ECC_COR_INFO__SHIFT(bank); - - if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { - /* - * This flag is set when uncorrectable error occurs at least in - * one ECC sector. We can not know "how many sectors", or - * "which sector(s)". We need erase-page check for all sectors. - */ - *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); - return 0; - } - - max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); - - /* - * The register holds the maximum of per-sector corrected bitflips. - * This is suitable for the return value of the ->read_page() callback. - * Unfortunately, we can not know the total number of corrected bits in - * the page. Increase the stats by max_bitflips. (compromised solution) - */ - mtd->ecc_stats.corrected += max_bitflips; - - return max_bitflips; -} - -static int denali_sw_ecc_fixup(struct mtd_info *mtd, - struct denali_nand_info *denali, - unsigned long *uncor_ecc_flags, uint8_t *buf) -{ - unsigned int ecc_size = denali->nand.ecc.size; - unsigned int bitflips = 0; - unsigned int max_bitflips = 0; - uint32_t err_addr, err_cor_info; - unsigned int err_byte, err_sector, err_device; - uint8_t err_cor_value; - unsigned int prev_sector = 0; - uint32_t irq_status; - - denali_reset_irq(denali); - - do { - err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); - err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); - err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); - - err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); - err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, - err_cor_info); - err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, - err_cor_info); - - /* reset the bitflip counter when crossing ECC sector */ - if (err_sector != prev_sector) - bitflips = 0; - - if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { - /* - * Check later if this is a real ECC error, or - * an erased sector. - */ - *uncor_ecc_flags |= BIT(err_sector); - } else if (err_byte < ecc_size) { - /* - * If err_byte is larger than ecc_size, means error - * happened in OOB, so we ignore it. It's no need for - * us to correct it err_device is represented the NAND - * error bits are happened in if there are more than - * one NAND connected. - */ - int offset; - unsigned int flips_in_byte; - - offset = (err_sector * ecc_size + err_byte) * - denali->devs_per_cs + err_device; - - /* correct the ECC error */ - flips_in_byte = hweight8(buf[offset] ^ err_cor_value); - buf[offset] ^= err_cor_value; - mtd->ecc_stats.corrected += flips_in_byte; - bitflips += flips_in_byte; - - max_bitflips = max(max_bitflips, bitflips); - } - - prev_sector = err_sector; - } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); - - /* - * Once handle all ECC errors, controller will trigger an - * ECC_TRANSACTION_DONE interrupt. - */ - irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); - if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) - return -EIO; - - return max_bitflips; -} - -static void denali_setup_dma64(struct denali_nand_info *denali, - dma_addr_t dma_addr, int page, int write) -{ - uint32_t mode; - const int page_count = 1; - - mode = DENALI_MAP10 | DENALI_BANK(denali) | page; - - /* DMA is a three step process */ - - /* - * 1. setup transfer type, interrupt when complete, - * burst len = 64 bytes, the number of pages - */ - denali->host_write(denali, mode, - 0x01002000 | (64 << 16) | (write << 8) | page_count); - - /* 2. set memory low address */ - denali->host_write(denali, mode, lower_32_bits(dma_addr)); - - /* 3. set memory high address */ - denali->host_write(denali, mode, upper_32_bits(dma_addr)); -} - -static void denali_setup_dma32(struct denali_nand_info *denali, - dma_addr_t dma_addr, int page, int write) -{ - uint32_t mode; - const int page_count = 1; - - mode = DENALI_MAP10 | DENALI_BANK(denali); - - /* DMA is a four step process */ - - /* 1. setup transfer type and # of pages */ - denali->host_write(denali, mode | page, - 0x2000 | (write << 8) | page_count); - - /* 2. set memory high address bits 23:8 */ - denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); - - /* 3. set memory low address bits 23:8 */ - denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); - - /* 4. interrupt when complete, burst len = 64 bytes */ - denali->host_write(denali, mode | 0x14000, 0x2400); -} - -static int denali_pio_read(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw) -{ - u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; - uint32_t *buf32 = (uint32_t *)buf; - uint32_t irq_status, ecc_err_mask; - int i; - - if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) - ecc_err_mask = INTR__ECC_UNCOR_ERR; - else - ecc_err_mask = INTR__ECC_ERR; - - denali_reset_irq(denali); - - for (i = 0; i < size / 4; i++) - *buf32++ = denali->host_read(denali, addr); - - irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); - if (!(irq_status & INTR__PAGE_XFER_INC)) - return -EIO; - - if (irq_status & INTR__ERASED_PAGE) - memset(buf, 0xff, size); - - return irq_status & ecc_err_mask ? -EBADMSG : 0; -} - -static int denali_pio_write(struct denali_nand_info *denali, - const void *buf, size_t size, int page, int raw) -{ - u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; - const uint32_t *buf32 = (uint32_t *)buf; - uint32_t irq_status; - int i; - - denali_reset_irq(denali); - - for (i = 0; i < size / 4; i++) - denali->host_write(denali, addr, *buf32++); - - irq_status = denali_wait_for_irq(denali, - INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); - if (!(irq_status & INTR__PROGRAM_COMP)) - return -EIO; - - return 0; -} - -static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - if (write) - return denali_pio_write(denali, buf, size, page, raw); - else - return denali_pio_read(denali, buf, size, page, raw); -} - -static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - dma_addr_t dma_addr; - uint32_t irq_mask, irq_status, ecc_err_mask; - enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - int ret = 0; - - dma_addr = dma_map_single(denali->dev, buf, size, dir); - if (dma_mapping_error(denali->dev, dma_addr)) { - dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); - return denali_pio_xfer(denali, buf, size, page, raw, write); - } - - if (write) { - /* - * INTR__PROGRAM_COMP is never asserted for the DMA transfer. - * We can use INTR__DMA_CMD_COMP instead. This flag is asserted - * when the page program is completed. - */ - irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; - ecc_err_mask = 0; - } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { - irq_mask = INTR__DMA_CMD_COMP; - ecc_err_mask = INTR__ECC_UNCOR_ERR; - } else { - irq_mask = INTR__DMA_CMD_COMP; - ecc_err_mask = INTR__ECC_ERR; - } - - iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); - - denali_reset_irq(denali); - denali->setup_dma(denali, dma_addr, page, write); - - irq_status = denali_wait_for_irq(denali, irq_mask); - if (!(irq_status & INTR__DMA_CMD_COMP)) - ret = -EIO; - else if (irq_status & ecc_err_mask) - ret = -EBADMSG; - - iowrite32(0, denali->reg + DMA_ENABLE); - - dma_unmap_single(denali->dev, dma_addr, size, dir); - - if (irq_status & INTR__ERASED_PAGE) - memset(buf, 0xff, size); - - return ret; -} - -static int denali_data_xfer(struct denali_nand_info *denali, void *buf, - size_t size, int page, int raw, int write) -{ - iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); - iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0, - denali->reg + TRANSFER_SPARE_REG); - - if (denali->dma_avail) - return denali_dma_xfer(denali, buf, size, page, raw, write); - else - return denali_pio_xfer(denali, buf, size, page, raw, write); -} - -static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, - int page, int write) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0; - unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT; - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - uint8_t *bufpoi = chip->oob_poi; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int i, pos, len; - - /* BBM at the beginning of the OOB area */ - chip->cmdfunc(mtd, start_cmd, writesize, page); - if (write) - chip->write_buf(mtd, bufpoi, oob_skip); - else - chip->read_buf(mtd, bufpoi, oob_skip); - bufpoi += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - chip->cmdfunc(mtd, rnd_cmd, pos, -1); - if (write) - chip->write_buf(mtd, bufpoi, len); - else - chip->read_buf(mtd, bufpoi, len); - bufpoi += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1); - if (write) - chip->write_buf(mtd, bufpoi, len); - else - chip->read_buf(mtd, bufpoi, len); - bufpoi += len; - } - } - - /* OOB free */ - len = oobsize - (bufpoi - chip->oob_poi); - chip->cmdfunc(mtd, rnd_cmd, size - len, -1); - if (write) - chip->write_buf(mtd, bufpoi, len); - else - chip->read_buf(mtd, bufpoi, len); -} - -static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - void *tmp_buf = denali->buf; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int ret, i, pos, len; - - ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0); - if (ret) - return ret; - - /* Arrange the buffer for syndrome payload/ecc layout */ - if (buf) { - for (i = 0; i < ecc_steps; i++) { - pos = i * (ecc_size + ecc_bytes); - len = ecc_size; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(buf, tmp_buf + pos, len); - buf += len; - if (len < ecc_size) { - len = ecc_size - len; - memcpy(buf, tmp_buf + writesize + oob_skip, - len); - buf += len; - } - } - } - - if (oob_required) { - uint8_t *oob = chip->oob_poi; - - /* BBM at the beginning of the OOB area */ - memcpy(oob, tmp_buf + writesize, oob_skip); - oob += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(oob, tmp_buf + pos, len); - oob += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - memcpy(oob, tmp_buf + writesize + oob_skip, - len); - oob += len; - } - } - - /* OOB free */ - len = oobsize - (oob - chip->oob_poi); - memcpy(oob, tmp_buf + size - len, len); - } - - return 0; -} - -static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - denali_oob_xfer(mtd, chip, page, 0); - - return 0; -} - -static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int status; - - denali_reset_irq(denali); - - denali_oob_xfer(mtd, chip, page, 1); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - unsigned long uncor_ecc_flags = 0; - int stat = 0; - int ret; - - ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0); - if (ret && ret != -EBADMSG) - return ret; - - if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) - stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags); - else if (ret == -EBADMSG) - stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf); - - if (stat < 0) - return stat; - - if (uncor_ecc_flags) { - ret = denali_read_oob(mtd, chip, page); - if (ret) - return ret; - - stat = denali_check_erased_page(mtd, chip, buf, - uncor_ecc_flags, stat); - } - - return stat; -} - -static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - int writesize = mtd->writesize; - int oobsize = mtd->oobsize; - int ecc_steps = chip->ecc.steps; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - void *tmp_buf = denali->buf; - int oob_skip = denali->oob_skip_bytes; - size_t size = writesize + oobsize; - int i, pos, len; - - /* - * Fill the buffer with 0xff first except the full page transfer. - * This simplifies the logic. - */ - if (!buf || !oob_required) - memset(tmp_buf, 0xff, size); - - /* Arrange the buffer for syndrome payload/ecc layout */ - if (buf) { - for (i = 0; i < ecc_steps; i++) { - pos = i * (ecc_size + ecc_bytes); - len = ecc_size; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(tmp_buf + pos, buf, len); - buf += len; - if (len < ecc_size) { - len = ecc_size - len; - memcpy(tmp_buf + writesize + oob_skip, buf, - len); - buf += len; - } - } - } - - if (oob_required) { - const uint8_t *oob = chip->oob_poi; - - /* BBM at the beginning of the OOB area */ - memcpy(tmp_buf + writesize, oob, oob_skip); - oob += oob_skip; - - /* OOB ECC */ - for (i = 0; i < ecc_steps; i++) { - pos = ecc_size + i * (ecc_size + ecc_bytes); - len = ecc_bytes; - - if (pos >= writesize) - pos += oob_skip; - else if (pos + len > writesize) - len = writesize - pos; - - memcpy(tmp_buf + pos, oob, len); - oob += len; - if (len < ecc_bytes) { - len = ecc_bytes - len; - memcpy(tmp_buf + writesize + oob_skip, oob, - len); - oob += len; - } - } - - /* OOB free */ - len = oobsize - (oob - chip->oob_poi); - memcpy(tmp_buf + size - len, oob, len); - } - - return denali_data_xfer(denali, tmp_buf, size, page, 1, 1); -} - -static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - return denali_data_xfer(denali, (void *)buf, mtd->writesize, - page, 0, 1); -} - -static void denali_select_chip(struct mtd_info *mtd, int chip) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - - denali->active_bank = chip; -} - -static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_status; - - /* R/B# pin transitioned from low to high? */ - irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); - - return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; -} - -static int denali_erase(struct mtd_info *mtd, int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t irq_status; - - denali_reset_irq(denali); - - denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, - DENALI_ERASE); - - /* wait for erase to complete or failure to occur */ - irq_status = denali_wait_for_irq(denali, - INTR__ERASE_COMP | INTR__ERASE_FAIL); - - return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL; -} - -static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, - const struct nand_data_interface *conf) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - const struct nand_sdr_timings *timings; - unsigned long t_clk; - int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; - int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; - int addr_2_data_mask; - uint32_t tmp; - - timings = nand_get_sdr_timings(conf); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - /* clk_x period in picoseconds */ - t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); - if (!t_clk) - return -EINVAL; - - if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) - return 0; - - /* tREA -> ACC_CLKS */ - acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); - acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); - - tmp = ioread32(denali->reg + ACC_CLKS); - tmp &= ~ACC_CLKS__VALUE; - tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); - iowrite32(tmp, denali->reg + ACC_CLKS); - - /* tRWH -> RE_2_WE */ - re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); - re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); - - tmp = ioread32(denali->reg + RE_2_WE); - tmp &= ~RE_2_WE__VALUE; - tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); - iowrite32(tmp, denali->reg + RE_2_WE); - - /* tRHZ -> RE_2_RE */ - re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); - re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); - - tmp = ioread32(denali->reg + RE_2_RE); - tmp &= ~RE_2_RE__VALUE; - tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); - iowrite32(tmp, denali->reg + RE_2_RE); - - /* - * tCCS, tWHR -> WE_2_RE - * - * With WE_2_RE properly set, the Denali controller automatically takes - * care of the delay; the driver need not set NAND_WAIT_TCCS. - */ - we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), - t_clk); - we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); - - tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); - tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; - tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); - iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); - - /* tADL -> ADDR_2_DATA */ - - /* for older versions, ADDR_2_DATA is only 6 bit wide */ - addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; - if (denali->revision < 0x0501) - addr_2_data_mask >>= 1; - - addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); - addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); - - tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); - tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; - tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); - iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); - - /* tREH, tWH -> RDWR_EN_HI_CNT */ - rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), - t_clk); - rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); - - tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); - tmp &= ~RDWR_EN_HI_CNT__VALUE; - tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); - iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); - - /* tRP, tWP -> RDWR_EN_LO_CNT */ - rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), - t_clk); - rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), - t_clk); - rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); - rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); - rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); - - tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); - tmp &= ~RDWR_EN_LO_CNT__VALUE; - tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); - iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); - - /* tCS, tCEA -> CS_SETUP_CNT */ - cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, - (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, - 0); - cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); - - tmp = ioread32(denali->reg + CS_SETUP_CNT); - tmp &= ~CS_SETUP_CNT__VALUE; - tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); - iowrite32(tmp, denali->reg + CS_SETUP_CNT); - - return 0; -} - -static void denali_reset_banks(struct denali_nand_info *denali) -{ - u32 irq_status; - int i; - - for (i = 0; i < denali->max_banks; i++) { - denali->active_bank = i; - - denali_reset_irq(denali); - - iowrite32(DEVICE_RESET__BANK(i), - denali->reg + DEVICE_RESET); - - irq_status = denali_wait_for_irq(denali, - INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); - if (!(irq_status & INTR__INT_ACT)) - break; - } - - dev_dbg(denali->dev, "%d chips connected\n", i); - denali->max_banks = i; -} - -static void denali_hw_init(struct denali_nand_info *denali) -{ - /* - * The REVISION register may not be reliable. Platforms are allowed to - * override it. - */ - if (!denali->revision) - denali->revision = swab16(ioread32(denali->reg + REVISION)); - - /* - * tell driver how many bit controller will skip before writing - * ECC code in OOB. This is normally used for bad block marker - */ - denali->oob_skip_bytes = CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES; - iowrite32(denali->oob_skip_bytes, denali->reg + SPARE_AREA_SKIP_BYTES); - denali_detect_max_banks(denali); - iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); - iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); - - iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); -} - -int denali_calc_ecc_bytes(int step_size, int strength) -{ - /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ - return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; -} -EXPORT_SYMBOL(denali_calc_ecc_bytes); - -static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, - struct denali_nand_info *denali) -{ - int oobavail = mtd->oobsize - denali->oob_skip_bytes; - int ret; - - /* - * If .size and .strength are already set (usually by DT), - * check if they are supported by this controller. - */ - if (chip->ecc.size && chip->ecc.strength) - return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); - - /* - * We want .size and .strength closest to the chip's requirement - * unless NAND_ECC_MAXIMIZE is requested. - */ - if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { - ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); - if (!ret) - return 0; - } - - /* Max ECC strength is the last thing we can do */ - return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); -} - -static struct nand_ecclayout nand_oob; - -static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = denali->oob_skip_bytes; - oobregion->length = chip->ecc.total; - - return 0; -} - -static int denali_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - struct nand_chip *chip = mtd_to_nand(mtd); - - if (section) - return -ERANGE; - - oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; - oobregion->length = mtd->oobsize - oobregion->offset; - - return 0; -} - -static const struct mtd_ooblayout_ops denali_ooblayout_ops = { - .ecc = denali_ooblayout_ecc, - .free = denali_ooblayout_free, -}; - -static int denali_multidev_fixup(struct denali_nand_info *denali) -{ - struct nand_chip *chip = &denali->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - - /* - * Support for multi device: - * When the IP configuration is x16 capable and two x8 chips are - * connected in parallel, DEVICES_CONNECTED should be set to 2. - * In this case, the core framework knows nothing about this fact, - * so we should tell it the _logical_ pagesize and anything necessary. - */ - denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); - - /* - * On some SoCs, DEVICES_CONNECTED is not auto-detected. - * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. - */ - if (denali->devs_per_cs == 0) { - denali->devs_per_cs = 1; - iowrite32(1, denali->reg + DEVICES_CONNECTED); - } - - if (denali->devs_per_cs == 1) - return 0; - - if (denali->devs_per_cs != 2) { - dev_err(denali->dev, "unsupported number of devices %d\n", - denali->devs_per_cs); - return -EINVAL; - } - - /* 2 chips in parallel */ - mtd->size <<= 1; - mtd->erasesize <<= 1; - mtd->writesize <<= 1; - mtd->oobsize <<= 1; - chip->chipsize <<= 1; - chip->page_shift += 1; - chip->phys_erase_shift += 1; - chip->bbt_erase_shift += 1; - chip->chip_shift += 1; - chip->pagemask <<= 1; - chip->ecc.size <<= 1; - chip->ecc.bytes <<= 1; - chip->ecc.strength <<= 1; - denali->oob_skip_bytes <<= 1; - - return 0; -} - -int denali_init(struct denali_nand_info *denali) -{ - struct nand_chip *chip = &denali->nand; - struct mtd_info *mtd = nand_to_mtd(chip); - u32 features = ioread32(denali->reg + FEATURES); - int ret; - - denali_hw_init(denali); - - denali_clear_irq_all(denali); - - denali_reset_banks(denali); - - denali->active_bank = DENALI_INVALID_BANK; - - chip->flash_node = dev_of_offset(denali->dev); - /* Fallback to the default name if DT did not give "label" property */ - if (!mtd->name) - mtd->name = "denali-nand"; - - chip->select_chip = denali_select_chip; - chip->read_byte = denali_read_byte; - chip->write_byte = denali_write_byte; - chip->read_word = denali_read_word; - chip->cmd_ctrl = denali_cmd_ctrl; - chip->dev_ready = denali_dev_ready; - chip->waitfunc = denali_waitfunc; - - if (features & FEATURES__INDEX_ADDR) { - denali->host_read = denali_indexed_read; - denali->host_write = denali_indexed_write; - } else { - denali->host_read = denali_direct_read; - denali->host_write = denali_direct_write; - } - - /* clk rate info is needed for setup_data_interface */ - if (denali->clk_x_rate) - chip->setup_data_interface = denali_setup_data_interface; - - ret = nand_scan_ident(mtd, denali->max_banks, NULL); - if (ret) - return ret; - - if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) - denali->dma_avail = 1; - - if (denali->dma_avail) { - chip->buf_align = ARCH_DMA_MINALIGN; - if (denali->caps & DENALI_CAP_DMA_64BIT) - denali->setup_dma = denali_setup_dma64; - else - denali->setup_dma = denali_setup_dma32; - } else { - chip->buf_align = 4; - } - - chip->options |= NAND_USE_BOUNCE_BUFFER; - chip->bbt_options |= NAND_BBT_USE_FLASH; - chip->bbt_options |= NAND_BBT_NO_OOB; - denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; - - /* no subpage writes on denali */ - chip->options |= NAND_NO_SUBPAGE_WRITE; - - ret = denali_ecc_setup(mtd, chip, denali); - if (ret) { - dev_err(denali->dev, "Failed to setup ECC settings.\n"); - return ret; - } - - dev_dbg(denali->dev, - "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", - chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); - - iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | - FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), - denali->reg + ECC_CORRECTION); - iowrite32(mtd->erasesize / mtd->writesize, - denali->reg + PAGES_PER_BLOCK); - iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, - denali->reg + DEVICE_WIDTH); - iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG, - denali->reg + TWO_ROW_ADDR_CYCLES); - iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); - iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); - - iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); - iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); - /* chip->ecc.steps is set by nand_scan_tail(); not available here */ - iowrite32(mtd->writesize / chip->ecc.size, - denali->reg + CFG_NUM_DATA_BLOCKS); - - mtd_set_ooblayout(mtd, &denali_ooblayout_ops); - - nand_oob.eccbytes = denali->nand.ecc.bytes; - denali->nand.ecc.layout = &nand_oob; - - if (chip->options & NAND_BUSWIDTH_16) { - chip->read_buf = denali_read_buf16; - chip->write_buf = denali_write_buf16; - } else { - chip->read_buf = denali_read_buf; - chip->write_buf = denali_write_buf; - } - chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; - chip->ecc.read_page = denali_read_page; - chip->ecc.read_page_raw = denali_read_page_raw; - chip->ecc.write_page = denali_write_page; - chip->ecc.write_page_raw = denali_write_page_raw; - chip->ecc.read_oob = denali_read_oob; - chip->ecc.write_oob = denali_write_oob; - chip->erase = denali_erase; - - ret = denali_multidev_fixup(denali); - if (ret) - return ret; - - /* - * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not - * use devm_kmalloc() because the memory allocated by devm_ does not - * guarantee DMA-safe alignment. - */ - denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!denali->buf) - return -ENOMEM; - - ret = nand_scan_tail(mtd); - if (ret) - goto free_buf; - - ret = nand_register(0, mtd); - if (ret) { - dev_err(denali->dev, "Failed to register MTD: %d\n", ret); - goto free_buf; - } - return 0; - -free_buf: - kfree(denali->buf); - - return ret; -} diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h deleted file mode 100644 index 9b797beffa..0000000000 --- a/drivers/mtd/nand/denali.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2013-2014 Altera Corporation - * Copyright (C) 2009-2010, Intel Corporation and its suppliers. - */ - -#ifndef __DENALI_H__ -#define __DENALI_H__ - -#include -#include -#include - -#define DEVICE_RESET 0x0 -#define DEVICE_RESET__BANK(bank) BIT(bank) - -#define TRANSFER_SPARE_REG 0x10 -#define TRANSFER_SPARE_REG__FLAG BIT(0) - -#define LOAD_WAIT_CNT 0x20 -#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0) - -#define PROGRAM_WAIT_CNT 0x30 -#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0) - -#define ERASE_WAIT_CNT 0x40 -#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0) - -#define INT_MON_CYCCNT 0x50 -#define INT_MON_CYCCNT__VALUE GENMASK(15, 0) - -#define RB_PIN_ENABLED 0x60 -#define RB_PIN_ENABLED__BANK(bank) BIT(bank) - -#define MULTIPLANE_OPERATION 0x70 -#define MULTIPLANE_OPERATION__FLAG BIT(0) - -#define MULTIPLANE_READ_ENABLE 0x80 -#define MULTIPLANE_READ_ENABLE__FLAG BIT(0) - -#define COPYBACK_DISABLE 0x90 -#define COPYBACK_DISABLE__FLAG BIT(0) - -#define CACHE_WRITE_ENABLE 0xa0 -#define CACHE_WRITE_ENABLE__FLAG BIT(0) - -#define CACHE_READ_ENABLE 0xb0 -#define CACHE_READ_ENABLE__FLAG BIT(0) - -#define PREFETCH_MODE 0xc0 -#define PREFETCH_MODE__PREFETCH_EN BIT(0) -#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4) - -#define CHIP_ENABLE_DONT_CARE 0xd0 -#define CHIP_EN_DONT_CARE__FLAG BIT(0) - -#define ECC_ENABLE 0xe0 -#define ECC_ENABLE__FLAG BIT(0) - -#define GLOBAL_INT_ENABLE 0xf0 -#define GLOBAL_INT_EN_FLAG BIT(0) - -#define TWHR2_AND_WE_2_RE 0x100 -#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0) -#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8) - -#define TCWAW_AND_ADDR_2_DATA 0x110 -/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */ -#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0) -#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8) - -#define RE_2_WE 0x120 -#define RE_2_WE__VALUE GENMASK(5, 0) - -#define ACC_CLKS 0x130 -#define ACC_CLKS__VALUE GENMASK(3, 0) - -#define NUMBER_OF_PLANES 0x140 -#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0) - -#define PAGES_PER_BLOCK 0x150 -#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0) - -#define DEVICE_WIDTH 0x160 -#define DEVICE_WIDTH__VALUE GENMASK(1, 0) - -#define DEVICE_MAIN_AREA_SIZE 0x170 -#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0) - -#define DEVICE_SPARE_AREA_SIZE 0x180 -#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0) - -#define TWO_ROW_ADDR_CYCLES 0x190 -#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0) - -#define MULTIPLANE_ADDR_RESTRICT 0x1a0 -#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0) - -#define ECC_CORRECTION 0x1b0 -#define ECC_CORRECTION__VALUE GENMASK(4, 0) -#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) - -#define READ_MODE 0x1c0 -#define READ_MODE__VALUE GENMASK(3, 0) - -#define WRITE_MODE 0x1d0 -#define WRITE_MODE__VALUE GENMASK(3, 0) - -#define COPYBACK_MODE 0x1e0 -#define COPYBACK_MODE__VALUE GENMASK(3, 0) - -#define RDWR_EN_LO_CNT 0x1f0 -#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0) - -#define RDWR_EN_HI_CNT 0x200 -#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0) - -#define MAX_RD_DELAY 0x210 -#define MAX_RD_DELAY__VALUE GENMASK(3, 0) - -#define CS_SETUP_CNT 0x220 -#define CS_SETUP_CNT__VALUE GENMASK(4, 0) -#define CS_SETUP_CNT__TWB GENMASK(17, 12) - -#define SPARE_AREA_SKIP_BYTES 0x230 -#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0) - -#define SPARE_AREA_MARKER 0x240 -#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0) - -#define DEVICES_CONNECTED 0x250 -#define DEVICES_CONNECTED__VALUE GENMASK(2, 0) - -#define DIE_MASK 0x260 -#define DIE_MASK__VALUE GENMASK(7, 0) - -#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 -#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0) - -#define WRITE_PROTECT 0x280 -#define WRITE_PROTECT__FLAG BIT(0) - -#define RE_2_RE 0x290 -#define RE_2_RE__VALUE GENMASK(5, 0) - -#define MANUFACTURER_ID 0x300 -#define MANUFACTURER_ID__VALUE GENMASK(7, 0) - -#define DEVICE_ID 0x310 -#define DEVICE_ID__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_0 0x320 -#define DEVICE_PARAM_0__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_1 0x330 -#define DEVICE_PARAM_1__VALUE GENMASK(7, 0) - -#define DEVICE_PARAM_2 0x340 -#define DEVICE_PARAM_2__VALUE GENMASK(7, 0) - -#define LOGICAL_PAGE_DATA_SIZE 0x350 -#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0) - -#define LOGICAL_PAGE_SPARE_SIZE 0x360 -#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0) - -#define REVISION 0x370 -#define REVISION__VALUE GENMASK(15, 0) - -#define ONFI_DEVICE_FEATURES 0x380 -#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0) - -#define ONFI_OPTIONAL_COMMANDS 0x390 -#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0) - -#define ONFI_TIMING_MODE 0x3a0 -#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0) - -#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 -#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0) - -#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 -#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0) -#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8) - -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0) - -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 -#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0) - -#define FEATURES 0x3f0 -#define FEATURES__N_BANKS GENMASK(1, 0) -#define FEATURES__ECC_MAX_ERR GENMASK(5, 2) -#define FEATURES__DMA BIT(6) -#define FEATURES__CMD_DMA BIT(7) -#define FEATURES__PARTITION BIT(8) -#define FEATURES__XDMA_SIDEBAND BIT(9) -#define FEATURES__GPREG BIT(10) -#define FEATURES__INDEX_ADDR BIT(11) - -#define TRANSFER_MODE 0x400 -#define TRANSFER_MODE__VALUE GENMASK(1, 0) - -#define INTR_STATUS(bank) (0x410 + (bank) * 0x50) -#define INTR_EN(bank) (0x420 + (bank) * 0x50) -/* bit[1:0] is used differently depending on IP version */ -#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */ -#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */ -#define INTR__ECC_ERR BIT(1) /* old IP */ -#define INTR__DMA_CMD_COMP BIT(2) -#define INTR__TIME_OUT BIT(3) -#define INTR__PROGRAM_FAIL BIT(4) -#define INTR__ERASE_FAIL BIT(5) -#define INTR__LOAD_COMP BIT(6) -#define INTR__PROGRAM_COMP BIT(7) -#define INTR__ERASE_COMP BIT(8) -#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9) -#define INTR__LOCKED_BLK BIT(10) -#define INTR__UNSUP_CMD BIT(11) -#define INTR__INT_ACT BIT(12) -#define INTR__RST_COMP BIT(13) -#define INTR__PIPE_CMD_ERR BIT(14) -#define INTR__PAGE_XFER_INC BIT(15) -#define INTR__ERASED_PAGE BIT(16) - -#define PAGE_CNT(bank) (0x430 + (bank) * 0x50) -#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50) -#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50) - -#define ECC_THRESHOLD 0x600 -#define ECC_THRESHOLD__VALUE GENMASK(9, 0) - -#define ECC_ERROR_BLOCK_ADDRESS 0x610 -#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0) - -#define ECC_ERROR_PAGE_ADDRESS 0x620 -#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0) -#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12) - -#define ECC_ERROR_ADDRESS 0x630 -#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) -#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12) - -#define ERR_CORRECTION_INFO 0x640 -#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0) -#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8) -#define ERR_CORRECTION_INFO__UNCOR BIT(14) -#define ERR_CORRECTION_INFO__LAST_ERR BIT(15) - -#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) -#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) -#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0) -#define ECC_COR_INFO__UNCOR_ERR BIT(7) - -#define CFG_DATA_BLOCK_SIZE 0x6b0 - -#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0 - -#define CFG_NUM_DATA_BLOCKS 0x6d0 - -#define CFG_META_DATA_SIZE 0x6e0 - -#define DMA_ENABLE 0x700 -#define DMA_ENABLE__FLAG BIT(0) - -#define IGNORE_ECC_DONE 0x710 -#define IGNORE_ECC_DONE__FLAG BIT(0) - -#define DMA_INTR 0x720 -#define DMA_INTR_EN 0x730 -#define DMA_INTR__TARGET_ERROR BIT(0) -#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1) -#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2) -#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3) -#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4) -#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5) - -#define TARGET_ERR_ADDR_LO 0x740 -#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0) - -#define TARGET_ERR_ADDR_HI 0x750 -#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0) - -#define CHNL_ACTIVE 0x760 -#define CHNL_ACTIVE__CHANNEL0 BIT(0) -#define CHNL_ACTIVE__CHANNEL1 BIT(1) -#define CHNL_ACTIVE__CHANNEL2 BIT(2) -#define CHNL_ACTIVE__CHANNEL3 BIT(3) - -struct udevice; - -struct denali_nand_info { - struct nand_chip nand; - unsigned long clk_x_rate; /* bus interface clock rate */ - int active_bank; /* currently selected bank */ - struct udevice *dev; - uint32_t page; - void __iomem *reg; /* Register Interface */ - void __iomem *host; /* Host Data/Command Interface */ - u32 irq_mask; /* interrupts we are waiting for */ - u32 irq_status; /* interrupts that have happened */ - int irq; - void *buf; /* for syndrome layout conversion */ - dma_addr_t dma_addr; - int dma_avail; /* can support DMA? */ - int devs_per_cs; /* devices connected in parallel */ - int oob_skip_bytes; /* number of bytes reserved for BBM */ - int max_banks; - unsigned int revision; /* IP revision */ - unsigned int caps; /* IP capability (or quirk) */ - const struct nand_ecc_caps *ecc_caps; - u32 (*host_read)(struct denali_nand_info *denali, u32 addr); - void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); - void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, - int page, int write); -}; - -#define DENALI_CAP_HW_ECC_FIXUP BIT(0) -#define DENALI_CAP_DMA_64BIT BIT(1) - -int denali_calc_ecc_bytes(int step_size, int strength); -int denali_init(struct denali_nand_info *denali); - -#endif /* __DENALI_H__ */ diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c deleted file mode 100644 index 65a7797f0f..0000000000 --- a/drivers/mtd/nand/denali_dt.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2017 Socionext Inc. - * Author: Masahiro Yamada - */ - -#include -#include -#include -#include -#include - -#include "denali.h" - -struct denali_dt_data { - unsigned int revision; - unsigned int caps; - const struct nand_ecc_caps *ecc_caps; -}; - -NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, - 512, 8, 15); -static const struct denali_dt_data denali_socfpga_data = { - .caps = DENALI_CAP_HW_ECC_FIXUP, - .ecc_caps = &denali_socfpga_ecc_caps, -}; - -NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, - 1024, 8, 16, 24); -static const struct denali_dt_data denali_uniphier_v5a_data = { - .caps = DENALI_CAP_HW_ECC_FIXUP | - DENALI_CAP_DMA_64BIT, - .ecc_caps = &denali_uniphier_v5a_ecc_caps, -}; - -NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes, - 1024, 8, 16); -static const struct denali_dt_data denali_uniphier_v5b_data = { - .revision = 0x0501, - .caps = DENALI_CAP_HW_ECC_FIXUP | - DENALI_CAP_DMA_64BIT, - .ecc_caps = &denali_uniphier_v5b_ecc_caps, -}; - -static const struct udevice_id denali_nand_dt_ids[] = { - { - .compatible = "altr,socfpga-denali-nand", - .data = (unsigned long)&denali_socfpga_data, - }, - { - .compatible = "socionext,uniphier-denali-nand-v5a", - .data = (unsigned long)&denali_uniphier_v5a_data, - }, - { - .compatible = "socionext,uniphier-denali-nand-v5b", - .data = (unsigned long)&denali_uniphier_v5b_data, - }, - { /* sentinel */ } -}; - -static int denali_dt_probe(struct udevice *dev) -{ - struct denali_nand_info *denali = dev_get_priv(dev); - const struct denali_dt_data *data; - struct clk clk; - struct resource res; - int ret; - - data = (void *)dev_get_driver_data(dev); - if (data) { - denali->revision = data->revision; - denali->caps = data->caps; - denali->ecc_caps = data->ecc_caps; - } - - denali->dev = dev; - - ret = dev_read_resource_byname(dev, "denali_reg", &res); - if (ret) - return ret; - - denali->reg = devm_ioremap(dev, res.start, resource_size(&res)); - - ret = dev_read_resource_byname(dev, "nand_data", &res); - if (ret) - return ret; - - denali->host = devm_ioremap(dev, res.start, resource_size(&res)); - - ret = clk_get_by_index(dev, 0, &clk); - if (ret) - return ret; - - ret = clk_enable(&clk); - if (ret) - return ret; - - denali->clk_x_rate = clk_get_rate(&clk); - - return denali_init(denali); -} - -U_BOOT_DRIVER(denali_nand_dt) = { - .name = "denali-nand-dt", - .id = UCLASS_MISC, - .of_match = denali_nand_dt_ids, - .probe = denali_dt_probe, - .priv_auto_alloc_size = sizeof(struct denali_nand_info), -}; - -void board_nand_init(void) -{ - struct udevice *dev; - int ret; - - ret = uclass_get_device_by_driver(UCLASS_MISC, - DM_GET_DRIVER(denali_nand_dt), - &dev); - if (ret && ret != -ENODEV) - pr_err("Failed to initialize Denali NAND controller. (error %d)\n", - ret); -} diff --git a/drivers/mtd/nand/denali_spl.c b/drivers/mtd/nand/denali_spl.c deleted file mode 100644 index dbaba3cab2..0000000000 --- a/drivers/mtd/nand/denali_spl.c +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2014 Panasonic Corporation - * Copyright (C) 2014-2015 Masahiro Yamada - */ - -#include -#include -#include -#include -#include "denali.h" - -#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ -#define DENALI_MAP10 (2 << 26) /* high-level control plane */ - -#define INDEX_CTRL_REG 0x0 -#define INDEX_DATA_REG 0x10 - -#define SPARE_ACCESS 0x41 -#define MAIN_ACCESS 0x42 -#define PIPELINE_ACCESS 0x2000 - -#define BANK(x) ((x) << 24) - -static void __iomem *denali_flash_mem = - (void __iomem *)CONFIG_SYS_NAND_DATA_BASE; -static void __iomem *denali_flash_reg = - (void __iomem *)CONFIG_SYS_NAND_REGS_BASE; - -static const int flash_bank; -static int page_size, oob_size, pages_per_block; - -static void index_addr(uint32_t address, uint32_t data) -{ - writel(address, denali_flash_mem + INDEX_CTRL_REG); - writel(data, denali_flash_mem + INDEX_DATA_REG); -} - -static int wait_for_irq(uint32_t irq_mask) -{ - unsigned long timeout = 1000000; - uint32_t intr_status; - - do { - intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank)); - - if (intr_status & INTR__ECC_UNCOR_ERR) { - debug("Uncorrected ECC detected\n"); - return -EBADMSG; - } - - if (intr_status & irq_mask) - break; - - udelay(1); - timeout--; - } while (timeout); - - if (!timeout) { - debug("Timeout with interrupt status %08x\n", intr_status); - return -EIO; - } - - return 0; -} - -static void read_data_from_flash_mem(uint8_t *buf, int len) -{ - int i; - uint32_t *buf32; - - /* transfer the data from the flash */ - buf32 = (uint32_t *)buf; - - /* - * Let's take care of unaligned access although it rarely happens. - * Avoid put_unaligned() for the normal use cases since it leads to - * a bit performance regression. - */ - if ((unsigned long)buf32 % 4) { - for (i = 0; i < len / 4; i++) - put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG), - buf32++); - } else { - for (i = 0; i < len / 4; i++) - *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG); - } - - if (len % 4) { - u32 tmp; - - tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG)); - buf = (uint8_t *)buf32; - for (i = 0; i < len % 4; i++) { - *buf++ = tmp; - tmp >>= 8; - } - } -} - -int denali_send_pipeline_cmd(int page, int ecc_en, int access_type) -{ - uint32_t addr, cmd; - static uint32_t page_count = 1; - - writel(ecc_en, denali_flash_reg + ECC_ENABLE); - - /* clear all bits of intr_status. */ - writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank)); - - addr = BANK(flash_bank) | page; - - /* setup the acccess type */ - cmd = DENALI_MAP10 | addr; - index_addr(cmd, access_type); - - /* setup the pipeline command */ - index_addr(cmd, PIPELINE_ACCESS | page_count); - - cmd = DENALI_MAP01 | addr; - writel(cmd, denali_flash_mem + INDEX_CTRL_REG); - - return wait_for_irq(INTR__LOAD_COMP); -} - -static int nand_read_oob(void *buf, int page) -{ - int ret; - - ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS); - if (ret < 0) - return ret; - - read_data_from_flash_mem(buf, oob_size); - - return 0; -} - -static int nand_read_page(void *buf, int page) -{ - int ret; - - ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS); - if (ret < 0) - return ret; - - read_data_from_flash_mem(buf, page_size); - - return 0; -} - -static int nand_block_isbad(void *buf, int block) -{ - int ret; - - ret = nand_read_oob(buf, block * pages_per_block); - if (ret < 0) - return ret; - - return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff; -} - -/* nand_init() - initialize data to make nand usable by SPL */ -void nand_init(void) -{ - /* access to main area */ - writel(0, denali_flash_reg + TRANSFER_SPARE_REG); - - /* - * These registers are expected to be already set by the hardware - * or earlier boot code. So we read these values out. - */ - page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE); - oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE); - pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK); -} - -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) -{ - int block, page, column, readlen; - int ret; - int force_bad_block_check = 1; - - page = offs / page_size; - column = offs % page_size; - - block = page / pages_per_block; - page = page % pages_per_block; - - while (size) { - if (force_bad_block_check || page == 0) { - ret = nand_block_isbad(dst, block); - if (ret < 0) - return ret; - - if (ret) { - block++; - continue; - } - } - - force_bad_block_check = 0; - - ret = nand_read_page(dst, block * pages_per_block + page); - if (ret < 0) - return ret; - - readlen = min(page_size - column, (int)size); - - if (unlikely(column)) { - /* Partial page read */ - memmove(dst, dst + column, readlen); - column = 0; - } - - size -= readlen; - dst += readlen; - page++; - if (page == pages_per_block) { - block++; - page = 0; - } - } - - return 0; -} - -void nand_deselect(void) {} diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c deleted file mode 100644 index 263d46ec8f..0000000000 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ /dev/null @@ -1,810 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Freescale Enhanced Local Bus Controller FCM NAND driver - * - * Copyright (c) 2006-2008 Freescale Semiconductor - * - * Authors: Nick Spence , - * Scott Wood - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include - -#ifdef VERBOSE_DEBUG -#define DEBUG_ELBC -#define vdbg(format, arg...) printf("DEBUG: " format, ##arg) -#else -#define vdbg(format, arg...) do {} while (0) -#endif - -/* Can't use plain old DEBUG because the linux mtd - * headers define it as a macro. - */ -#ifdef DEBUG_ELBC -#define dbg(format, arg...) printf("DEBUG: " format, ##arg) -#else -#define dbg(format, arg...) do {} while (0) -#endif - -#define MAX_BANKS 8 -#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ - -#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) - -struct fsl_elbc_ctrl; - -/* mtd information per set */ - -struct fsl_elbc_mtd { - struct nand_chip chip; - struct fsl_elbc_ctrl *ctrl; - - struct device *dev; - int bank; /* Chip select bank number */ - u8 __iomem *vbase; /* Chip select base virtual address */ - int page_size; /* NAND page size (0=512, 1=2048) */ - unsigned int fmr; /* FCM Flash Mode Register value */ -}; - -/* overview of the fsl elbc controller */ - -struct fsl_elbc_ctrl { - struct nand_hw_control controller; - struct fsl_elbc_mtd *chips[MAX_BANKS]; - - /* device info */ - fsl_lbc_t *regs; - u8 __iomem *addr; /* Address of assigned FCM buffer */ - unsigned int page; /* Last page written to / read from */ - unsigned int read_bytes; /* Number of bytes read during command */ - unsigned int column; /* Saved column from SEQIN */ - unsigned int index; /* Pointer to next byte to 'read' */ - unsigned int status; /* status read from LTESR after last op */ - unsigned int mdr; /* UPM/FCM Data Register value */ - unsigned int use_mdr; /* Non zero if the MDR is to be set */ - unsigned int oob; /* Non zero if operating on OOB data */ -}; - -/* These map to the positions used by the FCM hardware ECC generator */ - -/* Small Page FLASH with FMR[ECCM] = 0 */ -static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { - .eccbytes = 3, - .eccpos = {6, 7, 8}, - .oobfree = { {0, 5}, {9, 7} }, -}; - -/* Small Page FLASH with FMR[ECCM] = 1 */ -static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { - .eccbytes = 3, - .eccpos = {8, 9, 10}, - .oobfree = { {0, 5}, {6, 2}, {11, 5} }, -}; - -/* Large Page FLASH with FMR[ECCM] = 0 */ -static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { - .eccbytes = 12, - .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, - .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, -}; - -/* Large Page FLASH with FMR[ECCM] = 1 */ -static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { - .eccbytes = 12, - .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, - .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, -}; - -/* - * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset - * 1, so we have to adjust bad block pattern. This pattern should be used for - * x8 chips only. So far hardware does not support x16 chips anyway. - */ -static u8 scan_ff_pattern[] = { 0xff, }; - -static struct nand_bbt_descr largepage_memorybased = { - .options = 0, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern, -}; - -/* - * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, - * interfere with ECC positions, that's why we implement our own descriptors. - * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. - */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 11, - .len = 4, - .veroffs = 15, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -/*=================================*/ - -/* - * Set up the FCM hardware block and page address fields, and the fcm - * structure addr field to point to the correct FCM buffer in memory - */ -static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbc_t *lbc = ctrl->regs; - int buf_num; - - ctrl->page = page_addr; - - if (priv->page_size) { - out_be32(&lbc->fbar, page_addr >> 6); - out_be32(&lbc->fpar, - ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | - (oob ? FPAR_LP_MS : 0) | column); - buf_num = (page_addr & 1) << 2; - } else { - out_be32(&lbc->fbar, page_addr >> 5); - out_be32(&lbc->fpar, - ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | - (oob ? FPAR_SP_MS : 0) | column); - buf_num = page_addr & 7; - } - - ctrl->addr = priv->vbase + buf_num * 1024; - ctrl->index = column; - - /* for OOB data point to the second half of the buffer */ - if (oob) - ctrl->index += priv->page_size ? 2048 : 512; - - vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " - "index %x, pes %d ps %d\n", - buf_num, ctrl->addr, priv->vbase, ctrl->index, - chip->phys_erase_shift, chip->page_shift); -} - -/* - * execute FCM command and wait for it to complete - */ -static int fsl_elbc_run_command(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbc_t *lbc = ctrl->regs; - u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; - u32 time_start; - u32 ltesr; - - /* Setup the FMR[OP] to execute without write protection */ - out_be32(&lbc->fmr, priv->fmr | 3); - if (ctrl->use_mdr) - out_be32(&lbc->mdr, ctrl->mdr); - - vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", - in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); - vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x " - "fbcr=%08x bank=%d\n", - in_be32(&lbc->fbar), in_be32(&lbc->fpar), - in_be32(&lbc->fbcr), priv->bank); - - /* execute special operation */ - out_be32(&lbc->lsor, priv->bank); - - /* wait for FCM complete flag or timeout */ - time_start = get_timer(0); - - ltesr = 0; - while (get_timer(time_start) < timeo) { - ltesr = in_be32(&lbc->ltesr); - if (ltesr & LTESR_CC) - break; - } - - ctrl->status = ltesr & LTESR_NAND_MASK; - out_be32(&lbc->ltesr, ctrl->status); - out_be32(&lbc->lteatr, 0); - - /* store mdr value in case it was needed */ - if (ctrl->use_mdr) - ctrl->mdr = in_be32(&lbc->mdr); - - ctrl->use_mdr = 0; - - vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", - ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); - - /* returns 0 on success otherwise non-zero) */ - return ctrl->status == LTESR_CC ? 0 : -EIO; -} - -static void fsl_elbc_do_read(struct nand_chip *chip, int oob) -{ - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbc_t *lbc = ctrl->regs; - - if (priv->page_size) { - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_CW1 << FIR_OP3_SHIFT) | - (FIR_OP_RBW << FIR_OP4_SHIFT)); - - out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | - (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); - } else { - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_RBW << FIR_OP3_SHIFT)); - - if (oob) - out_be32(&lbc->fcr, - NAND_CMD_READOOB << FCR_CMD0_SHIFT); - else - out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); - } -} - -/* cmdfunc send commands to the FCM */ -static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbc_t *lbc = ctrl->regs; - - ctrl->use_mdr = 0; - - /* clear the read buffer */ - ctrl->read_bytes = 0; - if (command != NAND_CMD_PAGEPROG) - ctrl->index = 0; - - switch (command) { - /* READ0 and READ1 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ1: - column += 256; - - /* fall-through */ - case NAND_CMD_READ0: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" - " 0x%x, column: 0x%x.\n", page_addr, column); - - out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ - set_addr(mtd, 0, page_addr, 0); - - ctrl->read_bytes = mtd->writesize + mtd->oobsize; - ctrl->index += column; - - fsl_elbc_do_read(chip, 0); - fsl_elbc_run_command(mtd); - return; - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" - " 0x%x, column: 0x%x.\n", page_addr, column); - - out_be32(&lbc->fbcr, mtd->oobsize - column); - set_addr(mtd, column, page_addr, 1); - - ctrl->read_bytes = mtd->writesize + mtd->oobsize; - - fsl_elbc_do_read(chip, 1); - fsl_elbc_run_command(mtd); - - return; - - /* READID must read all 5 possible bytes while CEB is active */ - case NAND_CMD_READID: - case NAND_CMD_PARAM: - vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command); - - out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_UA << FIR_OP1_SHIFT) | - (FIR_OP_RBW << FIR_OP2_SHIFT)); - out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - out_be32(&lbc->fbcr, 256); - ctrl->read_bytes = 256; - ctrl->use_mdr = 1; - ctrl->mdr = column; - set_addr(mtd, 0, 0, 0); - fsl_elbc_run_command(mtd); - return; - - /* ERASE1 stores the block and page address */ - case NAND_CMD_ERASE1: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " - "page_addr: 0x%x.\n", page_addr); - set_addr(mtd, 0, page_addr, 0); - return; - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); - - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_PA << FIR_OP1_SHIFT) | - (FIR_OP_CM1 << FIR_OP2_SHIFT)); - - out_be32(&lbc->fcr, - (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); - - out_be32(&lbc->fbcr, 0); - ctrl->read_bytes = 0; - - fsl_elbc_run_command(mtd); - return; - - /* SEQIN sets up the addr buffer and all registers except the length */ - case NAND_CMD_SEQIN: { - u32 fcr; - vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " - "page_addr: 0x%x, column: 0x%x.\n", - page_addr, column); - - ctrl->column = column; - ctrl->oob = 0; - - if (priv->page_size) { - fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | - (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); - - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_WB << FIR_OP3_SHIFT) | - (FIR_OP_CW1 << FIR_OP4_SHIFT)); - } else { - fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | - (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); - - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CM2 << FIR_OP1_SHIFT) | - (FIR_OP_CA << FIR_OP2_SHIFT) | - (FIR_OP_PA << FIR_OP3_SHIFT) | - (FIR_OP_WB << FIR_OP4_SHIFT) | - (FIR_OP_CW1 << FIR_OP5_SHIFT)); - - if (column >= mtd->writesize) { - /* OOB area --> READOOB */ - column -= mtd->writesize; - fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; - ctrl->oob = 1; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; - } else { - /* Second 256 bytes --> READ1 */ - fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; - } - } - - out_be32(&lbc->fcr, fcr); - set_addr(mtd, column, page_addr, ctrl->oob); - return; - } - - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: { - vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " - "writing %d bytes.\n", ctrl->index); - - /* if the write did not start at 0 or is not a full page - * then set the exact length, otherwise use a full page - * write so the HW generates the ECC. - */ - if (ctrl->oob || ctrl->column != 0 || - ctrl->index != mtd->writesize + mtd->oobsize) - out_be32(&lbc->fbcr, ctrl->index); - else - out_be32(&lbc->fbcr, 0); - - fsl_elbc_run_command(mtd); - - return; - } - - /* CMD_STATUS must read the status byte while CEB is active */ - /* Note - it does not wait for the ready line */ - case NAND_CMD_STATUS: - out_be32(&lbc->fir, - (FIR_OP_CM0 << FIR_OP0_SHIFT) | - (FIR_OP_RBW << FIR_OP1_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); - out_be32(&lbc->fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_elbc_run_command(mtd); - - /* The chip always seems to report that it is - * write-protected, even when it is not. - */ - out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); - return; - - /* RESET without waiting for the ready line */ - case NAND_CMD_RESET: - dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); - out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); - out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); - fsl_elbc_run_command(mtd); - return; - - default: - printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", - command); - } -} - -static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) -{ - /* The hardware does not seem to support multiple - * chips per bank. - */ -} - -/* - * Write buf to the FCM Controller Data Buffer - */ -static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - unsigned int bufsize = mtd->writesize + mtd->oobsize; - - if (len <= 0) { - printf("write_buf of %d bytes", len); - ctrl->status = 0; - return; - } - - if ((unsigned int)len > bufsize - ctrl->index) { - printf("write_buf beyond end of buffer " - "(%d requested, %u available)\n", - len, bufsize - ctrl->index); - len = bufsize - ctrl->index; - } - - memcpy_toio(&ctrl->addr[ctrl->index], buf, len); - /* - * This is workaround for the weird elbc hangs during nand write, - * Scott Wood says: "...perhaps difference in how long it takes a - * write to make it through the localbus compared to a write to IMMR - * is causing problems, and sync isn't helping for some reason." - * Reading back the last byte helps though. - */ - in_8(&ctrl->addr[ctrl->index] + len - 1); - - ctrl->index += len; -} - -/* - * read a byte from either the FCM hardware buffer if it has any data left - * otherwise issue a command to read a single byte. - */ -static u8 fsl_elbc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - - /* If there are still bytes in the FCM, then use the next byte. */ - if (ctrl->index < ctrl->read_bytes) - return in_8(&ctrl->addr[ctrl->index++]); - - printf("read_byte beyond end of buffer\n"); - return ERR_BYTE; -} - -/* - * Read from the FCM Controller Data Buffer - */ -static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - int avail; - - if (len < 0) - return; - - avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); - memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); - ctrl->index += avail; - - if (len > avail) - printf("read_buf beyond end of buffer " - "(%d requested, %d available)\n", - len, avail); -} - -/* This function is called after Program and Erase Operations to - * check for success or failure. - */ -static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbc_t *lbc = ctrl->regs; - - if (ctrl->status != LTESR_CC) - return NAND_STATUS_FAIL; - - /* Use READ_STATUS command, but wait for the device to be ready */ - ctrl->use_mdr = 0; - out_be32(&lbc->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_RBW << FIR_OP1_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); - out_be32(&lbc->fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_elbc_run_command(mtd); - - if (ctrl->status != LTESR_CC) - return NAND_STATUS_FAIL; - - /* The chip always seems to report that it is - * write-protected, even when it is not. - */ - out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); - return fsl_elbc_read_byte(mtd); -} - -static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - fsl_elbc_read_buf(mtd, buf, mtd->writesize); - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) - mtd->ecc_stats.failed++; - - return 0; -} - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - fsl_elbc_write_buf(mtd, buf, mtd->writesize); - fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static struct fsl_elbc_ctrl *elbc_ctrl; - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, uint32_t data_len, - const uint8_t *buf, int oob_required, int page) -{ - fsl_elbc_write_buf(mtd, buf, mtd->writesize); - fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static void fsl_elbc_ctrl_init(void) -{ - elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL); - if (!elbc_ctrl) - return; - - elbc_ctrl->regs = LBC_BASE_ADDR; - - /* clear event registers */ - out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK); - out_be32(&elbc_ctrl->regs->lteatr, 0); - - /* Enable interrupts for any detected events */ - out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK); - - elbc_ctrl->read_bytes = 0; - elbc_ctrl->index = 0; - elbc_ctrl->addr = NULL; -} - -static int fsl_elbc_chip_init(int devnum, u8 *addr) -{ - struct mtd_info *mtd; - struct nand_chip *nand; - struct fsl_elbc_mtd *priv; - uint32_t br = 0, or = 0; - int ret; - - if (!elbc_ctrl) { - fsl_elbc_ctrl_init(); - if (!elbc_ctrl) - return -1; - } - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->ctrl = elbc_ctrl; - priv->vbase = addr; - - /* Find which chip select it is connected to. It'd be nice - * if we could pass more than one datum to the NAND driver... - */ - for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { - phys_addr_t phys_addr = virt_to_phys(addr); - - br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); - or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); - - if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && - (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) - break; - } - - if (priv->bank >= MAX_BANKS) { - printf("fsl_elbc_nand: address did not match any " - "chip selects\n"); - kfree(priv); - return -ENODEV; - } - - nand = &priv->chip; - mtd = nand_to_mtd(nand); - - elbc_ctrl->chips[priv->bank] = priv; - - /* fill in nand_chip structure */ - /* set up function call table */ - nand->read_byte = fsl_elbc_read_byte; - nand->write_buf = fsl_elbc_write_buf; - nand->read_buf = fsl_elbc_read_buf; - nand->select_chip = fsl_elbc_select_chip; - nand->cmdfunc = fsl_elbc_cmdfunc; - nand->waitfunc = fsl_elbc_wait; - - /* set up nand options */ - nand->bbt_td = &bbt_main_descr; - nand->bbt_md = &bbt_mirror_descr; - - /* set up nand options */ - nand->options = NAND_NO_SUBPAGE_WRITE; - nand->bbt_options = NAND_BBT_USE_FLASH; - - nand->controller = &elbc_ctrl->controller; - nand_set_controller_data(nand, priv); - - nand->ecc.read_page = fsl_elbc_read_page; - nand->ecc.write_page = fsl_elbc_write_page; - nand->ecc.write_subpage = fsl_elbc_write_subpage; - - priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); - - /* If CS Base Register selects full hardware ECC then use it */ - if ((br & BR_DECC) == BR_DECC_CHK_GEN) { - nand->ecc.mode = NAND_ECC_HW; - - nand->ecc.layout = (priv->fmr & FMR_ECCM) ? - &fsl_elbc_oob_sp_eccm1 : - &fsl_elbc_oob_sp_eccm0; - - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.steps = 1; - nand->ecc.strength = 1; - } else { - /* otherwise fall back to software ECC */ -#if defined(CONFIG_NAND_ECC_BCH) - nand->ecc.mode = NAND_ECC_SOFT_BCH; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif - } - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - /* Large-page-specific setup */ - if (mtd->writesize == 2048) { - setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, - OR_FCM_PGS); - in_be32(&elbc_ctrl->regs->bank[priv->bank].or); - - priv->page_size = 1; - nand->badblock_pattern = &largepage_memorybased; - - /* - * Hardware expects small page has ECCM0, large page has - * ECCM1 when booting from NAND, and we follow that even - * when not booting from NAND. - */ - priv->fmr |= FMR_ECCM; - - /* adjust ecc setup if needed */ - if ((br & BR_DECC) == BR_DECC_CHK_GEN) { - nand->ecc.steps = 4; - nand->ecc.layout = (priv->fmr & FMR_ECCM) ? - &fsl_elbc_oob_lp_eccm1 : - &fsl_elbc_oob_lp_eccm0; - } - } else if (mtd->writesize == 512) { - clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, - OR_FCM_PGS); - in_be32(&elbc_ctrl->regs->bank[priv->bank].or); - } else { - return -ENODEV; - } - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - ret = nand_register(devnum, mtd); - if (ret) - return ret; - - return 0; -} - -#ifndef CONFIG_SYS_NAND_BASE_LIST -#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } -#endif - -static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = - CONFIG_SYS_NAND_BASE_LIST; - -void board_nand_init(void) -{ - int i; - - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) - fsl_elbc_chip_init(i, (u8 *)base_address[i]); -} diff --git a/drivers/mtd/nand/fsl_elbc_spl.c b/drivers/mtd/nand/fsl_elbc_spl.c deleted file mode 100644 index 30c3308940..0000000000 --- a/drivers/mtd/nand/fsl_elbc_spl.c +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine - * - * (C) Copyright 2006-2008 - * Stefan Roese, DENX Software Engineering, sr@denx.de. - * - * Copyright (c) 2008 Freescale Semiconductor, Inc. - * Author: Scott Wood - */ - -#include -#include -#include -#include - -#define WINDOW_SIZE 8192 - -static void nand_wait(void) -{ - fsl_lbc_t *regs = LBC_BASE_ADDR; - - for (;;) { - uint32_t status = in_be32(®s->ltesr); - - if (status == 1) - return; - - if (status & 1) { - puts("read failed (ltesr)\n"); - for (;;); - } - } -} - -#ifdef CONFIG_TPL_BUILD -int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) -#else -static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) -#endif -{ - fsl_lbc_t *regs = LBC_BASE_ADDR; - uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; - const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS; - const int block_shift = large ? 17 : 14; - const int block_size = 1 << block_shift; - const int page_size = large ? 2048 : 512; - const int bad_marker = large ? page_size + 0 : page_size + 5; - int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2; - int pos = 0; - char *dst = vdst; - - if (offs & (block_size - 1)) { - puts("bad offset\n"); - for (;;); - } - - if (large) { - fmr |= FMR_ECCM; - out_be32(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | - (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); - out_be32(®s->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_CW1 << FIR_OP3_SHIFT) | - (FIR_OP_RBW << FIR_OP4_SHIFT)); - } else { - out_be32(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); - out_be32(®s->fir, - (FIR_OP_CW0 << FIR_OP0_SHIFT) | - (FIR_OP_CA << FIR_OP1_SHIFT) | - (FIR_OP_PA << FIR_OP2_SHIFT) | - (FIR_OP_RBW << FIR_OP3_SHIFT)); - } - - out_be32(®s->fbcr, 0); - clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN); - - while (pos < uboot_size) { - int i = 0; - out_be32(®s->fbar, offs >> block_shift); - - do { - int j; - unsigned int page_offs = (offs & (block_size - 1)) << 1; - - out_be32(®s->ltesr, ~0); - out_be32(®s->lteatr, 0); - out_be32(®s->fpar, page_offs); - out_be32(®s->fmr, fmr); - out_be32(®s->lsor, 0); - nand_wait(); - - page_offs %= WINDOW_SIZE; - - /* - * If either of the first two pages are marked bad, - * continue to the next block. - */ - if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) { - puts("skipping\n"); - offs = (offs + block_size) & ~(block_size - 1); - pos &= ~(block_size - 1); - break; - } - - for (j = 0; j < page_size; j++) - dst[pos + j] = buf[page_offs + j]; - - pos += page_size; - offs += page_size; - } while ((offs & (block_size - 1)) && (pos < uboot_size)); - } - - return 0; -} - -/* - * Defines a static function nand_load_image() here, because non-static makes - * the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes) - */ -#ifndef CONFIG_TPL_BUILD -#define nand_spl_load_image(offs, uboot_size, vdst) \ - nand_load_image(offs, uboot_size, vdst) -#endif - -/* - * The main entry for NAND booting. It's necessary that SDRAM is already - * configured and available since this code loads the main U-Boot image - * from NAND into SDRAM and starts it from there. - */ -void nand_boot(void) -{ - __attribute__((noreturn)) void (*uboot)(void); - /* - * Load U-Boot image from NAND into RAM - */ - nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, - CONFIG_SYS_NAND_U_BOOT_SIZE, - (void *)CONFIG_SYS_NAND_U_BOOT_DST); - -#ifdef CONFIG_NAND_ENV_DST - nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, - (void *)CONFIG_NAND_ENV_DST); - -#ifdef CONFIG_ENV_OFFSET_REDUND - nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, - (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); -#endif -#endif - -#ifdef CONFIG_SPL_FLUSH_IMAGE - /* - * Clean d-cache and invalidate i-cache, to - * make sure that no stale data is executed. - */ - flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); -#endif - - puts("transfering control\n"); - /* - * Jump to U-Boot image - */ - uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; - (*uboot)(); -} diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c deleted file mode 100644 index 29f30d8ccc..0000000000 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ /dev/null @@ -1,1064 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* Integrated Flash Controller NAND Machine Driver - * - * Copyright (c) 2012 Freescale Semiconductor, Inc - * - * Authors: Dipen Dudhat - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#ifndef CONFIG_SYS_FSL_IFC_BANK_COUNT -#define CONFIG_SYS_FSL_IFC_BANK_COUNT 4 -#endif - -#define MAX_BANKS CONFIG_SYS_FSL_IFC_BANK_COUNT -#define ERR_BYTE 0xFF /* Value returned for read bytes - when read failed */ - -struct fsl_ifc_ctrl; - -/* mtd information per set */ -struct fsl_ifc_mtd { - struct nand_chip chip; - struct fsl_ifc_ctrl *ctrl; - - struct device *dev; - int bank; /* Chip select bank number */ - unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ - u8 __iomem *vbase; /* Chip select base virtual address */ -}; - -/* overview of the fsl ifc controller */ -struct fsl_ifc_ctrl { - struct nand_hw_control controller; - struct fsl_ifc_mtd *chips[MAX_BANKS]; - - /* device info */ - struct fsl_ifc regs; - void __iomem *addr; /* Address of assigned IFC buffer */ - unsigned int page; /* Last page written to / read from */ - unsigned int read_bytes; /* Number of bytes read during command */ - unsigned int column; /* Saved column from SEQIN */ - unsigned int index; /* Pointer to next byte to 'read' */ - unsigned int status; /* status read from NEESR after last op */ - unsigned int oob; /* Non zero if operating on OOB data */ - unsigned int eccread; /* Non zero for a full-page ECC read */ -}; - -static struct fsl_ifc_ctrl *ifc_ctrl; - -/* 512-byte page with 4-bit ECC, 8-bit */ -static struct nand_ecclayout oob_512_8bit_ecc4 = { - .eccbytes = 8, - .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {0, 5}, {6, 2} }, -}; - -/* 512-byte page with 4-bit ECC, 16-bit */ -static struct nand_ecclayout oob_512_16bit_ecc4 = { - .eccbytes = 8, - .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {2, 6}, }, -}; - -/* 2048-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_2048_ecc4 = { - .eccbytes = 32, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - }, - .oobfree = { {2, 6}, {40, 24} }, -}; - -/* 4096-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_4096_ecc4 = { - .eccbytes = 64, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - }, - .oobfree = { {2, 6}, {72, 56} }, -}; - -/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ -static struct nand_ecclayout oob_4096_ecc8 = { - .eccbytes = 128, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - }, - .oobfree = { {2, 6}, {136, 82} }, -}; - -/* 8192-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_8192_ecc4 = { - .eccbytes = 128, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - }, - .oobfree = { {2, 6}, {136, 208} }, -}; - -/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ -static struct nand_ecclayout oob_8192_ecc8 = { - .eccbytes = 256, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, - 256, 257, 258, 259, 260, 261, 262, 263, - }, - .oobfree = { {2, 6}, {264, 80} }, -}; - -/* - * Generic flash bbt descriptors - */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 2, /* 0 on 8-bit small page */ - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 2, /* 0 on 8-bit small page */ - .len = 4, - .veroffs = 6, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -/* - * Set up the IFC hardware block and page address fields, and the ifc nand - * structure addr field to point to the correct IFC buffer in memory - */ -static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; - int buf_num; - - ctrl->page = page_addr; - - /* Program ROW0/COL0 */ - ifc_out32(&ifc->ifc_nand.row0, page_addr); - ifc_out32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); - - buf_num = page_addr & priv->bufnum_mask; - - ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); - ctrl->index = column; - - /* for OOB data point to the second half of the buffer */ - if (oob) - ctrl->index += mtd->writesize; -} - -/* returns nonzero if entire page is blank */ -static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, - u32 eccstat, unsigned int bufnum) -{ - return (eccstat >> ((3 - bufnum % 4) * 8)) & 15; -} - -/* - * execute IFC NAND command and wait for it to complete - */ -static int fsl_ifc_run_command(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; - u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; - u32 time_start; - u32 eccstat; - int i; - - /* set the chip select for NAND Transaction */ - ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); - - /* start read/write seq */ - ifc_out32(&ifc->ifc_nand.nandseq_strt, - IFC_NAND_SEQ_STRT_FIR_STRT); - - /* wait for NAND Machine complete flag or timeout */ - time_start = get_timer(0); - - while (get_timer(time_start) < timeo) { - ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); - - if (ctrl->status & IFC_NAND_EVTER_STAT_OPC) - break; - } - - ifc_out32(&ifc->ifc_nand.nand_evter_stat, ctrl->status); - - if (ctrl->status & IFC_NAND_EVTER_STAT_FTOER) - printf("%s: Flash Time Out Error\n", __func__); - if (ctrl->status & IFC_NAND_EVTER_STAT_WPER) - printf("%s: Write Protect Error\n", __func__); - - if (ctrl->eccread) { - int errors; - int bufnum = ctrl->page & priv->bufnum_mask; - int sector_start = bufnum * chip->ecc.steps; - int sector_end = sector_start + chip->ecc.steps - 1; - u32 *eccstat_regs; - - eccstat_regs = ifc->ifc_nand.nand_eccstat; - eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); - - for (i = sector_start; i <= sector_end; i++) { - if ((i != sector_start) && !(i % 4)) - eccstat = ifc_in32(&eccstat_regs[i / 4]); - - errors = check_read_ecc(mtd, ctrl, eccstat, i); - - if (errors == 15) { - /* - * Uncorrectable error. - * We'll check for blank pages later. - * - * We disable ECCER reporting due to erratum - * IFC-A002770 -- so report it now if we - * see an uncorrectable error in ECCSTAT. - */ - ctrl->status |= IFC_NAND_EVTER_STAT_ECCER; - continue; - } - - mtd->ecc_stats.corrected += errors; - } - - ctrl->eccread = 0; - } - - /* returns 0 on success otherwise non-zero) */ - return ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO; -} - -static void fsl_ifc_do_read(struct nand_chip *chip, - int oob, - struct mtd_info *mtd) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; - - /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ - if (mtd->writesize > 512) { - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); - - ifc_out32(&ifc->ifc_nand.nand_fcr0, - (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); - } else { - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT)); - - if (oob) - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); - else - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); - } -} - -/* cmdfunc send commands to the IFC NAND Machine */ -static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; - - /* clear the read buffer */ - ctrl->read_bytes = 0; - if (command != NAND_CMD_PAGEPROG) - ctrl->index = 0; - - switch (command) { - /* READ0 read the entire buffer to use hardware ECC. */ - case NAND_CMD_READ0: { - ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); - set_addr(mtd, 0, page_addr, 0); - - ctrl->read_bytes = mtd->writesize + mtd->oobsize; - ctrl->index += column; - - if (chip->ecc.mode == NAND_ECC_HW) - ctrl->eccread = 1; - - fsl_ifc_do_read(chip, 0, mtd); - fsl_ifc_run_command(mtd); - return; - } - - /* READOOB reads only the OOB because no ECC is performed. */ - case NAND_CMD_READOOB: - ifc_out32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); - set_addr(mtd, column, page_addr, 1); - - ctrl->read_bytes = mtd->writesize + mtd->oobsize; - - fsl_ifc_do_read(chip, 1, mtd); - fsl_ifc_run_command(mtd); - - return; - - /* READID must read all possible bytes while CEB is active */ - case NAND_CMD_READID: - case NAND_CMD_PARAM: { - int timing = IFC_FIR_OP_RB; - if (command == NAND_CMD_PARAM) - timing = IFC_FIR_OP_RBCD; - - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (timing << IFC_NAND_FIR0_OP2_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fcr0, - command << IFC_NAND_FCR0_CMD0_SHIFT); - ifc_out32(&ifc->ifc_nand.row3, column); - - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - ifc_out32(&ifc->ifc_nand.nand_fbcr, 256); - ctrl->read_bytes = 256; - - set_addr(mtd, 0, 0, 0); - fsl_ifc_run_command(mtd); - return; - } - - /* ERASE1 stores the block and page address */ - case NAND_CMD_ERASE1: - set_addr(mtd, 0, page_addr, 0); - return; - - /* ERASE2 uses the block and page address from ERASE1 */ - case NAND_CMD_ERASE2: - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT)); - - ifc_out32(&ifc->ifc_nand.nand_fcr0, - (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); - - ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); - ctrl->read_bytes = 0; - fsl_ifc_run_command(mtd); - return; - - /* SEQIN sets up the addr buffer and all registers except the length */ - case NAND_CMD_SEQIN: { - u32 nand_fcr0; - ctrl->column = column; - ctrl->oob = 0; - - if (mtd->writesize > 512) { - nand_fcr0 = - (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_WBCD << - IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fir1, - (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_RDSTAT << - IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT)); - } else { - nand_fcr0 = ((NAND_CMD_PAGEPROG << - IFC_NAND_FCR0_CMD1_SHIFT) | - (NAND_CMD_SEQIN << - IFC_NAND_FCR0_CMD2_SHIFT) | - (NAND_CMD_STATUS << - IFC_NAND_FCR0_CMD3_SHIFT)); - - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fir1, - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_RDSTAT << - IFC_NAND_FIR1_OP7_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT)); - - if (column >= mtd->writesize) - nand_fcr0 |= - NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; - else - nand_fcr0 |= - NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; - } - - if (column >= mtd->writesize) { - /* OOB area --> READOOB */ - column -= mtd->writesize; - ctrl->oob = 1; - } - ifc_out32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); - set_addr(mtd, column, page_addr, ctrl->oob); - return; - } - - /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ - case NAND_CMD_PAGEPROG: - if (ctrl->oob) - ifc_out32(&ifc->ifc_nand.nand_fbcr, - ctrl->index - ctrl->column); - else - ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); - - fsl_ifc_run_command(mtd); - return; - - case NAND_CMD_STATUS: - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); - ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - - /* - * The chip always seems to report that it is - * write-protected, even when it is not. - */ - if (chip->options & NAND_BUSWIDTH_16) - ifc_out16(ctrl->addr, - ifc_in16(ctrl->addr) | NAND_STATUS_WP); - else - out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); - return; - - case NAND_CMD_RESET: - ifc_out32(&ifc->ifc_nand.nand_fir0, - IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); - fsl_ifc_run_command(mtd); - return; - - default: - printf("%s: error, unsupported command 0x%x.\n", - __func__, command); - } -} - -/* - * Write buf to the IFC NAND Controller Data Buffer - */ -static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - unsigned int bufsize = mtd->writesize + mtd->oobsize; - - if (len <= 0) { - printf("%s of %d bytes", __func__, len); - ctrl->status = 0; - return; - } - - if ((unsigned int)len > bufsize - ctrl->index) { - printf("%s beyond end of buffer " - "(%d requested, %u available)\n", - __func__, len, bufsize - ctrl->index); - len = bufsize - ctrl->index; - } - - memcpy_toio(ctrl->addr + ctrl->index, buf, len); - ctrl->index += len; -} - -/* - * read a byte from either the IFC hardware buffer if it has any data left - * otherwise issue a command to read a single byte. - */ -static u8 fsl_ifc_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - unsigned int offset; - - /* - * If there are still bytes in the IFC buffer, then use the - * next byte. - */ - if (ctrl->index < ctrl->read_bytes) { - offset = ctrl->index++; - return in_8(ctrl->addr + offset); - } - - printf("%s beyond end of buffer\n", __func__); - return ERR_BYTE; -} - -/* - * Read two bytes from the IFC hardware buffer - * read function for 16-bit buswith - */ -static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - uint16_t data; - - /* - * If there are still bytes in the IFC buffer, then use the - * next byte. - */ - if (ctrl->index < ctrl->read_bytes) { - data = ifc_in16(ctrl->addr + ctrl->index); - ctrl->index += 2; - return (uint8_t)data; - } - - printf("%s beyond end of buffer\n", __func__); - return ERR_BYTE; -} - -/* - * Read from the IFC Controller Data Buffer - */ -static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - int avail; - - if (len < 0) - return; - - avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); - memcpy_fromio(buf, ctrl->addr + ctrl->index, avail); - ctrl->index += avail; - - if (len > avail) - printf("%s beyond end of buffer " - "(%d requested, %d available)\n", - __func__, len, avail); -} - -/* This function is called after Program and Erase Operations to - * check for success or failure. - */ -static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; - u32 nand_fsr; - int status; - - if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) - return NAND_STATUS_FAIL; - - /* Use READ_STATUS command, but wait for the device to be ready */ - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << - IFC_NAND_FCR0_CMD0_SHIFT); - ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); - set_addr(mtd, 0, 0, 0); - ctrl->read_bytes = 1; - - fsl_ifc_run_command(mtd); - - if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) - return NAND_STATUS_FAIL; - - nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); - status = nand_fsr >> 24; - - /* Chip sometimes reporting write protect even when it's not */ - return status | NAND_STATUS_WP; -} - -/* - * The controller does not check for bitflips in erased pages, - * therefore software must check instead. - */ -static int -check_erased_page(struct nand_chip *chip, u8 *buf, struct mtd_info *mtd) -{ - u8 *ecc = chip->oob_poi; - const int ecc_size = chip->ecc.bytes; - const int pkt_size = chip->ecc.size; - int i, res, bitflips; - - /* IFC starts ecc bytes at offset 8 in the spare area. */ - ecc += 8; - bitflips = 0; - for (i = 0; i < chip->ecc.steps; i++) { - res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, - NULL, 0, chip->ecc.strength); - - if (res < 0) { - printf("fsl-ifc: NAND Flash ECC Uncorrectable Error\n"); - mtd->ecc_stats.failed++; - } else if (res > 0) { - mtd->ecc_stats.corrected += res; - } - bitflips = max(res, bitflips); - buf += pkt_size; - ecc += ecc_size; - } - - return bitflips; -} - -static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); - struct fsl_ifc_ctrl *ctrl = priv->ctrl; - - fsl_ifc_read_buf(mtd, buf, mtd->writesize); - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (ctrl->status & IFC_NAND_EVTER_STAT_ECCER) - return check_erased_page(chip, buf, mtd); - - if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) - mtd->ecc_stats.failed++; - - return 0; -} - -/* ECC will be calculated automatically, and errors will be detected in - * waitfunc. - */ -static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - fsl_ifc_write_buf(mtd, buf, mtd->writesize); - fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static void fsl_ifc_ctrl_init(void) -{ - uint32_t ver = 0; - ifc_ctrl = kzalloc(sizeof(*ifc_ctrl), GFP_KERNEL); - if (!ifc_ctrl) - return; - - ifc_ctrl->regs.gregs = IFC_FCM_BASE_ADDR; - - ver = ifc_in32(&ifc_ctrl->regs.gregs->ifc_rev); - if (ver >= FSL_IFC_V2_0_0) - ifc_ctrl->regs.rregs = - (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET; - else - ifc_ctrl->regs.rregs = - (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET; - - /* clear event registers */ - ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_stat, ~0U); - ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.pgrdcmpl_evt_stat, ~0U); - - /* Enable error and event for any detected errors */ - ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_en, - IFC_NAND_EVTER_EN_OPC_EN | - IFC_NAND_EVTER_EN_PGRDCMPL_EN | - IFC_NAND_EVTER_EN_FTOER_EN | - IFC_NAND_EVTER_EN_WPER_EN); - - ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.ncfgr, 0x0); -} - -static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) -{ -} - -static int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv, uint32_t ver) -{ - struct fsl_ifc_runtime *ifc = ifc_ctrl->regs.rregs; - uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0; - uint32_t ncfgr = 0; - u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; - u32 time_start; - - if (ver > FSL_IFC_V1_1_0) { - ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr); - ifc_out32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN); - - /* wait for SRAM_INIT bit to be clear or timeout */ - time_start = get_timer(0); - while (get_timer(time_start) < timeo) { - ifc_ctrl->status = - ifc_in32(&ifc->ifc_nand.nand_evter_stat); - - if (!(ifc_ctrl->status & IFC_NAND_SRAM_INIT_EN)) - return 0; - } - printf("fsl-ifc: Failed to Initialise SRAM\n"); - return 1; - } - - cs = priv->bank; - - /* Save CSOR and CSOR_ext */ - csor = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor); - csor_ext = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext); - - /* chage PageSize 8K and SpareSize 1K*/ - csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor_8k); - ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, 0x0000400); - - /* READID */ - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); - ifc_out32(&ifc->ifc_nand.row3, 0x0); - - ifc_out32(&ifc->ifc_nand.nand_fbcr, 0x0); - - /* Program ROW0/COL0 */ - ifc_out32(&ifc->ifc_nand.row0, 0x0); - ifc_out32(&ifc->ifc_nand.col0, 0x0); - - /* set the chip select for NAND Transaction */ - ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); - - /* start read seq */ - ifc_out32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); - - time_start = get_timer(0); - - while (get_timer(time_start) < timeo) { - ifc_ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); - - if (ifc_ctrl->status & IFC_NAND_EVTER_STAT_OPC) - break; - } - - if (ifc_ctrl->status != IFC_NAND_EVTER_STAT_OPC) { - printf("fsl-ifc: Failed to Initialise SRAM\n"); - return 1; - } - - ifc_out32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status); - - /* Restore CSOR and CSOR_ext */ - ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor); - ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, csor_ext); - - return 0; -} - -static int fsl_ifc_chip_init(int devnum, u8 *addr) -{ - struct mtd_info *mtd; - struct nand_chip *nand; - struct fsl_ifc_mtd *priv; - struct nand_ecclayout *layout; - struct fsl_ifc_fcm *gregs = NULL; - uint32_t cspr = 0, csor = 0, ver = 0; - int ret = 0; - - if (!ifc_ctrl) { - fsl_ifc_ctrl_init(); - if (!ifc_ctrl) - return -1; - } - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->ctrl = ifc_ctrl; - priv->vbase = addr; - gregs = ifc_ctrl->regs.gregs; - - /* Find which chip select it is connected to. - */ - for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { - phys_addr_t phys_addr = virt_to_phys(addr); - - cspr = ifc_in32(&gregs->cspr_cs[priv->bank].cspr); - csor = ifc_in32(&gregs->csor_cs[priv->bank].csor); - - if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND && - (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr)) - break; - } - - if (priv->bank >= MAX_BANKS) { - printf("%s: address did not match any " - "chip selects\n", __func__); - kfree(priv); - return -ENODEV; - } - - nand = &priv->chip; - mtd = nand_to_mtd(nand); - - ifc_ctrl->chips[priv->bank] = priv; - - /* fill in nand_chip structure */ - /* set up function call table */ - - nand->write_buf = fsl_ifc_write_buf; - nand->read_buf = fsl_ifc_read_buf; - nand->select_chip = fsl_ifc_select_chip; - nand->cmdfunc = fsl_ifc_cmdfunc; - nand->waitfunc = fsl_ifc_wait; - - /* set up nand options */ - nand->bbt_td = &bbt_main_descr; - nand->bbt_md = &bbt_mirror_descr; - - /* set up nand options */ - nand->options = NAND_NO_SUBPAGE_WRITE; - nand->bbt_options = NAND_BBT_USE_FLASH; - - if (cspr & CSPR_PORT_SIZE_16) { - nand->read_byte = fsl_ifc_read_byte16; - nand->options |= NAND_BUSWIDTH_16; - } else { - nand->read_byte = fsl_ifc_read_byte; - } - - nand->controller = &ifc_ctrl->controller; - nand_set_controller_data(nand, priv); - - nand->ecc.read_page = fsl_ifc_read_page; - nand->ecc.write_page = fsl_ifc_write_page; - - /* Hardware generates ECC per 512 Bytes */ - nand->ecc.size = 512; - nand->ecc.bytes = 8; - - switch (csor & CSOR_NAND_PGS_MASK) { - case CSOR_NAND_PGS_512: - if (nand->options & NAND_BUSWIDTH_16) { - layout = &oob_512_16bit_ecc4; - } else { - layout = &oob_512_8bit_ecc4; - - /* Avoid conflict with bad block marker */ - bbt_main_descr.offs = 0; - bbt_mirror_descr.offs = 0; - } - - nand->ecc.strength = 4; - priv->bufnum_mask = 15; - break; - - case CSOR_NAND_PGS_2K: - layout = &oob_2048_ecc4; - nand->ecc.strength = 4; - priv->bufnum_mask = 3; - break; - - case CSOR_NAND_PGS_4K: - if ((csor & CSOR_NAND_ECC_MODE_MASK) == - CSOR_NAND_ECC_MODE_4) { - layout = &oob_4096_ecc4; - nand->ecc.strength = 4; - } else { - layout = &oob_4096_ecc8; - nand->ecc.strength = 8; - nand->ecc.bytes = 16; - } - - priv->bufnum_mask = 1; - break; - - case CSOR_NAND_PGS_8K: - if ((csor & CSOR_NAND_ECC_MODE_MASK) == - CSOR_NAND_ECC_MODE_4) { - layout = &oob_8192_ecc4; - nand->ecc.strength = 4; - } else { - layout = &oob_8192_ecc8; - nand->ecc.strength = 8; - nand->ecc.bytes = 16; - } - - priv->bufnum_mask = 0; - break; - - - default: - printf("ifc nand: bad csor %#x: bad page size\n", csor); - return -ENODEV; - } - - /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ - if (csor & CSOR_NAND_ECC_DEC_EN) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = layout; - } else { - nand->ecc.mode = NAND_ECC_SOFT; - } - - ver = ifc_in32(&gregs->ifc_rev); - if (ver >= FSL_IFC_V1_1_0) - ret = fsl_ifc_sram_init(priv, ver); - if (ret) - return ret; - - if (ver >= FSL_IFC_V2_0_0) - priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; - - ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; - - ret = nand_scan_tail(mtd); - if (ret) - return ret; - - ret = nand_register(devnum, mtd); - if (ret) - return ret; - return 0; -} - -#ifndef CONFIG_SYS_NAND_BASE_LIST -#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } -#endif - -static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = - CONFIG_SYS_NAND_BASE_LIST; - -void board_nand_init(void) -{ - int i; - - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) - fsl_ifc_chip_init(i, (u8 *)base_address[i]); -} diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c deleted file mode 100644 index 7137eb4108..0000000000 --- a/drivers/mtd/nand/fsl_ifc_spl.c +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * NAND boot for Freescale Integrated Flash Controller, NAND FCM - * - * Copyright 2011 Freescale Semiconductor, Inc. - * Author: Dipen Dudhat - */ - -#include -#include -#include -#include -#ifdef CONFIG_CHAIN_OF_TRUST -#include -#endif - -static inline int is_blank(uchar *addr, int page_size) -{ - int i; - - for (i = 0; i < page_size; i++) { - if (__raw_readb(&addr[i]) != 0xff) - return 0; - } - - /* - * For the SPL, don't worry about uncorrectable errors - * where the main area is all FFs but shouldn't be. - */ - return 1; -} - -/* returns nonzero if entire page is blank */ -static inline int check_read_ecc(uchar *buf, u32 *eccstat, - unsigned int bufnum, int page_size) -{ - u32 reg = eccstat[bufnum / 4]; - int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; - - if (errors == 0xf) { /* uncorrectable */ - /* Blank pages fail hw ECC checks */ - if (is_blank(buf, page_size)) - return 1; - - puts("ecc error\n"); - for (;;) - ; - } - - return 0; -} - -static inline struct fsl_ifc_runtime *runtime_regs_address(void) -{ - struct fsl_ifc regs = {(void *)CONFIG_SYS_IFC_ADDR, NULL}; - int ver = 0; - - ver = ifc_in32(®s.gregs->ifc_rev); - if (ver >= FSL_IFC_V2_0_0) - regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET; - else - regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET; - - return regs.rregs; -} - -static inline void nand_wait(uchar *buf, int bufnum, int page_size) -{ - struct fsl_ifc_runtime *ifc = runtime_regs_address(); - u32 status; - u32 eccstat[8]; - int bufperpage = page_size / 512; - int bufnum_end, i; - - bufnum *= bufperpage; - bufnum_end = bufnum + bufperpage - 1; - - do { - status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); - } while (!(status & IFC_NAND_EVTER_STAT_OPC)); - - if (status & IFC_NAND_EVTER_STAT_FTOER) { - puts("flash time out error\n"); - for (;;) - ; - } - - for (i = bufnum / 4; i <= bufnum_end / 4; i++) - eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); - - for (i = bufnum; i <= bufnum_end; i++) { - if (check_read_ecc(buf, eccstat, i, page_size)) - break; - } - - ifc_out32(&ifc->ifc_nand.nand_evter_stat, status); -} - -static inline int bad_block(uchar *marker, int port_size) -{ - if (port_size == 8) - return __raw_readb(marker) != 0xff; - else - return __raw_readw((u16 *)marker) != 0xffff; -} - -int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) -{ - struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR; - struct fsl_ifc_runtime *ifc = NULL; - uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; - int page_size; - int port_size; - int pages_per_blk; - int blk_size; - int bad_marker = 0; - int bufnum_mask, bufnum, ver = 0; - - int csor, cspr; - int pos = 0; - int j = 0; - - int sram_addr; - int pg_no; - uchar *dst = vdst; - - ifc = runtime_regs_address(); - - /* Get NAND Flash configuration */ - csor = CONFIG_SYS_NAND_CSOR; - cspr = CONFIG_SYS_NAND_CSPR; - - port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; - - if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) { - page_size = 8192; - bufnum_mask = 0x0; - } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) { - page_size = 4096; - bufnum_mask = 0x1; - } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) { - page_size = 2048; - bufnum_mask = 0x3; - } else { - page_size = 512; - bufnum_mask = 0xf; - - if (port_size == 8) - bad_marker = 5; - } - - ver = ifc_in32(&gregs->ifc_rev); - if (ver >= FSL_IFC_V2_0_0) - bufnum_mask = (bufnum_mask * 2) + 1; - - pages_per_blk = - 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); - - blk_size = pages_per_blk * page_size; - - /* Open Full SRAM mapping for spare are access */ - ifc_out32(&ifc->ifc_nand.ncfgr, 0x0); - - /* Clear Boot events */ - ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); - - /* Program FIR/FCR for Large/Small page */ - if (page_size > 512) { - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); - - ifc_out32(&ifc->ifc_nand.nand_fcr0, - (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); - } else { - ifc_out32(&ifc->ifc_nand.nand_fir0, - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); - ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); - - ifc_out32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); - } - - /* Program FBCR = 0 for full page read */ - ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); - - /* Read and copy u-boot on SDRAM from NAND device, In parallel - * check for Bad block if found skip it and read continue to - * next Block - */ - while (pos < uboot_size) { - int i = 0; - do { - pg_no = offs / page_size; - bufnum = pg_no & bufnum_mask; - sram_addr = bufnum * page_size * 2; - - ifc_out32(&ifc->ifc_nand.row0, pg_no); - ifc_out32(&ifc->ifc_nand.col0, 0); - /* start read */ - ifc_out32(&ifc->ifc_nand.nandseq_strt, - IFC_NAND_SEQ_STRT_FIR_STRT); - - /* wait for read to complete */ - nand_wait(&buf[sram_addr], bufnum, page_size); - - /* - * If either of the first two pages are marked bad, - * continue to the next block. - */ - if (i++ < 2 && - bad_block(&buf[sram_addr + page_size + bad_marker], - port_size)) { - puts("skipping\n"); - offs = (offs + blk_size) & ~(blk_size - 1); - pos &= ~(blk_size - 1); - break; - } - - for (j = 0; j < page_size; j++) - dst[pos + j] = __raw_readb(&buf[sram_addr + j]); - - pos += page_size; - offs += page_size; - } while ((offs & (blk_size - 1)) && (pos < uboot_size)); - } - - return 0; -} - -/* - * Main entrypoint for NAND Boot. It's necessary that SDRAM is already - * configured and available since this code loads the main U-Boot image - * from NAND into SDRAM and starts from there. - */ -void nand_boot(void) -{ - __attribute__((noreturn)) void (*uboot)(void); - /* - * Load U-Boot image from NAND into RAM - */ - nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, - CONFIG_SYS_NAND_U_BOOT_SIZE, - (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); - -#ifdef CONFIG_NAND_ENV_DST - nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, - (uchar *)CONFIG_NAND_ENV_DST); - -#ifdef CONFIG_ENV_OFFSET_REDUND - nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, - (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); -#endif -#endif - /* - * Jump to U-Boot image - */ -#ifdef CONFIG_SPL_FLUSH_IMAGE - /* - * Clean d-cache and invalidate i-cache, to - * make sure that no stale data is executed. - */ - flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); -#endif - -#ifdef CONFIG_CHAIN_OF_TRUST - /* - * U-Boot header is appended at end of U-boot image, so - * calculate U-boot header address using U-boot header size. - */ -#define CONFIG_U_BOOT_HDR_ADDR \ - ((CONFIG_SYS_NAND_U_BOOT_START + \ - CONFIG_SYS_NAND_U_BOOT_SIZE) - \ - CONFIG_U_BOOT_HDR_SIZE) - spl_validate_uboot(CONFIG_U_BOOT_HDR_ADDR, - CONFIG_SYS_NAND_U_BOOT_START); - /* - * In case of failure in validation, spl_validate_uboot would - * not return back in case of Production environment with ITS=1. - * Thus U-Boot will not start. - * In Development environment (ITS=0 and SB_EN=1), the function - * may return back in case of non-fatal failures. - */ -#endif - - uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; - uboot(); -} - -#ifndef CONFIG_SPL_NAND_INIT -void nand_init(void) -{ -} - -void nand_deselect(void) -{ -} -#endif diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c deleted file mode 100644 index dfbdbca3ae..0000000000 --- a/drivers/mtd/nand/fsl_upm.c +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * FSL UPM NAND driver - * - * Copyright (C) 2007 MontaVista Software, Inc. - * Anton Vorontsov - */ - -#include -#include -#include -#include -#include -#include -#include - -static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) -{ - clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); - (void)in_be32(upm->mxmr); -} - -static void fsl_upm_end_pattern(struct fsl_upm *upm) -{ - clrbits_be32(upm->mxmr, MxMR_OP_RUNP); - - while (in_be32(upm->mxmr) & MxMR_OP_RUNP) - eieio(); -} - -static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, - void __iomem *io_addr, u32 mar) -{ - out_be32(upm->mar, mar); - (void)in_be32(upm->mar); - switch (width) { - case 8: - out_8(io_addr, 0x0); - break; - case 16: - out_be16(io_addr, 0x0); - break; - case 32: - out_be32(io_addr, 0x0); - break; - } -} - -static void fun_wait(struct fsl_upm_nand *fun) -{ - if (fun->dev_ready) { - while (!fun->dev_ready(fun->chip_nr)) - debug("unexpected busy state\n"); - } else { - /* - * If the R/B pin is not connected, - * a short delay is necessary. - */ - udelay(1); - } -} - -#if CONFIG_SYS_NAND_MAX_CHIPS > 1 -static void fun_select_chip(struct mtd_info *mtd, int chip_nr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = nand_get_controller_data(chip); - - if (chip_nr >= 0) { - fun->chip_nr = chip_nr; - chip->IO_ADDR_R = chip->IO_ADDR_W = - fun->upm.io_addr + fun->chip_offset * chip_nr; - } else if (chip_nr == -1) { - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - } -} -#endif - -static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = nand_get_controller_data(chip); - void __iomem *io_addr; - u32 mar; - - if (!(ctrl & fun->last_ctrl)) { - fsl_upm_end_pattern(&fun->upm); - - if (cmd == NAND_CMD_NONE) - return; - - fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); - } - - if (ctrl & NAND_CTRL_CHANGE) { - if (ctrl & NAND_ALE) - fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); - else if (ctrl & NAND_CLE) - fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); - } - - mar = cmd << (32 - fun->width); - io_addr = fun->upm.io_addr; -#if CONFIG_SYS_NAND_MAX_CHIPS > 1 - if (fun->chip_nr > 0) { - io_addr += fun->chip_offset * fun->chip_nr; - if (fun->upm_mar_chip_offset) - mar |= fun->upm_mar_chip_offset * fun->chip_nr; - } -#endif - fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar); - - /* - * Some boards/chips needs this. At least the MPC8360E-RDK - * needs it. Probably weird chip, because I don't see any - * need for this on MPC8555E + Samsung K9F1G08U0A. Usually - * here are 0-2 unexpected busy states per block read. - */ - if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) - fun_wait(fun); -} - -static u8 upm_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - return in_8(chip->IO_ADDR_R); -} - -static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = nand_get_controller_data(chip); - - for (i = 0; i < len; i++) { - out_8(chip->IO_ADDR_W, buf[i]); - if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) - fun_wait(fun); - } - - if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) - fun_wait(fun); -} - -static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - - for (i = 0; i < len; i++) - buf[i] = in_8(chip->IO_ADDR_R); -} - -static int nand_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct fsl_upm_nand *fun = nand_get_controller_data(chip); - - return fun->dev_ready(fun->chip_nr); -} - -int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) -{ - if (fun->width != 8 && fun->width != 16 && fun->width != 32) - return -ENOSYS; - - fun->last_ctrl = NAND_CLE; - - nand_set_controller_data(chip, fun); - chip->chip_delay = fun->chip_delay; - chip->ecc.mode = NAND_ECC_SOFT; - chip->cmd_ctrl = fun_cmd_ctrl; -#if CONFIG_SYS_NAND_MAX_CHIPS > 1 - chip->select_chip = fun_select_chip; -#endif - chip->read_byte = upm_nand_read_byte; - chip->read_buf = upm_nand_read_buf; - chip->write_buf = upm_nand_write_buf; - if (fun->dev_ready) - chip->dev_ready = nand_dev_ready; - - return 0; -} diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c deleted file mode 100644 index 1f4c74f0f6..0000000000 --- a/drivers/mtd/nand/fsmc_nand.c +++ /dev/null @@ -1,518 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2010 - * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. - * - * (C) Copyright 2012 - * Amit Virdi, ST Microelectronics, amit.virdi@st.com. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static u32 fsmc_version; -static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) - CONFIG_SYS_FSMC_BASE; - -/* - * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of - * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can - * correct 1 bit in 512 bytes - */ - -static struct nand_ecclayout fsmc_ecc4_lp_layout = { - .eccbytes = 104, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, - 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, - 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, - 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126 - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 3}, - {.offset = 79, .length = 3}, - {.offset = 95, .length = 3}, - {.offset = 111, .length = 3}, - {.offset = 127, .length = 1} - } -}; - -/* - * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes - * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 - * bytes are free for use. - */ -static struct nand_ecclayout fsmc_ecc4_224_layout = { - .eccbytes = 104, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, - 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, - 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, - 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126 - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 3}, - {.offset = 79, .length = 3}, - {.offset = 95, .length = 3}, - {.offset = 111, .length = 3}, - {.offset = 127, .length = 97} - } -}; - -/* - * ECC placement definitions in oobfree type format - * There are 13 bytes of ecc for every 512 byte block and it has to be read - * consecutively and immediately after the 512 byte data block for hardware to - * generate the error bit offsets in 512 byte data - * Managing the ecc bytes in the following way makes it easier for software to - * read ecc bytes consecutive to data bytes. This way is similar to - * oobfree structure maintained already in u-boot nand driver - */ -static struct fsmc_eccplace fsmc_eccpl_lp = { - .eccplace = { - {.offset = 2, .length = 13}, - {.offset = 18, .length = 13}, - {.offset = 34, .length = 13}, - {.offset = 50, .length = 13}, - {.offset = 66, .length = 13}, - {.offset = 82, .length = 13}, - {.offset = 98, .length = 13}, - {.offset = 114, .length = 13} - } -}; - -static struct nand_ecclayout fsmc_ecc4_sp_layout = { - .eccbytes = 13, - .eccpos = { 0, 1, 2, 3, 6, 7, 8, - 9, 10, 11, 12, 13, 14 - }, - .oobfree = { - {.offset = 15, .length = 1}, - } -}; - -static struct fsmc_eccplace fsmc_eccpl_sp = { - .eccplace = { - {.offset = 0, .length = 4}, - {.offset = 6, .length = 9} - } -}; - -static struct nand_ecclayout fsmc_ecc1_layout = { - .eccbytes = 24, - .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, - 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, - .oobfree = { - {.offset = 8, .length = 8}, - {.offset = 24, .length = 8}, - {.offset = 40, .length = 8}, - {.offset = 56, .length = 8}, - {.offset = 72, .length = 8}, - {.offset = 88, .length = 8}, - {.offset = 104, .length = 8}, - {.offset = 120, .length = 8} - } -}; - -/* Count the number of 0's in buff upto a max of max_bits */ -static int count_written_bits(uint8_t *buff, int size, int max_bits) -{ - int k, written_bits = 0; - - for (k = 0; k < size; k++) { - written_bits += hweight8(~buff[k]); - if (written_bits > max_bits) - break; - } - - return written_bits; -} - -static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - ulong IO_ADDR_W; - - if (ctrl & NAND_CTRL_CHANGE) { - IO_ADDR_W = (ulong)this->IO_ADDR_W; - - IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); - if (ctrl & NAND_CLE) - IO_ADDR_W |= CONFIG_SYS_NAND_CLE; - if (ctrl & NAND_ALE) - IO_ADDR_W |= CONFIG_SYS_NAND_ALE; - - if (ctrl & NAND_NCE) { - writel(readl(&fsmc_regs_p->pc) | - FSMC_ENABLE, &fsmc_regs_p->pc); - } else { - writel(readl(&fsmc_regs_p->pc) & - ~FSMC_ENABLE, &fsmc_regs_p->pc); - } - this->IO_ADDR_W = (void *)IO_ADDR_W; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - /* The calculated ecc is actually the correction index in data */ - u32 err_idx[8]; - u32 num_err, i; - u32 ecc1, ecc2, ecc3, ecc4; - - num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; - - if (likely(num_err == 0)) - return 0; - - if (unlikely(num_err > 8)) { - /* - * This is a temporary erase check. A newly erased page read - * would result in an ecc error because the oob data is also - * erased to FF and the calculated ecc for an FF data is not - * FF..FF. - * This is a workaround to skip performing correction in case - * data is FF..FF - * - * Logic: - * For every page, each bit written as 0 is counted until these - * number of bits are greater than 8 (the maximum correction - * capability of FSMC for each 512 + 13 bytes) - */ - - int bits_ecc = count_written_bits(read_ecc, 13, 8); - int bits_data = count_written_bits(dat, 512, 8); - - if ((bits_ecc + bits_data) <= 8) { - if (bits_data) - memset(dat, 0xff, 512); - return bits_data + bits_ecc; - } - - return -EBADMSG; - } - - ecc1 = readl(&fsmc_regs_p->ecc1); - ecc2 = readl(&fsmc_regs_p->ecc2); - ecc3 = readl(&fsmc_regs_p->ecc3); - ecc4 = readl(&fsmc_regs_p->sts); - - err_idx[0] = (ecc1 >> 0) & 0x1FFF; - err_idx[1] = (ecc1 >> 13) & 0x1FFF; - err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); - err_idx[3] = (ecc2 >> 7) & 0x1FFF; - err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); - err_idx[5] = (ecc3 >> 1) & 0x1FFF; - err_idx[6] = (ecc3 >> 14) & 0x1FFF; - err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); - - i = 0; - while (i < num_err) { - err_idx[i] ^= 3; - - if (err_idx[i] < 512 * 8) - __change_bit(err_idx[i], dat); - - i++; - } - - return num_err; -} - -static int fsmc_read_hwecc(struct mtd_info *mtd, - const u_char *data, u_char *ecc) -{ - u_int ecc_tmp; - int timeout = CONFIG_SYS_HZ; - ulong start; - - switch (fsmc_version) { - case FSMC_VER8: - start = get_timer(0); - while (get_timer(start) < timeout) { - /* - * Busy waiting for ecc computation - * to finish for 512 bytes - */ - if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) - break; - } - - ecc_tmp = readl(&fsmc_regs_p->ecc1); - ecc[0] = (u_char) (ecc_tmp >> 0); - ecc[1] = (u_char) (ecc_tmp >> 8); - ecc[2] = (u_char) (ecc_tmp >> 16); - ecc[3] = (u_char) (ecc_tmp >> 24); - - ecc_tmp = readl(&fsmc_regs_p->ecc2); - ecc[4] = (u_char) (ecc_tmp >> 0); - ecc[5] = (u_char) (ecc_tmp >> 8); - ecc[6] = (u_char) (ecc_tmp >> 16); - ecc[7] = (u_char) (ecc_tmp >> 24); - - ecc_tmp = readl(&fsmc_regs_p->ecc3); - ecc[8] = (u_char) (ecc_tmp >> 0); - ecc[9] = (u_char) (ecc_tmp >> 8); - ecc[10] = (u_char) (ecc_tmp >> 16); - ecc[11] = (u_char) (ecc_tmp >> 24); - - ecc_tmp = readl(&fsmc_regs_p->sts); - ecc[12] = (u_char) (ecc_tmp >> 16); - break; - - default: - ecc_tmp = readl(&fsmc_regs_p->ecc1); - ecc[0] = (u_char) (ecc_tmp >> 0); - ecc[1] = (u_char) (ecc_tmp >> 8); - ecc[2] = (u_char) (ecc_tmp >> 16); - break; - } - - return 0; -} - -void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, - &fsmc_regs_p->pc); - writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, - &fsmc_regs_p->pc); - writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, - &fsmc_regs_p->pc); -} - -/* - * fsmc_read_page_hwecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller expects OOB data read to chip->oob_poi - * @page: page number to read - * - * This routine is needed for fsmc verison 8 as reading from NAND chip has to be - * performed in a strict sequence as follows: - * data(512 byte) -> ecc(13 byte) - * After this read, fsmc hardware generates and reports error data bits(upto a - * max of 8 bits) - */ -static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - struct fsmc_eccplace *fsmc_eccpl; - int i, j, s, stat, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - int off, len, group = 0; - uint8_t oob[13] __attribute__ ((aligned (2))); - - /* Differentiate between small and large page ecc place definitions */ - if (mtd->writesize == 512) - fsmc_eccpl = &fsmc_eccpl_sp; - else - fsmc_eccpl = &fsmc_eccpl_lp; - - for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - - chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - - for (j = 0; j < eccbytes;) { - off = fsmc_eccpl->eccplace[group].offset; - len = fsmc_eccpl->eccplace[group].length; - group++; - - /* - * length is intentionally kept a higher multiple of 2 - * to read at least 13 bytes even in case of 16 bit NAND - * devices - */ - if (chip->options & NAND_BUSWIDTH_16) - len = roundup(len, 2); - chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); - chip->read_buf(mtd, oob + j, len); - j += len; - } - - memcpy(&ecc_code[i], oob, 13); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], - &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - - return 0; -} - -#ifndef CONFIG_SPL_BUILD -/* - * fsmc_nand_switch_ecc - switch the ECC operation between different engines - * - * @eccstrength - the number of bits that could be corrected - * (1 - HW, 4 - SW BCH4) - */ -int fsmc_nand_switch_ecc(uint32_t eccstrength) -{ - struct nand_chip *nand; - struct mtd_info *mtd; - int err; - - /* - * This functions is only called on SPEAr600 platforms, supporting - * 1 bit HW ECC. The BCH8 HW ECC (FSMC_VER8) from the ST-Ericsson - * Nomadik SoC is currently supporting this fsmc_nand_switch_ecc() - * function, as it doesn't need to switch to a different ECC layout. - */ - mtd = get_nand_dev_by_index(nand_curr_device); - nand = mtd_to_nand(mtd); - - /* Setup the ecc configurations again */ - if (eccstrength == 1) { - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - nand->ecc.layout = &fsmc_ecc1_layout; - nand->ecc.calculate = fsmc_read_hwecc; - nand->ecc.correct = nand_correct_data; - } else if (eccstrength == 4) { - /* - * .calculate .correct and .bytes will be set in - * nand_scan_tail() - */ - nand->ecc.mode = NAND_ECC_SOFT_BCH; - nand->ecc.strength = 4; - nand->ecc.layout = NULL; - } else { - printf("Error: ECC strength %d not supported!\n", eccstrength); - } - - /* Update NAND handling after ECC mode switch */ - err = nand_scan_tail(mtd); - - return err; -} -#endif /* CONFIG_SPL_BUILD */ - -int fsmc_nand_init(struct nand_chip *nand) -{ - static int chip_nr; - struct mtd_info *mtd; - u32 peripid2 = readl(&fsmc_regs_p->peripid2); - - fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & - FSMC_REVISION_MSK; - - writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); - -#if defined(CONFIG_SYS_FSMC_NAND_16BIT) - writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, - &fsmc_regs_p->pc); -#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) - writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, - &fsmc_regs_p->pc); -#else -#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT -#endif - writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, - &fsmc_regs_p->pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->attrib); - - nand->options = 0; -#if defined(CONFIG_SYS_FSMC_NAND_16BIT) - nand->options |= NAND_BUSWIDTH_16; -#endif - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = 512; - nand->ecc.calculate = fsmc_read_hwecc; - nand->ecc.hwctl = fsmc_enable_hwecc; - nand->cmd_ctrl = fsmc_nand_hwcontrol; - nand->IO_ADDR_R = nand->IO_ADDR_W = - (void __iomem *)CONFIG_SYS_NAND_BASE; - nand->badblockbits = 7; - - mtd = nand_to_mtd(nand); - - switch (fsmc_version) { - case FSMC_VER8: - nand->ecc.bytes = 13; - nand->ecc.strength = 8; - nand->ecc.correct = fsmc_bch8_correct_data; - nand->ecc.read_page = fsmc_read_page_hwecc; - if (mtd->writesize == 512) - nand->ecc.layout = &fsmc_ecc4_sp_layout; - else { - if (mtd->oobsize == 224) - nand->ecc.layout = &fsmc_ecc4_224_layout; - else - nand->ecc.layout = &fsmc_ecc4_lp_layout; - } - - break; - default: - nand->ecc.bytes = 3; - nand->ecc.strength = 1; - nand->ecc.layout = &fsmc_ecc1_layout; - nand->ecc.correct = nand_correct_data; - break; - } - - /* Detect NAND chips */ - if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) - return -ENXIO; - - if (nand_scan_tail(mtd)) - return -ENXIO; - - if (nand_register(chip_nr++, mtd)) - return -ENXIO; - - return 0; -} diff --git a/drivers/mtd/nand/kb9202_nand.c b/drivers/mtd/nand/kb9202_nand.c deleted file mode 100644 index 0f68f1cd86..0000000000 --- a/drivers/mtd/nand/kb9202_nand.c +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2006 - * KwikByte - * - * (C) Copyright 2009 - * Matthias Kaehlcke - */ - -#include -#include -#include -#include - -#include - -/* - * hardware specific access to control-lines - */ - -#define MASK_ALE (1 << 22) /* our ALE is A22 */ -#define MASK_CLE (1 << 21) /* our CLE is A21 */ - -#define KB9202_NAND_NCE (1 << 28) /* EN* on D28 */ -#define KB9202_NAND_BUSY (1 << 29) /* RB* on D29 */ - -#define KB9202_SMC2_NWS (1 << 2) -#define KB9202_SMC2_TDF (1 << 8) -#define KB9202_SMC2_RWSETUP (1 << 24) -#define KB9202_SMC2_RWHOLD (1 << 29) - -/* - * Board-specific function to access device control signals - */ -static void kb9202_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - if (ctrl & NAND_CTRL_CHANGE) { - ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; - - /* clear ALE and CLE bits */ - IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); - - if (ctrl & NAND_CLE) - IO_ADDR_W |= MASK_CLE; - - if (ctrl & NAND_ALE) - IO_ADDR_W |= MASK_ALE; - - this->IO_ADDR_W = (void *) IO_ADDR_W; - - if (ctrl & NAND_NCE) - writel(KB9202_NAND_NCE, AT91C_PIOC_CODR); - else - writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - - -/* - * Board-specific function to access the device ready signal. - */ -static int kb9202_nand_ready(struct mtd_info *mtd) -{ - return readl(AT91C_PIOC_PDSR) & KB9202_NAND_BUSY; -} - - -/* - * Board-specific NAND init. Copied from include/linux/mtd/nand.h for reference. - * - * struct nand_chip - NAND Private Flash Chip Data - * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device - * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device - * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines - * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line - * If set to NULL no access to ready/busy is available and the ready/busy information - * is read from the chip status register - * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only - * be provided if a hardware ECC is available - * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines - * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) - * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about - * special functionality. See the defines for further explanation -*/ -/* - * This routine initializes controller and GPIOs. - */ -int board_nand_init(struct nand_chip *nand) -{ - unsigned int value; - - nand->ecc.mode = NAND_ECC_SOFT; - nand->cmd_ctrl = kb9202_nand_hwcontrol; - nand->dev_ready = kb9202_nand_ready; - - /* in case running outside of bootloader */ - writel(1 << AT91C_ID_PIOC, AT91C_PMC_PCER); - - /* setup nand flash access (allow ample margin) */ - /* 4 wait states, 1 setup, 1 hold, 1 float for 8-bit device */ - writel(AT91C_SMC2_WSEN | KB9202_SMC2_NWS | KB9202_SMC2_TDF | - AT91C_SMC2_DBW_8 | KB9202_SMC2_RWSETUP | KB9202_SMC2_RWHOLD, - AT91C_SMC_CSR3); - - /* enable internal NAND controller */ - value = readl(AT91C_EBI_CSA); - value |= AT91C_EBI_CS3A_SMC_SmartMedia; - writel(value, AT91C_EBI_CSA); - - /* enable SMOE/SMWE */ - writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_ASR); - writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_PDR); - writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_OER); - - /* set NCE to high */ - writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); - - /* disable output on pin connected to the busy line of the NAND */ - writel(KB9202_NAND_BUSY, AT91C_PIOC_ODR); - - /* enable the PIO to control NCE and BUSY */ - writel(KB9202_NAND_NCE | KB9202_NAND_BUSY, AT91C_PIOC_PER); - - /* enable output for NCE */ - writel(KB9202_NAND_NCE, AT91C_PIOC_OER); - - return (0); -} diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c deleted file mode 100644 index 0757fa840b..0000000000 --- a/drivers/mtd/nand/kirkwood_nand.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2009 - * Marvell Semiconductor - * Written-by: Prafulla Wadaskar - */ - -#include -#include -#include -#include -#include - -/* NAND Flash Soc registers */ -struct kwnandf_registers { - u32 rd_params; /* 0x10418 */ - u32 wr_param; /* 0x1041c */ - u8 pad[0x10470 - 0x1041c - 4]; - u32 ctrl; /* 0x10470 */ -}; - -static struct kwnandf_registers *nf_reg = - (struct kwnandf_registers *)KW_NANDF_BASE; - -static u32 nand_mpp_backup[9] = { 0 }; - -/* - * hardware specific access to control-lines/bits - */ -#define NAND_ACTCEBOOT_BIT 0x02 - -static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *nc = mtd_to_nand(mtd); - u32 offs; - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - offs = (1 << 0); /* Commands with A[1:0] == 01 */ - else if (ctrl & NAND_ALE) - offs = (1 << 1); /* Addresses with A[1:0] == 10 */ - else - return; - - writeb(cmd, nc->IO_ADDR_W + offs); -} - -void kw_nand_select_chip(struct mtd_info *mtd, int chip) -{ - u32 data; - static const u32 nand_config[] = { - MPP0_NF_IO2, - MPP1_NF_IO3, - MPP2_NF_IO4, - MPP3_NF_IO5, - MPP4_NF_IO6, - MPP5_NF_IO7, - MPP18_NF_IO0, - MPP19_NF_IO1, - 0 - }; - - if (chip >= 0) - kirkwood_mpp_conf(nand_config, nand_mpp_backup); - else - kirkwood_mpp_conf(nand_mpp_backup, NULL); - - data = readl(&nf_reg->ctrl); - data |= NAND_ACTCEBOOT_BIT; - writel(data, &nf_reg->ctrl); -} - -int board_nand_init(struct nand_chip *nand) -{ - nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; -#if defined(CONFIG_SYS_NAND_NO_SUBPAGE_WRITE) - nand->options |= NAND_NO_SUBPAGE_WRITE; -#endif -#if defined(CONFIG_NAND_ECC_BCH) - nand->ecc.mode = NAND_ECC_SOFT_BCH; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif - nand->cmd_ctrl = kw_nand_hwcontrol; - nand->chip_delay = 40; - nand->select_chip = kw_nand_select_chip; - return 0; -} diff --git a/drivers/mtd/nand/kmeter1_nand.c b/drivers/mtd/nand/kmeter1_nand.c deleted file mode 100644 index 7103300060..0000000000 --- a/drivers/mtd/nand/kmeter1_nand.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2009 - * Heiko Schocher, DENX Software Engineering, hs@denx.de - */ - -#include -#include -#include - -#define CONFIG_NAND_MODE_REG (void *)(CONFIG_SYS_NAND_BASE + 0x20000) -#define CONFIG_NAND_DATA_REG (void *)(CONFIG_SYS_NAND_BASE + 0x30000) - -#define read_mode() in_8(CONFIG_NAND_MODE_REG) -#define write_mode(val) out_8(CONFIG_NAND_MODE_REG, val) -#define read_data() in_8(CONFIG_NAND_DATA_REG) -#define write_data(val) out_8(CONFIG_NAND_DATA_REG, val) - -#define KPN_RDY2 (1 << 7) -#define KPN_RDY1 (1 << 6) -#define KPN_WPN (1 << 4) -#define KPN_CE2N (1 << 3) -#define KPN_CE1N (1 << 2) -#define KPN_ALE (1 << 1) -#define KPN_CLE (1 << 0) - -#define KPN_DEFAULT_CHIP_DELAY 50 - -static int kpn_chip_ready(void) -{ - if (read_mode() & KPN_RDY1) - return 1; - - return 0; -} - -static void kpn_wait_rdy(void) -{ - int cnt = 1000000; - - while (--cnt && !kpn_chip_ready()) - udelay(1); - - if (!cnt) - printf ("timeout while waiting for RDY\n"); -} - -static void kpn_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - u8 reg_val = read_mode(); - - if (ctrl & NAND_CTRL_CHANGE) { - reg_val = reg_val & ~(KPN_ALE + KPN_CLE); - - if (ctrl & NAND_CLE) - reg_val = reg_val | KPN_CLE; - if (ctrl & NAND_ALE) - reg_val = reg_val | KPN_ALE; - if (ctrl & NAND_NCE) - reg_val = reg_val & ~KPN_CE1N; - else - reg_val = reg_val | KPN_CE1N; - - write_mode(reg_val); - } - if (cmd != NAND_CMD_NONE) - write_data(cmd); - - /* wait until flash is ready */ - kpn_wait_rdy(); -} - -static u_char kpn_nand_read_byte(struct mtd_info *mtd) -{ - return read_data(); -} - -static void kpn_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) { - write_data(buf[i]); - kpn_wait_rdy(); - } -} - -static void kpn_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = read_data(); -} - -static int kpn_nand_dev_ready(struct mtd_info *mtd) -{ - kpn_wait_rdy(); - - return 1; -} - -int board_nand_init(struct nand_chip *nand) -{ -#if defined(CONFIG_NAND_ECC_BCH) - nand->ecc.mode = NAND_ECC_SOFT_BCH; -#else - nand->ecc.mode = NAND_ECC_SOFT; -#endif - - /* Reference hardware control function */ - nand->cmd_ctrl = kpn_nand_hwcontrol; - nand->read_byte = kpn_nand_read_byte; - nand->write_buf = kpn_nand_write_buf; - nand->read_buf = kpn_nand_read_buf; - nand->dev_ready = kpn_nand_dev_ready; - nand->chip_delay = KPN_DEFAULT_CHIP_DELAY; - - /* reset mode register */ - write_mode(KPN_CE1N + KPN_CE2N + KPN_WPN); - return 0; -} diff --git a/drivers/mtd/nand/lpc32xx_nand_mlc.c b/drivers/mtd/nand/lpc32xx_nand_mlc.c deleted file mode 100644 index 5d4ffea608..0000000000 --- a/drivers/mtd/nand/lpc32xx_nand_mlc.c +++ /dev/null @@ -1,761 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * LPC32xx MLC NAND flash controller driver - * - * (C) Copyright 2014 3ADEV - * Written by Albert ARIBAUD - * - * NOTE: - * - * The MLC NAND flash controller provides hardware Reed-Solomon ECC - * covering in- and out-of-band data together. Therefore, in- and out- - * of-band data must be written together in order to have a valid ECC. - * - * Consequently, pages with meaningful in-band data are written with - * blank (all-ones) out-of-band data and a valid ECC, and any later - * out-of-band data write will void the ECC. - * - * Therefore, code which reads such late-written out-of-band data - * should not rely on the ECC validity. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * MLC NAND controller registers. - */ -struct lpc32xx_nand_mlc_registers { - u8 buff[32768]; /* controller's serial data buffer */ - u8 data[32768]; /* NAND's raw data buffer */ - u32 cmd; - u32 addr; - u32 ecc_enc_reg; - u32 ecc_dec_reg; - u32 ecc_auto_enc_reg; - u32 ecc_auto_dec_reg; - u32 rpr; - u32 wpr; - u32 rubp; - u32 robp; - u32 sw_wp_add_low; - u32 sw_wp_add_hig; - u32 icr; - u32 time_reg; - u32 irq_mr; - u32 irq_sr; - u32 lock_pr; - u32 isr; - u32 ceh; -}; - -/* LOCK_PR register defines */ -#define LOCK_PR_UNLOCK_KEY 0x0000A25E /* Magic unlock value */ - -/* ICR defines */ -#define ICR_LARGE_BLOCKS 0x00000004 /* configure for 2KB blocks */ -#define ICR_ADDR4 0x00000002 /* configure for 4-word addrs */ - -/* CEH defines */ -#define CEH_NORMAL_CE 0x00000001 /* do not force CE ON */ - -/* ISR register defines */ -#define ISR_NAND_READY 0x00000001 -#define ISR_CONTROLLER_READY 0x00000002 -#define ISR_ECC_READY 0x00000004 -#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1) -#define ISR_DECODER_FAILURE 0x00000040 -#define ISR_DECODER_ERROR 0x00000008 - -/* time-out for NAND chip / controller loops, in us */ -#define LPC32X_NAND_TIMEOUT 5000 - -/* - * There is a single instance of the NAND MLC controller - */ - -static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers - = (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE; - -#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o) - -/** - * OOB data in each small page are 6 'free' then 10 ECC bytes. - * To make things easier, when reading large pages, the four pages' - * 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer, - * while the the four ECC bytes are groupe in its last 40 bytes. - * - * The struct below represents how free vs ecc oob bytes are stored - * in the buffer. - * - * Note: the OOB bytes contain the bad block marker at offsets 0 and 1. - */ - -struct lpc32xx_oob { - struct { - uint8_t free_oob_bytes[6]; - } free[4]; - struct { - uint8_t ecc_oob_bytes[10]; - } ecc[4]; -}; - -/* - * Initialize the controller - */ - -static void lpc32xx_nand_init(void) -{ - unsigned int clk; - - /* Configure controller for no software write protection, x8 bus - width, large block device, and 4 address words */ - - /* unlock controller registers with magic key */ - writel(LOCK_PR_UNLOCK_KEY, - &lpc32xx_nand_mlc_registers->lock_pr); - - /* enable large blocks and large NANDs */ - writel(ICR_LARGE_BLOCKS | ICR_ADDR4, - &lpc32xx_nand_mlc_registers->icr); - - /* Make sure MLC interrupts are disabled */ - writel(0, &lpc32xx_nand_mlc_registers->irq_mr); - - /* Normal chip enable operation */ - writel(CEH_NORMAL_CE, - &lpc32xx_nand_mlc_registers->ceh); - - /* Setup NAND timing */ - clk = get_hclk_clk_rate(); - - writel( - clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA, 0x07, 16) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH, 0x0F, 12) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW, 0x0F, 8) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH, 0x0F, 4) | - clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW, 0x0F, 0), - &lpc32xx_nand_mlc_registers->time_reg); -} - -#if !defined(CONFIG_SPL_BUILD) - -/** - * lpc32xx_cmd_ctrl - write command to either cmd or data register - */ - -static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd); - else if (ctrl & NAND_ALE) - writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr); -} - -/** - * lpc32xx_read_byte - read a byte from the NAND - * @mtd: MTD device structure - */ - -static uint8_t lpc32xx_read_byte(struct mtd_info *mtd) -{ - return readb(&lpc32xx_nand_mlc_registers->data); -} - -/** - * lpc32xx_dev_ready - test if NAND device (actually controller) is ready - * @mtd: MTD device structure - * @mode: mode to set the ECC HW to. - */ - -static int lpc32xx_dev_ready(struct mtd_info *mtd) -{ - /* means *controller* ready for us */ - int status = readl(&lpc32xx_nand_mlc_registers->isr); - return status & ISR_CONTROLLER_READY; -} - -/** - * ECC layout -- this is needed whatever ECC mode we are using. - * In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes. - * To make U-Boot's life easier, we pack 'useable' OOB at the - * front and R/S ECC at the back. - */ - -static struct nand_ecclayout lpc32xx_largepage_ecclayout = { - .eccbytes = 40, - .eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 48, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - }, - .oobfree = { - /* bytes 0 and 1 are used for the bad block marker */ - { - .offset = 2, - .length = 22 - }, - } -}; - -/** - * lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Use large block Auto Decode Read Mode(1) as described in User Manual - * section 8.6.2.1. - * - * The initial Read Mode and Read Start commands are sent by the caller. - * - * ECC will be false if out-of-band data has been updated since in-band - * data was initially written. - */ - -static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, - int page) -{ - unsigned int i, status, timeout, err, max_bitflips = 0; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - /* go through all four small pages */ - for (i = 0; i < 4; i++) { - /* start auto decode (reads 528 NAND bytes) */ - writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); - /* wait for controller to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_CONTROLLER_READY) - break; - udelay(1); - } - /* if decoder failed, return failure */ - if (status & ISR_DECODER_FAILURE) - return -1; - /* keep count of maximum bitflips performed */ - if (status & ISR_DECODER_ERROR) { - err = ISR_DECODER_ERRORS(status); - if (err > max_bitflips) - max_bitflips = err; - } - /* copy first 512 bytes into buffer */ - memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512); - /* copy next 6 bytes at front of OOB buffer */ - memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); - /* copy last 10 bytes (R/S ECC) at back of OOB buffer */ - memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10); - } - return max_bitflips; -} - -/** - * lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Read NAND directly; can read pages with invalid ECC. - */ - -static int lpc32xx_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, - int page) -{ - unsigned int i, status, timeout; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - /* when we get here we've already had the Read Mode(1) */ - - /* go through all four small pages */ - for (i = 0; i < 4; i++) { - /* wait for NAND to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_NAND_READY) - break; - udelay(1); - } - /* if NAND stalled, return failure */ - if (!(status & ISR_NAND_READY)) - return -1; - /* copy first 512 bytes into buffer */ - memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512); - /* copy next 6 bytes at front of OOB buffer */ - memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6); - /* copy last 10 bytes (R/S ECC) at back of OOB buffer */ - memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10); - } - return 0; -} - -/** - * lpc32xx_read_oob - read out-of-band data - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * - * Read out-of-band data. User Manual section 8.6.4 suggests using Read - * Mode(3) which the controller will turn into a Read Mode(1) internally - * but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0) - * directly. - * - * ECC covers in- and out-of-band data and was written when out-of-band - * data was blank. Therefore, if the out-of-band being read here is not - * blank, then the ECC will be false and the read will return bitflips, - * even in case of ECC failure where we will return 5 bitflips. The - * caller should be prepared to handle this. - */ - -static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - unsigned int i, status, timeout, err, max_bitflips = 0; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - /* No command was sent before calling read_oob() so send one */ - - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - /* go through all four small pages */ - for (i = 0; i < 4; i++) { - /* start auto decode (reads 528 NAND bytes) */ - writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); - /* wait for controller to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_CONTROLLER_READY) - break; - udelay(1); - } - /* if decoder failure, count 'one too many' bitflips */ - if (status & ISR_DECODER_FAILURE) - max_bitflips = 5; - /* keep count of maximum bitflips performed */ - if (status & ISR_DECODER_ERROR) { - err = ISR_DECODER_ERRORS(status); - if (err > max_bitflips) - max_bitflips = err; - } - /* set read pointer to OOB area */ - writel(0, &lpc32xx_nand_mlc_registers->robp); - /* copy next 6 bytes at front of OOB buffer */ - memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); - /* copy next 10 bytes (R/S ECC) at back of OOB buffer */ - memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10); - } - return max_bitflips; -} - -/** - * lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * - * Use large block Auto Encode as per User Manual section 8.6.4. - * - * The initial Write Serial Input and final Auto Program commands are - * sent by the caller. - */ - -static int lpc32xx_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - unsigned int i, status, timeout; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - /* when we get here we've already had the SEQIN */ - for (i = 0; i < 4; i++) { - /* start encode (expects 518 writes to buff) */ - writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg); - /* copy first 512 bytes from buffer */ - memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512); - /* copy next 6 bytes from OOB buffer -- excluding ECC */ - memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6); - /* wait for ECC to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_ECC_READY) - break; - udelay(1); - } - /* if ECC stalled, return failure */ - if (!(status & ISR_ECC_READY)) - return -1; - /* Trigger auto encode (writes 528 bytes to NAND) */ - writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg); - /* wait for controller to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_CONTROLLER_READY) - break; - udelay(1); - } - /* if controller stalled, return error */ - if (!(status & ISR_CONTROLLER_READY)) - return -1; - } - return 0; -} - -/** - * lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Use large block write but without encode. - * - * The initial Write Serial Input and final Auto Program commands are - * sent by the caller. - * - * This function will write the full out-of-band data, including the - * ECC area. Therefore, it can write pages with valid *or* invalid ECC. - */ - -static int lpc32xx_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - unsigned int i; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - /* when we get here we've already had the Read Mode(1) */ - for (i = 0; i < 4; i++) { - /* copy first 512 bytes from buffer */ - memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512); - /* copy next 6 bytes into OOB buffer -- excluding ECC */ - memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6); - /* copy next 10 bytes into OOB buffer -- that is 'ECC' */ - memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10); - } - return 0; -} - -/** - * lpc32xx_write_oob - write out-of-band data - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * - * Since ECC covers in- and out-of-band data, writing out-of-band data - * with ECC will render the page ECC wrong -- or, if the page was blank, - * then it will produce a good ECC but a later in-band data write will - * render it wrong. - * - * Therefore, do not compute or write any ECC, and always return success. - * - * This implies that we do four writes, since non-ECC out-of-band data - * are not contiguous in a large page. - */ - -static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - /* update oob on all 4 subpages in sequence */ - unsigned int i, status, timeout; - struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; - - for (i = 0; i < 4; i++) { - /* start data input */ - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page); - /* copy 6 non-ECC out-of-band bytes directly into NAND */ - memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6); - /* program page */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - /* wait for NAND to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_NAND_READY) - break; - udelay(1); - } - /* if NAND stalled, return error */ - if (!(status & ISR_NAND_READY)) - return -1; - } - return 0; -} - -/** - * lpc32xx_waitfunc - wait until a command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * - * Wait for controller and FLASH to both be ready. - */ - -static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) -{ - int status; - unsigned int timeout; - /* wait until both controller and NAND are ready */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY)) - == (ISR_CONTROLLER_READY || ISR_NAND_READY)) - break; - udelay(1); - } - /* if controller or NAND stalled, return error */ - if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY)) - != (ISR_CONTROLLER_READY || ISR_NAND_READY)) - return -1; - /* write NAND status command */ - writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd); - /* read back status and return it */ - return readb(&lpc32xx_nand_mlc_registers->data); -} - -/* - * We are self-initializing, so we need our own chip struct - */ - -static struct nand_chip lpc32xx_chip; - -/* - * Initialize the controller - */ - -void board_nand_init(void) -{ - struct mtd_info *mtd = nand_to_mtd(&lpc32xx_chip); - int ret; - - /* Set all BOARDSPECIFIC (actually core-specific) fields */ - - lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff; - lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff; - lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl; - /* do not set init_size: nand_base.c will read sizes from chip */ - lpc32xx_chip.dev_ready = lpc32xx_dev_ready; - /* do not set setup_read_retry: this is NAND-chip-specific */ - /* do not set chip_delay: we have dev_ready defined. */ - lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE; - - /* Set needed ECC fields */ - - lpc32xx_chip.ecc.mode = NAND_ECC_HW; - lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout; - lpc32xx_chip.ecc.size = 512; - lpc32xx_chip.ecc.bytes = 10; - lpc32xx_chip.ecc.strength = 4; - lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc; - lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw; - lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc; - lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw; - lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob; - lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob; - lpc32xx_chip.waitfunc = lpc32xx_waitfunc; - - lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */ - - /* BBT options: read from last two pages */ - lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK - | NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE - | NAND_BBT_WRITE; - - /* Initialize NAND interface */ - lpc32xx_nand_init(); - - /* identify chip */ - ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL); - if (ret) { - pr_err("nand_scan_ident returned %i", ret); - return; - } - - /* finish scanning the chip */ - ret = nand_scan_tail(mtd); - if (ret) { - pr_err("nand_scan_tail returned %i", ret); - return; - } - - /* chip is good, register it */ - ret = nand_register(0, mtd); - if (ret) - pr_err("nand_register returned %i", ret); -} - -#else /* defined(CONFIG_SPL_BUILD) */ - -void nand_init(void) -{ - /* enable NAND controller */ - lpc32xx_mlc_nand_init(); - /* initialize NAND controller */ - lpc32xx_nand_init(); -} - -void nand_deselect(void) -{ - /* nothing to do, but SPL requires this function */ -} - -static int read_single_page(uint8_t *dest, int page, - struct lpc32xx_oob *oob) -{ - int status, i, timeout, err, max_bitflips = 0; - - /* enter read mode */ - writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd); - /* send column (lsb then MSB) and page (lsb to MSB) */ - writel(0, &lpc32xx_nand_mlc_registers->addr); - writel(0, &lpc32xx_nand_mlc_registers->addr); - writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr); - writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr); - writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr); - /* start reading */ - writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd); - - /* large page auto decode read */ - for (i = 0; i < 4; i++) { - /* start auto decode (reads 528 NAND bytes) */ - writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); - /* wait for controller to return to ready state */ - for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { - status = readl(&lpc32xx_nand_mlc_registers->isr); - if (status & ISR_CONTROLLER_READY) - break; - udelay(1); - } - /* if controller stalled, return error */ - if (!(status & ISR_CONTROLLER_READY)) - return -1; - /* if decoder failure, return error */ - if (status & ISR_DECODER_FAILURE) - return -1; - /* keep count of maximum bitflips performed */ - if (status & ISR_DECODER_ERROR) { - err = ISR_DECODER_ERRORS(status); - if (err > max_bitflips) - max_bitflips = err; - } - /* copy first 512 bytes into buffer */ - memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512); - /* copy next 6 bytes bytes into OOB buffer */ - memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); - } - return max_bitflips; -} - -/* - * Load U-Boot signed image. - * This loads an image from NAND, skipping bad blocks. - * A block is declared bad if at least one of its readable pages has - * a bad block marker in its OOB at position 0. - * If all pages ion a block are unreadable, the block is considered - * bad (i.e., assumed not to be part of the image) and skipped. - * - * IMPORTANT NOTE: - * - * If the first block of the image is fully unreadable, it will be - * ignored and skipped as if it had been marked bad. If it was not - * actually marked bad at the time of writing the image, the resulting - * image loaded will lack a header and magic number. It could thus be - * considered as a raw, headerless, image and SPL might erroneously - * jump into it. - * - * In order to avoid this risk, LPC32XX-based boards which use this - * driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE. - */ - -#define BYTES_PER_PAGE 2048 -#define PAGES_PER_BLOCK 64 -#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK) -#define PAGES_PER_CHIP_MAX 524288 - -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) -{ - int bytes_left = size; - int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE); - int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK); - int block = 0; - int page = offs / BYTES_PER_PAGE; - /* perform reads block by block */ - while (blocks_left) { - /* compute first page number to read */ - void *block_page_dst = dst; - /* read at most one block, possibly less */ - int block_bytes_left = bytes_left; - if (block_bytes_left > BYTES_PER_BLOCK) - block_bytes_left = BYTES_PER_BLOCK; - /* keep track of good, failed, and "bad" pages */ - int block_pages_good = 0; - int block_pages_bad = 0; - int block_pages_err = 0; - /* we shall read a full block of pages, maybe less */ - int block_pages_left = pages_left; - if (block_pages_left > PAGES_PER_BLOCK) - block_pages_left = PAGES_PER_BLOCK; - int block_pages = block_pages_left; - int block_page = page; - /* while pages are left and the block is not known as bad */ - while ((block_pages > 0) && (block_pages_bad == 0)) { - /* we will read OOB, too, for bad block markers */ - struct lpc32xx_oob oob; - /* read page */ - int res = read_single_page(block_page_dst, block_page, - &oob); - /* count readable pages */ - if (res >= 0) { - /* this page is good */ - block_pages_good++; - /* this page is bad */ - if ((oob.free[0].free_oob_bytes[0] != 0xff) - | (oob.free[0].free_oob_bytes[1] != 0xff)) - block_pages_bad++; - } else - /* count errors */ - block_pages_err++; - /* we're done with this page */ - block_page++; - block_page_dst += BYTES_PER_PAGE; - if (block_pages) - block_pages--; - } - /* a fully unreadable block is considered bad */ - if (block_pages_good == 0) - block_pages_bad = block_pages_err; - /* errors are fatal only in good blocks */ - if ((block_pages_err > 0) && (block_pages_bad == 0)) - return -1; - /* we keep reads only of good blocks */ - if (block_pages_bad == 0) { - dst += block_bytes_left; - bytes_left -= block_bytes_left; - pages_left -= block_pages_left; - blocks_left--; - } - /* good or bad, we're done with this block */ - block++; - page += PAGES_PER_BLOCK; - } - - /* report success */ - return 0; -} - -#endif /* CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/lpc32xx_nand_slc.c b/drivers/mtd/nand/lpc32xx_nand_slc.c deleted file mode 100644 index 99f6e15f4e..0000000000 --- a/drivers/mtd/nand/lpc32xx_nand_slc.c +++ /dev/null @@ -1,597 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * LPC32xx SLC NAND flash controller driver - * - * (C) Copyright 2015 Vladimir Zapolskiy - * - * Hardware ECC support original source code - * Copyright (C) 2008 by NXP Semiconductors - * Author: Kevin Wells - * - * Copyright (c) 2015 Tyco Fire Protection Products. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_DMA_LPC32XX) && defined(CONFIG_SPL_BUILD) -#warning "DMA support in SPL image is not tested" -#endif - -struct lpc32xx_nand_slc_regs { - u32 data; - u32 addr; - u32 cmd; - u32 stop; - u32 ctrl; - u32 cfg; - u32 stat; - u32 int_stat; - u32 ien; - u32 isr; - u32 icr; - u32 tac; - u32 tc; - u32 ecc; - u32 dma_data; -}; - -/* CFG register */ -#define CFG_CE_LOW (1 << 5) -#define CFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ -#define CFG_ECC_EN (1 << 3) /* ECC enable bit */ -#define CFG_DMA_BURST (1 << 2) /* DMA burst bit */ -#define CFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ - -/* CTRL register */ -#define CTRL_SW_RESET (1 << 2) -#define CTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ -#define CTRL_DMA_START (1 << 0) /* Start DMA channel bit */ - -/* STAT register */ -#define STAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ -#define STAT_NAND_READY (1 << 0) - -/* INT_STAT register */ -#define INT_STAT_TC (1 << 1) -#define INT_STAT_RDY (1 << 0) - -/* TAC register bits, be aware of overflows */ -#define TAC_W_RDY(n) (max_t(uint32_t, (n), 0xF) << 28) -#define TAC_W_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 24) -#define TAC_W_HOLD(n) (max_t(uint32_t, (n), 0xF) << 20) -#define TAC_W_SETUP(n) (max_t(uint32_t, (n), 0xF) << 16) -#define TAC_R_RDY(n) (max_t(uint32_t, (n), 0xF) << 12) -#define TAC_R_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 8) -#define TAC_R_HOLD(n) (max_t(uint32_t, (n), 0xF) << 4) -#define TAC_R_SETUP(n) (max_t(uint32_t, (n), 0xF) << 0) - -/* NAND ECC Layout for small page NAND devices - * Note: For large page devices, the default layouts are used. */ -static struct nand_ecclayout lpc32xx_nand_oob_16 = { - .eccbytes = 6, - .eccpos = {10, 11, 12, 13, 14, 15}, - .oobfree = { - {.offset = 0, - . length = 4}, - {.offset = 6, - . length = 4} - } -}; - -#if defined(CONFIG_DMA_LPC32XX) -#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) - -/* - * DMA Descriptors - * For Large Block: 17 descriptors = ((16 Data and ECC Read) + 1 Spare Area) - * For Small Block: 5 descriptors = ((4 Data and ECC Read) + 1 Spare Area) - */ -static struct lpc32xx_dmac_ll dmalist[ECCSTEPS * 2 + 1]; -static u32 ecc_buffer[8]; /* MAX ECC size */ -static unsigned int dmachan = (unsigned int)-1; /* Invalid channel */ - -/* - * Helper macro for the DMA client (i.e. NAND SLC): - * - to write the next DMA linked list item address - * (see arch/include/asm/arch-lpc32xx/dma.h). - * - to assign the DMA data register to DMA source or destination address. - * - to assign the ECC register to DMA source or destination address. - */ -#define lpc32xx_dmac_next_lli(x) ((u32)x) -#define lpc32xx_dmac_set_dma_data() ((u32)&lpc32xx_nand_slc_regs->dma_data) -#define lpc32xx_dmac_set_ecc() ((u32)&lpc32xx_nand_slc_regs->ecc) -#endif - -static struct lpc32xx_nand_slc_regs __iomem *lpc32xx_nand_slc_regs - = (struct lpc32xx_nand_slc_regs __iomem *)SLC_NAND_BASE; - -static void lpc32xx_nand_init(void) -{ - uint32_t hclk = get_hclk_clk_rate(); - - /* Reset SLC NAND controller */ - writel(CTRL_SW_RESET, &lpc32xx_nand_slc_regs->ctrl); - - /* 8-bit bus, no DMA, no ECC, ordinary CE signal */ - writel(0, &lpc32xx_nand_slc_regs->cfg); - - /* Interrupts disabled and cleared */ - writel(0, &lpc32xx_nand_slc_regs->ien); - writel(INT_STAT_TC | INT_STAT_RDY, - &lpc32xx_nand_slc_regs->icr); - - /* Configure NAND flash timings */ - writel(TAC_W_RDY(CONFIG_LPC32XX_NAND_SLC_WDR_CLKS) | - TAC_W_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_WWIDTH) | - TAC_W_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_WHOLD) | - TAC_W_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_WSETUP) | - TAC_R_RDY(CONFIG_LPC32XX_NAND_SLC_RDR_CLKS) | - TAC_R_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_RWIDTH) | - TAC_R_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_RHOLD) | - TAC_R_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_RSETUP), - &lpc32xx_nand_slc_regs->tac); -} - -static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, - int cmd, unsigned int ctrl) -{ - debug("ctrl: 0x%08x, cmd: 0x%08x\n", ctrl, cmd); - - if (ctrl & NAND_NCE) - setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW); - else - clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->cmd); - else if (ctrl & NAND_ALE) - writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->addr); -} - -static int lpc32xx_nand_dev_ready(struct mtd_info *mtd) -{ - return readl(&lpc32xx_nand_slc_regs->stat) & STAT_NAND_READY; -} - -#if defined(CONFIG_DMA_LPC32XX) -/* - * Prepares DMA descriptors for NAND RD/WR operations - * If the size is < 256 Bytes then it is assumed to be - * an OOB transfer - */ -static void lpc32xx_nand_dma_configure(struct nand_chip *chip, - const u8 *buffer, int size, - int read) -{ - u32 i, dmasrc, ctrl, ecc_ctrl, oob_ctrl, dmadst; - struct lpc32xx_dmac_ll *dmalist_cur; - struct lpc32xx_dmac_ll *dmalist_cur_ecc; - - /* - * CTRL descriptor entry for reading ECC - * Copy Multiple times to sync DMA with Flash Controller - */ - ecc_ctrl = 0x5 | - DMAC_CHAN_SRC_BURST_1 | - DMAC_CHAN_DEST_BURST_1 | - DMAC_CHAN_SRC_WIDTH_32 | - DMAC_CHAN_DEST_WIDTH_32 | - DMAC_CHAN_DEST_AHB1; - - /* CTRL descriptor entry for reading/writing Data */ - ctrl = (CONFIG_SYS_NAND_ECCSIZE / 4) | - DMAC_CHAN_SRC_BURST_4 | - DMAC_CHAN_DEST_BURST_4 | - DMAC_CHAN_SRC_WIDTH_32 | - DMAC_CHAN_DEST_WIDTH_32 | - DMAC_CHAN_DEST_AHB1; - - /* CTRL descriptor entry for reading/writing Spare Area */ - oob_ctrl = (CONFIG_SYS_NAND_OOBSIZE / 4) | - DMAC_CHAN_SRC_BURST_4 | - DMAC_CHAN_DEST_BURST_4 | - DMAC_CHAN_SRC_WIDTH_32 | - DMAC_CHAN_DEST_WIDTH_32 | - DMAC_CHAN_DEST_AHB1; - - if (read) { - dmasrc = lpc32xx_dmac_set_dma_data(); - dmadst = (u32)buffer; - ctrl |= DMAC_CHAN_DEST_AUTOINC; - } else { - dmadst = lpc32xx_dmac_set_dma_data(); - dmasrc = (u32)buffer; - ctrl |= DMAC_CHAN_SRC_AUTOINC; - } - - /* - * Write Operation Sequence for Small Block NAND - * ---------------------------------------------------------- - * 1. X'fer 256 bytes of data from Memory to Flash. - * 2. Copy generated ECC data from Register to Spare Area - * 3. X'fer next 256 bytes of data from Memory to Flash. - * 4. Copy generated ECC data from Register to Spare Area. - * 5. X'fer 16 byets of Spare area from Memory to Flash. - * Read Operation Sequence for Small Block NAND - * ---------------------------------------------------------- - * 1. X'fer 256 bytes of data from Flash to Memory. - * 2. Copy generated ECC data from Register to ECC calc Buffer. - * 3. X'fer next 256 bytes of data from Flash to Memory. - * 4. Copy generated ECC data from Register to ECC calc Buffer. - * 5. X'fer 16 bytes of Spare area from Flash to Memory. - * Write Operation Sequence for Large Block NAND - * ---------------------------------------------------------- - * 1. Steps(1-4) of Write Operations repeate for four times - * which generates 16 DMA descriptors to X'fer 2048 bytes of - * data & 32 bytes of ECC data. - * 2. X'fer 64 bytes of Spare area from Memory to Flash. - * Read Operation Sequence for Large Block NAND - * ---------------------------------------------------------- - * 1. Steps(1-4) of Read Operations repeate for four times - * which generates 16 DMA descriptors to X'fer 2048 bytes of - * data & 32 bytes of ECC data. - * 2. X'fer 64 bytes of Spare area from Flash to Memory. - */ - - for (i = 0; i < size/CONFIG_SYS_NAND_ECCSIZE; i++) { - dmalist_cur = &dmalist[i * 2]; - dmalist_cur_ecc = &dmalist[(i * 2) + 1]; - - dmalist_cur->dma_src = (read ? (dmasrc) : (dmasrc + (i*256))); - dmalist_cur->dma_dest = (read ? (dmadst + (i*256)) : dmadst); - dmalist_cur->next_lli = lpc32xx_dmac_next_lli(dmalist_cur_ecc); - dmalist_cur->next_ctrl = ctrl; - - dmalist_cur_ecc->dma_src = lpc32xx_dmac_set_ecc(); - dmalist_cur_ecc->dma_dest = (u32)&ecc_buffer[i]; - dmalist_cur_ecc->next_lli = - lpc32xx_dmac_next_lli(&dmalist[(i * 2) + 2]); - dmalist_cur_ecc->next_ctrl = ecc_ctrl; - } - - if (i) { /* Data only transfer */ - dmalist_cur_ecc = &dmalist[(i * 2) - 1]; - dmalist_cur_ecc->next_lli = 0; - dmalist_cur_ecc->next_ctrl |= DMAC_CHAN_INT_TC_EN; - return; - } - - /* OOB only transfer */ - if (read) { - dmasrc = lpc32xx_dmac_set_dma_data(); - dmadst = (u32)buffer; - oob_ctrl |= DMAC_CHAN_DEST_AUTOINC; - } else { - dmadst = lpc32xx_dmac_set_dma_data(); - dmasrc = (u32)buffer; - oob_ctrl |= DMAC_CHAN_SRC_AUTOINC; - } - - /* Read/ Write Spare Area Data To/From Flash */ - dmalist_cur = &dmalist[i * 2]; - dmalist_cur->dma_src = dmasrc; - dmalist_cur->dma_dest = dmadst; - dmalist_cur->next_lli = 0; - dmalist_cur->next_ctrl = (oob_ctrl | DMAC_CHAN_INT_TC_EN); -} - -static void lpc32xx_nand_xfer(struct mtd_info *mtd, const u8 *buf, - int len, int read) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u32 config; - int ret; - - /* DMA Channel Configuration */ - config = (read ? DMAC_CHAN_FLOW_D_P2M : DMAC_CHAN_FLOW_D_M2P) | - (read ? DMAC_DEST_PERIP(0) : DMAC_DEST_PERIP(DMA_PERID_NAND1)) | - (read ? DMAC_SRC_PERIP(DMA_PERID_NAND1) : DMAC_SRC_PERIP(0)) | - DMAC_CHAN_ENABLE; - - /* Prepare DMA descriptors */ - lpc32xx_nand_dma_configure(chip, buf, len, read); - - /* Setup SLC controller and start transfer */ - if (read) - setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR); - else /* NAND_ECC_WRITE */ - clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR); - setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_BURST); - - /* Write length for new transfers */ - if (!((readl(&lpc32xx_nand_slc_regs->stat) & STAT_DMA_FIFO) | - readl(&lpc32xx_nand_slc_regs->tc))) { - int tmp = (len != mtd->oobsize) ? mtd->oobsize : 0; - writel(len + tmp, &lpc32xx_nand_slc_regs->tc); - } - - setbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START); - - /* Start DMA transfers */ - ret = lpc32xx_dma_start_xfer(dmachan, dmalist, config); - if (unlikely(ret < 0)) - BUG(); - - - /* Wait for NAND to be ready */ - while (!lpc32xx_nand_dev_ready(mtd)) - ; - - /* Wait till DMA transfer is DONE */ - if (lpc32xx_dma_wait_status(dmachan)) - pr_err("NAND DMA transfer error!\r\n"); - - /* Stop DMA & HW ECC */ - clrbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START); - clrbits_le32(&lpc32xx_nand_slc_regs->cfg, - CFG_DMA_DIR | CFG_DMA_BURST | CFG_ECC_EN | CFG_DMA_ECC); -} - -static u32 slc_ecc_copy_to_buffer(u8 *spare, const u32 *ecc, int count) -{ - int i; - for (i = 0; i < (count * CONFIG_SYS_NAND_ECCBYTES); - i += CONFIG_SYS_NAND_ECCBYTES) { - u32 ce = ecc[i / CONFIG_SYS_NAND_ECCBYTES]; - ce = ~(ce << 2) & 0xFFFFFF; - spare[i+2] = (u8)(ce & 0xFF); ce >>= 8; - spare[i+1] = (u8)(ce & 0xFF); ce >>= 8; - spare[i] = (u8)(ce & 0xFF); - } - return 0; -} - -static int lpc32xx_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - return slc_ecc_copy_to_buffer(ecc_code, ecc_buffer, ECCSTEPS); -} - -/* - * Enables and prepares SLC NAND controller - * for doing data transfers with H/W ECC enabled. - */ -static void lpc32xx_hwecc_enable(struct mtd_info *mtd, int mode) -{ - /* Clear ECC */ - writel(CTRL_ECC_CLEAR, &lpc32xx_nand_slc_regs->ctrl); - - /* Setup SLC controller for H/W ECC operations */ - setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_ECC_EN | CFG_DMA_ECC); -} - -/* - * lpc32xx_correct_data - [NAND Interface] Detect and correct bit error(s) - * mtd: MTD block structure - * dat: raw data read from the chip - * read_ecc: ECC from the chip - * calc_ecc: the ECC calculated from raw data - * - * Detect and correct a 1 bit error for 256 byte block - */ -int lpc32xx_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - unsigned int i; - int ret1, ret2 = 0; - u_char *r = read_ecc; - u_char *c = calc_ecc; - u16 data_offset = 0; - - for (i = 0 ; i < ECCSTEPS ; i++) { - r += CONFIG_SYS_NAND_ECCBYTES; - c += CONFIG_SYS_NAND_ECCBYTES; - data_offset += CONFIG_SYS_NAND_ECCSIZE; - - ret1 = nand_correct_data(mtd, dat + data_offset, r, c); - if (ret1 < 0) - return -EBADMSG; - else - ret2 += ret1; - } - - return ret2; -} -#endif - -#if defined(CONFIG_DMA_LPC32XX) -static void lpc32xx_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - lpc32xx_nand_xfer(mtd, buf, len, 1); -} -#else -static void lpc32xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - while (len-- > 0) - *buf++ = readl(&lpc32xx_nand_slc_regs->data); -} -#endif - -static uint8_t lpc32xx_read_byte(struct mtd_info *mtd) -{ - return readl(&lpc32xx_nand_slc_regs->data); -} - -#if defined(CONFIG_DMA_LPC32XX) -static void lpc32xx_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - lpc32xx_nand_xfer(mtd, buf, len, 0); -} -#else -static void lpc32xx_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - while (len-- > 0) - writel(*buf++, &lpc32xx_nand_slc_regs->data); -} -#endif - -static void lpc32xx_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - writel(byte, &lpc32xx_nand_slc_regs->data); -} - -#if defined(CONFIG_DMA_LPC32XX) -/* Reuse the logic from "nand_read_page_hwecc()" */ -static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i; - int stat; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - unsigned int max_bitflips = 0; - - /* - * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes - * and section 9.7, the NAND SLC & DMA allowed single DMA transaction - * of a page size using DMA controller scatter/gather mode through - * linked list; the ECC read is done without any software intervention. - */ - - lpc32xx_hwecc_enable(mtd, NAND_ECC_READ); - lpc32xx_dma_read_buf(mtd, p, chip->ecc.size * chip->ecc.steps); - lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]); - lpc32xx_dma_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - stat = chip->ecc.correct(mtd, p, &ecc_code[0], &ecc_calc[0]); - if (stat < 0) - mtd->ecc_stats.failed++; - else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - - return max_bitflips; -} - -/* Reuse the logic from "nand_write_page_hwecc()" */ -static int lpc32xx_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i; - uint8_t *ecc_calc = chip->buffers->ecccalc; - const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - /* - * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes - * and section 9.7, the NAND SLC & DMA allowed single DMA transaction - * of a page size using DMA controller scatter/gather mode through - * linked list; the ECC read is done without any software intervention. - */ - - lpc32xx_hwecc_enable(mtd, NAND_ECC_WRITE); - lpc32xx_dma_write_buf(mtd, p, chip->ecc.size * chip->ecc.steps); - lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]); - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - lpc32xx_dma_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} -#endif - -/* - * LPC32xx has only one SLC NAND controller, don't utilize - * CONFIG_SYS_NAND_SELF_INIT to be able to reuse this function - * both in SPL NAND and U-Boot images. - */ -int board_nand_init(struct nand_chip *lpc32xx_chip) -{ -#if defined(CONFIG_DMA_LPC32XX) - int ret; - - /* Acquire a channel for our use */ - ret = lpc32xx_dma_get_channel(); - if (unlikely(ret < 0)) { - pr_info("Unable to get free DMA channel for NAND transfers\n"); - return -1; - } - dmachan = (unsigned int)ret; -#endif - - lpc32xx_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; - lpc32xx_chip->dev_ready = lpc32xx_nand_dev_ready; - - /* - * The implementation of these functions is quite common, but - * they MUST be defined, because access to data register - * is strictly 32-bit aligned. - */ - lpc32xx_chip->read_byte = lpc32xx_read_byte; - lpc32xx_chip->write_byte = lpc32xx_write_byte; - -#if defined(CONFIG_DMA_LPC32XX) - /* Hardware ECC calculation is supported when DMA driver is selected */ - lpc32xx_chip->ecc.mode = NAND_ECC_HW; - - lpc32xx_chip->read_buf = lpc32xx_dma_read_buf; - lpc32xx_chip->write_buf = lpc32xx_dma_write_buf; - - lpc32xx_chip->ecc.calculate = lpc32xx_ecc_calculate; - lpc32xx_chip->ecc.correct = lpc32xx_correct_data; - lpc32xx_chip->ecc.hwctl = lpc32xx_hwecc_enable; - lpc32xx_chip->chip_delay = 2000; - - lpc32xx_chip->ecc.read_page = lpc32xx_read_page_hwecc; - lpc32xx_chip->ecc.write_page = lpc32xx_write_page_hwecc; - lpc32xx_chip->options |= NAND_NO_SUBPAGE_WRITE; -#else - /* - * Hardware ECC calculation is not supported by the driver, - * because it requires DMA support, see LPC32x0 User Manual, - * note after SLC_ECC register description (UM10326, p.198) - */ - lpc32xx_chip->ecc.mode = NAND_ECC_SOFT; - - /* - * The implementation of these functions is quite common, but - * they MUST be defined, because access to data register - * is strictly 32-bit aligned. - */ - lpc32xx_chip->read_buf = lpc32xx_read_buf; - lpc32xx_chip->write_buf = lpc32xx_write_buf; -#endif - - /* - * These values are predefined - * for both small and large page NAND flash devices. - */ - lpc32xx_chip->ecc.size = CONFIG_SYS_NAND_ECCSIZE; - lpc32xx_chip->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; - lpc32xx_chip->ecc.strength = 1; - - if (CONFIG_SYS_NAND_PAGE_SIZE != NAND_LARGE_BLOCK_PAGE_SIZE) - lpc32xx_chip->ecc.layout = &lpc32xx_nand_oob_16; - -#if defined(CONFIG_SYS_NAND_USE_FLASH_BBT) - lpc32xx_chip->bbt_options |= NAND_BBT_USE_FLASH; -#endif - - /* Initialize NAND interface */ - lpc32xx_nand_init(); - - return 0; -} diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c deleted file mode 100644 index cf97e0f74f..0000000000 --- a/drivers/mtd/nand/mxc_nand.c +++ /dev/null @@ -1,1307 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2004-2007 Freescale Semiconductor, Inc. - * Copyright 2008 Sascha Hauer, kernel@pengutronix.de - * Copyright 2009 Ilya Yanok, - */ - -#include -#include -#include -#include -#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \ - defined(CONFIG_MX51) || defined(CONFIG_MX53) -#include -#endif -#include "mxc_nand.h" - -#define DRIVER_NAME "mxc_nand" - -struct mxc_nand_host { - struct nand_chip *nand; - - struct mxc_nand_regs __iomem *regs; -#ifdef MXC_NFC_V3_2 - struct mxc_nand_ip_regs __iomem *ip_regs; -#endif - int spare_only; - int status_request; - int pagesize_2k; - int clk_act; - uint16_t col_addr; - unsigned int page_addr; -}; - -static struct mxc_nand_host mxc_host; -static struct mxc_nand_host *host = &mxc_host; - -/* Define delays in microsec for NAND device operations */ -#define TROP_US_DELAY 2000 -/* Macros to get byte and bit positions of ECC */ -#define COLPOS(x) ((x) >> 3) -#define BITPOS(x) ((x) & 0xf) - -/* Define single bit Error positions in Main & Spare area */ -#define MAIN_SINGLEBIT_ERROR 0x4 -#define SPARE_SINGLEBIT_ERROR 0x1 - -/* OOB placement block for use with hardware ecc generation */ -#if defined(MXC_NFC_V1) -#ifndef CONFIG_SYS_NAND_LARGEPAGE -static struct nand_ecclayout nand_hw_eccoob = { - .eccbytes = 5, - .eccpos = {6, 7, 8, 9, 10}, - .oobfree = { {0, 5}, {11, 5}, } -}; -#else -static struct nand_ecclayout nand_hw_eccoob2k = { - .eccbytes = 20, - .eccpos = { - 6, 7, 8, 9, 10, - 22, 23, 24, 25, 26, - 38, 39, 40, 41, 42, - 54, 55, 56, 57, 58, - }, - .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, -}; -#endif -#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) -#ifndef CONFIG_SYS_NAND_LARGEPAGE -static struct nand_ecclayout nand_hw_eccoob = { - .eccbytes = 9, - .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {2, 5} } -}; -#else -static struct nand_ecclayout nand_hw_eccoob2k = { - .eccbytes = 36, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63, - }, - .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} }, -}; -#endif -#endif - -static int is_16bit_nand(void) -{ -#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) - return 1; -#else - return 0; -#endif -} - -static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) -{ - uint32_t *d = dest; - - size >>= 2; - while (size--) - __raw_writel(__raw_readl(source++), d++); - return dest; -} - -/* - * This function polls the NANDFC to wait for the basic operation to - * complete by checking the INT bit. - */ -static void wait_op_done(struct mxc_nand_host *host, int max_retries, - uint16_t param) -{ - uint32_t tmp; - - while (max_retries-- > 0) { -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - tmp = readnfc(&host->regs->config2); - if (tmp & NFC_V1_V2_CONFIG2_INT) { - tmp &= ~NFC_V1_V2_CONFIG2_INT; - writenfc(tmp, &host->regs->config2); -#elif defined(MXC_NFC_V3_2) - tmp = readnfc(&host->ip_regs->ipc); - if (tmp & NFC_V3_IPC_INT) { - tmp &= ~NFC_V3_IPC_INT; - writenfc(tmp, &host->ip_regs->ipc); -#endif - break; - } - udelay(1); - } - if (max_retries < 0) { - pr_debug("%s(%d): INT not set\n", - __func__, param); - } -} - -/* - * This function issues the specified command to the NAND device and - * waits for completion. - */ -static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) -{ - pr_debug("send_cmd(host, 0x%x)\n", cmd); - - writenfc(cmd, &host->regs->flash_cmd); - writenfc(NFC_CMD, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, cmd); -} - -/* - * This function sends an address (or partial address) to the - * NAND device. The address is used to select the source/destination for - * a NAND command. - */ -static void send_addr(struct mxc_nand_host *host, uint16_t addr) -{ - pr_debug("send_addr(host, 0x%x)\n", addr); - - writenfc(addr, &host->regs->flash_addr); - writenfc(NFC_ADDR, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, addr); -} - -/* - * This function requests the NANDFC to initiate the transfer - * of data currently in the NANDFC RAM buffer to the NAND device. - */ -static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) -{ - if (spare_only) - pr_debug("send_prog_page (%d)\n", spare_only); - - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - int i; - /* - * The controller copies the 64 bytes of spare data from - * the first 16 bytes of each of the 4 64 byte spare buffers. - * Copy the contiguous data starting in spare_area[0] to - * the four spare area buffers. - */ - for (i = 1; i < 4; i++) { - void __iomem *src = &host->regs->spare_area[0][i * 16]; - void __iomem *dst = &host->regs->spare_area[i][0]; - - mxc_nand_memcpy32(dst, src, 16); - } - } - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(buf_id, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - tmp |= NFC_V3_CONFIG1_RBA(buf_id); - writenfc(tmp, &host->regs->config1); -#endif - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readnfc(&host->regs->config1); - if (spare_only) - config1 |= NFC_CONFIG1_SP_EN; - else - config1 &= ~NFC_CONFIG1_SP_EN; - writenfc(config1, &host->regs->config1); - } - - writenfc(NFC_INPUT, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only); -} - -/* - * Requests NANDFC to initiate the transfer of data from the - * NAND device into in the NANDFC ram buffer. - */ -static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) -{ - pr_debug("send_read_page (%d)\n", spare_only); - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(buf_id, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - tmp |= NFC_V3_CONFIG1_RBA(buf_id); - writenfc(tmp, &host->regs->config1); -#endif - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readnfc(&host->regs->config1); - if (spare_only) - config1 |= NFC_CONFIG1_SP_EN; - else - config1 &= ~NFC_CONFIG1_SP_EN; - writenfc(config1, &host->regs->config1); - } - - writenfc(NFC_OUTPUT, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only); - - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - int i; - - /* - * The controller copies the 64 bytes of spare data to - * the first 16 bytes of each of the 4 spare buffers. - * Make the data contiguous starting in spare_area[0]. - */ - for (i = 1; i < 4; i++) { - void __iomem *src = &host->regs->spare_area[i][0]; - void __iomem *dst = &host->regs->spare_area[0][i * 16]; - - mxc_nand_memcpy32(dst, src, 16); - } - } -} - -/* Request the NANDFC to perform a read of the NAND device ID. */ -static void send_read_id(struct mxc_nand_host *host) -{ - uint32_t tmp; - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* NANDFC buffer 0 is used for device ID output */ - writenfc(0x0, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - writenfc(tmp, &host->regs->config1); -#endif - - /* Read ID into main buffer */ - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_CONFIG1_SP_EN; - writenfc(tmp, &host->regs->config1); - - writenfc(NFC_ID, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0); -} - -/* - * This function requests the NANDFC to perform a read of the - * NAND device status and returns the current status. - */ -static uint16_t get_dev_status(struct mxc_nand_host *host) -{ -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - void __iomem *main_buf = host->regs->main_area[1]; - uint32_t store; -#endif - uint32_t ret, tmp; - /* Issue status request to NAND device */ - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* store the main area1 first word, later do recovery */ - store = readl(main_buf); - /* NANDFC buffer 1 is used for device status */ - writenfc(1, &host->regs->buf_addr); -#endif - - /* Read status into main buffer */ - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_CONFIG1_SP_EN; - writenfc(tmp, &host->regs->config1); - - writenfc(NFC_STATUS, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0); - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* - * Status is placed in first word of main buffer - * get status, then recovery area 1 data - */ - ret = readw(main_buf); - writel(store, main_buf); -#elif defined(MXC_NFC_V3_2) - ret = readnfc(&host->regs->config1) >> 16; -#endif - - return ret; -} - -/* This function is used by upper layer to checks if device is ready */ -static int mxc_nand_dev_ready(struct mtd_info *mtd) -{ - /* - * NFC handles R/B internally. Therefore, this function - * always returns status as ready. - */ - return 1; -} - -static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - uint16_t tmp = readnfc(&host->regs->config1); - - if (on) - tmp |= NFC_V1_V2_CONFIG1_ECC_EN; - else - tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; - writenfc(tmp, &host->regs->config1); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->ip_regs->config2); - - if (on) - tmp |= NFC_V3_CONFIG2_ECC_EN; - else - tmp &= ~NFC_V3_CONFIG2_ECC_EN; - writenfc(tmp, &host->ip_regs->config2); -#endif -} - -#ifdef CONFIG_MXC_NAND_HWECC -static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) -{ - /* - * If HW ECC is enabled, we turn it on during init. There is - * no need to enable again here. - */ -} - -#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) -static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *bufpoi = buf; - int i, toread; - - pr_debug("%s: Reading OOB area of page %u to oob %p\n", - __func__, page, buf); - - chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); - for (i = 0; i < chip->ecc.steps; i++) { - toread = min_t(int, length, chip->ecc.prepad); - if (toread) { - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - bufpoi += chip->ecc.bytes; - host->col_addr += chip->ecc.bytes; - length -= chip->ecc.bytes; - - toread = min_t(int, length, chip->ecc.postpad); - if (toread) { - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); - - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READOOB, - mtd->writesize + chip->ecc.prepad, page); - bufpoi = buf + chip->ecc.prepad; - length = mtd->oobsize - chip->ecc.prepad; - for (i = 0; i < chip->ecc.steps; i++) { - toread = min_t(int, length, chip->ecc.bytes); - chip->read_buf(mtd, bufpoi, toread); - bufpoi += eccpitch; - length -= eccpitch; - host->col_addr += chip->ecc.postpad + chip->ecc.prepad; - } - _mxc_nand_enable_hwecc(mtd, 1); - return 1; -} - -static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int oob_required, - int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *oob = chip->oob_poi; - int steps, size; - int n; - - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - - for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { - host->col_addr = n * eccsize; - chip->read_buf(mtd, buf, eccsize); - buf += eccsize; - - host->col_addr = mtd->writesize + n * eccpitch; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->read_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); - _mxc_nand_enable_hwecc(mtd, 1); - - return 0; -} - -static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int oob_required, - int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - int n, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - pr_debug("Reading page %u to buf %p oob %p\n", - page, buf, oob); - - /* first read the data area and the available portion of OOB */ - for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { - int stat; - - host->col_addr = n * eccsize; - - chip->read_buf(mtd, p, eccsize); - - host->col_addr = mtd->writesize + n * eccpitch; - - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - stat = chip->ecc.correct(mtd, p, oob, NULL); - - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - n = mtd->oobsize - (oob - chip->oob_poi); - if (n) - chip->read_buf(mtd, oob, n); - - /* Then switch ECC off and read the OOB area to get the ECC code */ - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); - eccsteps = chip->ecc.steps; - oob = chip->oob_poi + chip->ecc.prepad; - for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { - host->col_addr = mtd->writesize + - n * eccpitch + - chip->ecc.prepad; - chip->read_buf(mtd, oob, eccbytes); - oob += eccbytes + chip->ecc.postpad; - } - _mxc_nand_enable_hwecc(mtd, 1); - return 0; -} - -static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int length = mtd->oobsize; - int i, len, status, steps = chip->ecc.steps; - const uint8_t *bufpoi = chip->oob_poi; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - for (i = 0; i < steps; i++) { - len = min_t(int, length, eccpitch); - - chip->write_buf(mtd, bufpoi, len); - bufpoi += len; - length -= len; - host->col_addr += chip->ecc.prepad + chip->ecc.postpad; - } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *oob = chip->oob_poi; - int steps, size; - int n; - - for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { - host->col_addr = n * eccsize; - chip->write_buf(mtd, buf, eccsize); - buf += eccsize; - - host->col_addr = mtd->writesize + n * eccpitch; - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - host->col_addr += eccbytes; - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); - return 0; -} - -static int mxc_nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct mxc_nand_host *host = nand_get_controller_data(chip); - int i, n, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - for (i = n = 0; - eccsteps; - n++, eccsteps--, i += eccbytes, p += eccsize) { - host->col_addr = n * eccsize; - - chip->write_buf(mtd, p, eccsize); - - host->col_addr = mtd->writesize + n * eccpitch; - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->write_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); - return 0; -} - -static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint32_t ecc_status = readl(&host->regs->ecc_status_result); - int subpages = mtd->writesize / nand_chip->subpagesize; - int pg2blk_shift = nand_chip->phys_erase_shift - - nand_chip->page_shift; - - do { - if ((ecc_status & 0xf) > 4) { - static int last_bad = -1; - - if (last_bad != host->page_addr >> pg2blk_shift) { - last_bad = host->page_addr >> pg2blk_shift; - printk(KERN_DEBUG - "MXC_NAND: HWECC uncorrectable ECC error" - " in block %u page %u subpage %d\n", - last_bad, host->page_addr, - mtd->writesize / nand_chip->subpagesize - - subpages); - } - return -EBADMSG; - } - ecc_status >>= 4; - subpages--; - } while (subpages > 0); - - return 0; -} -#else -#define mxc_nand_read_page_syndrome NULL -#define mxc_nand_read_page_raw_syndrome NULL -#define mxc_nand_read_oob_syndrome NULL -#define mxc_nand_write_page_syndrome NULL -#define mxc_nand_write_page_raw_syndrome NULL -#define mxc_nand_write_oob_syndrome NULL - -static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - /* - * 1-Bit errors are automatically corrected in HW. No need for - * additional correction. 2-Bit errors cannot be corrected by - * HW ECC, so we need to return failure - */ - uint16_t ecc_status = readnfc(&host->regs->ecc_status_result); - - if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { - pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); - return -EBADMSG; - } - - return 0; -} -#endif - -static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - return 0; -} -#endif - -static u_char mxc_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint8_t ret = 0; - uint16_t col; - uint16_t __iomem *main_buf = - (uint16_t __iomem *)host->regs->main_area[0]; - uint16_t __iomem *spare_buf = - (uint16_t __iomem *)host->regs->spare_area[0]; - union { - uint16_t word; - uint8_t bytes[2]; - } nfc_word; - - /* Check for status request */ - if (host->status_request) - return get_dev_status(host) & 0xFF; - - /* Get column for 16-bit access */ - col = host->col_addr >> 1; - - /* If we are accessing the spare region */ - if (host->spare_only) - nfc_word.word = readw(&spare_buf[col]); - else - nfc_word.word = readw(&main_buf[col]); - - /* Pick upper/lower byte of word from RAM buffer */ - ret = nfc_word.bytes[host->col_addr & 0x1]; - - /* Update saved column address */ - if (nand_chip->options & NAND_BUSWIDTH_16) - host->col_addr += 2; - else - host->col_addr++; - - return ret; -} - -static uint16_t mxc_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - uint16_t col, ret; - uint16_t __iomem *p; - - pr_debug("mxc_nand_read_word(col = %d)\n", host->col_addr); - - col = host->col_addr; - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - if (col < mtd->writesize) { - p = (uint16_t __iomem *)(host->regs->main_area[0] + - (col >> 1)); - } else { - p = (uint16_t __iomem *)(host->regs->spare_area[0] + - ((col - mtd->writesize) >> 1)); - } - - if (col & 1) { - union { - uint16_t word; - uint8_t bytes[2]; - } nfc_word[3]; - - nfc_word[0].word = readw(p); - nfc_word[1].word = readw(p + 1); - - nfc_word[2].bytes[0] = nfc_word[0].bytes[1]; - nfc_word[2].bytes[1] = nfc_word[1].bytes[0]; - - ret = nfc_word[2].word; - } else { - ret = readw(p); - } - - /* Update saved column address */ - host->col_addr = col + 2; - - return ret; -} - -/* - * Write data of length len to buffer buf. The data to be - * written on NAND Flash is first copied to RAMbuffer. After the Data Input - * Operation by the NFC, the data is written to NAND Flash - */ -static void mxc_nand_write_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int n, col, i = 0; - - pr_debug("mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, - len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - pr_debug("%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); - - while (n > 0) { - void __iomem *p; - - if (col < mtd->writesize) { - p = host->regs->main_area[0] + (col & ~3); - } else { - p = host->regs->spare_area[0] - - mtd->writesize + (col & ~3); - } - - pr_debug("%s:%d: p = %p\n", __func__, - __LINE__, p); - - if (((col | (unsigned long)&buf[i]) & 3) || n < 4) { - union { - uint32_t word; - uint8_t bytes[4]; - } nfc_word; - - nfc_word.word = readl(p); - nfc_word.bytes[col & 3] = buf[i++]; - n--; - col++; - - writel(nfc_word.word, p); - } else { - int m = mtd->writesize - col; - - if (col >= mtd->writesize) - m += mtd->oobsize; - - m = min(n, m) & ~3; - - pr_debug("%s:%d: n = %d, m = %d, i = %d, col = %d\n", - __func__, __LINE__, n, m, i, col); - - mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; -} - -/* - * Read the data buffer from the NAND Flash. To read the data from NAND - * Flash first the data output cycle is initiated by the NFC, which copies - * the data to RAMbuffer. This data of length len is then copied to buffer buf. - */ -static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - int n, col, i = 0; - - pr_debug("mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, - len); - - col = host->col_addr; - - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); - - while (n > 0) { - void __iomem *p; - - if (col < mtd->writesize) { - p = host->regs->main_area[0] + (col & ~3); - } else { - p = host->regs->spare_area[0] - - mtd->writesize + (col & ~3); - } - - if (((col | (int)&buf[i]) & 3) || n < 4) { - union { - uint32_t word; - uint8_t bytes[4]; - } nfc_word; - - nfc_word.word = readl(p); - buf[i++] = nfc_word.bytes[col & 3]; - n--; - col++; - } else { - int m = mtd->writesize - col; - - if (col >= mtd->writesize) - m += mtd->oobsize; - - m = min(n, m) & ~3; - mxc_nand_memcpy32((uint32_t *)&buf[i], p, m); - - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; -} - -/* - * This function is used by upper layer for select and - * deselect of the NAND chip - */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - switch (chip) { - case -1: - /* TODO: Disable the NFC clock */ - if (host->clk_act) - host->clk_act = 0; - break; - case 0: - /* TODO: Enable the NFC clock */ - if (!host->clk_act) - host->clk_act = 1; - break; - - default: - break; - } -} - -/* - * Used by the upper layer to write command to NAND Flash for - * different operations to be carried out on NAND Flash - */ -void mxc_nand_command(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *nand_chip = mtd_to_nand(mtd); - struct mxc_nand_host *host = nand_get_controller_data(nand_chip); - - pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", - command, column, page_addr); - - /* Reset command state information */ - host->status_request = false; - - /* Command pre-processing step */ - switch (command) { - - case NAND_CMD_STATUS: - host->col_addr = 0; - host->status_request = true; - break; - - case NAND_CMD_READ0: - host->page_addr = page_addr; - host->col_addr = column; - host->spare_only = false; - break; - - case NAND_CMD_READOOB: - host->col_addr = column; - host->spare_only = true; - if (host->pagesize_2k) - command = NAND_CMD_READ0; /* only READ0 is valid */ - break; - - case NAND_CMD_SEQIN: - if (column >= mtd->writesize) { - /* - * before sending SEQIN command for partial write, - * we need read one page out. FSL NFC does not support - * partial write. It always sends out 512+ecc+512+ecc - * for large page nand flash. But for small page nand - * flash, it does support SPARE ONLY operation. - */ - if (host->pagesize_2k) { - /* call ourself to read a page */ - mxc_nand_command(mtd, NAND_CMD_READ0, 0, - page_addr); - } - - host->col_addr = column - mtd->writesize; - host->spare_only = true; - - /* Set program pointer to spare region */ - if (!host->pagesize_2k) - send_cmd(host, NAND_CMD_READOOB); - } else { - host->spare_only = false; - host->col_addr = column; - - /* Set program pointer to page start */ - if (!host->pagesize_2k) - send_cmd(host, NAND_CMD_READ0); - } - break; - - case NAND_CMD_PAGEPROG: - send_prog_page(host, 0, host->spare_only); - - if (host->pagesize_2k && is_mxc_nfc_1()) { - /* data in 4 areas */ - send_prog_page(host, 1, host->spare_only); - send_prog_page(host, 2, host->spare_only); - send_prog_page(host, 3, host->spare_only); - } - - break; - } - - /* Write out the command to the device. */ - send_cmd(host, command); - - /* Write out column address, if necessary */ - if (column != -1) { - /* - * MXC NANDFC can only perform full page+spare or - * spare-only read/write. When the upper layers perform - * a read/write buffer operation, we will use the saved - * column address to index into the full page. - */ - send_addr(host, 0); - if (host->pagesize_2k) - /* another col addr cycle for 2k page */ - send_addr(host, 0); - } - - /* Write out page address, if necessary */ - if (page_addr != -1) { - u32 page_mask = nand_chip->pagemask; - do { - send_addr(host, page_addr & 0xFF); - page_addr >>= 8; - page_mask >>= 8; - } while (page_mask); - } - - /* Command post-processing step */ - switch (command) { - - case NAND_CMD_RESET: - break; - - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - if (host->pagesize_2k) { - /* send read confirm command */ - send_cmd(host, NAND_CMD_READSTART); - /* read for each AREA */ - send_read_page(host, 0, host->spare_only); - if (is_mxc_nfc_1()) { - send_read_page(host, 1, host->spare_only); - send_read_page(host, 2, host->spare_only); - send_read_page(host, 3, host->spare_only); - } - } else { - send_read_page(host, 0, host->spare_only); - } - break; - - case NAND_CMD_READID: - host->col_addr = 0; - send_read_id(host); - break; - - case NAND_CMD_PAGEPROG: - break; - - case NAND_CMD_STATUS: - break; - - case NAND_CMD_ERASE2: - break; - } -} - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -#endif - -int board_nand_init(struct nand_chip *this) -{ - struct mtd_info *mtd; -#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) - uint32_t tmp; -#endif - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - this->bbt_options |= NAND_BBT_USE_FLASH; - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; -#endif - - /* structures must be linked */ - mtd = &this->mtd; - host->nand = this; - - /* 5 us command delay time */ - this->chip_delay = 5; - - nand_set_controller_data(this, host); - this->dev_ready = mxc_nand_dev_ready; - this->cmdfunc = mxc_nand_command; - this->select_chip = mxc_nand_select_chip; - this->read_byte = mxc_nand_read_byte; - this->read_word = mxc_nand_read_word; - this->write_buf = mxc_nand_write_buf; - this->read_buf = mxc_nand_read_buf; - - host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; -#ifdef MXC_NFC_V3_2 - host->ip_regs = - (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; -#endif - host->clk_act = 1; - -#ifdef CONFIG_MXC_NAND_HWECC - this->ecc.calculate = mxc_nand_calculate_ecc; - this->ecc.hwctl = mxc_nand_enable_hwecc; - this->ecc.correct = mxc_nand_correct_data; - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - this->ecc.mode = NAND_ECC_HW_SYNDROME; - this->ecc.read_page = mxc_nand_read_page_syndrome; - this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; - this->ecc.read_oob = mxc_nand_read_oob_syndrome; - this->ecc.write_page = mxc_nand_write_page_syndrome; - this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome; - this->ecc.write_oob = mxc_nand_write_oob_syndrome; - this->ecc.bytes = 9; - this->ecc.prepad = 7; - } else { - this->ecc.mode = NAND_ECC_HW; - } - - if (is_mxc_nfc_1()) - this->ecc.strength = 1; - else - this->ecc.strength = 4; - - host->pagesize_2k = 0; - - this->ecc.size = 512; - _mxc_nand_enable_hwecc(mtd, 1); -#else - this->ecc.layout = &nand_soft_eccoob; - this->ecc.mode = NAND_ECC_SOFT; - _mxc_nand_enable_hwecc(mtd, 0); -#endif - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* NAND bus width determines access functions used by upper layer */ - if (is_16bit_nand()) - this->options |= NAND_BUSWIDTH_16; - -#ifdef CONFIG_SYS_NAND_LARGEPAGE - host->pagesize_2k = 1; - this->ecc.layout = &nand_hw_eccoob2k; -#else - host->pagesize_2k = 0; - this->ecc.layout = &nand_hw_eccoob; -#endif - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) -#ifdef MXC_NFC_V2_1 - tmp = readnfc(&host->regs->config1); - tmp |= NFC_V2_CONFIG1_ONE_CYCLE; - tmp |= NFC_V2_CONFIG1_ECC_MODE_4; - writenfc(tmp, &host->regs->config1); - if (host->pagesize_2k) - writenfc(64/2, &host->regs->spare_area_size); - else - writenfc(16/2, &host->regs->spare_area_size); -#endif - - /* - * preset operation - * Unlock the internal RAM Buffer - */ - writenfc(0x2, &host->regs->config); - - /* Blocks to be unlocked */ - writenfc(0x0, &host->regs->unlockstart_blkaddr); - /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the - * unlockend_blkaddr, but the magic 0x4000 does not always work - * when writing more than some 32 megabytes (on 2k page nands) - * However 0xFFFF doesn't seem to have this kind - * of limitation (tried it back and forth several times). - * The linux kernel driver sets this to 0xFFFF for the v2 controller - * only, but probably this was not tested there for v1. - * The very same limitation seems to apply to this kernel driver. - * This might be NAND chip specific and the i.MX31 datasheet is - * extremely vague about the semantics of this register. - */ - writenfc(0xFFFF, &host->regs->unlockend_blkaddr); - - /* Unlock Block Command for given address range */ - writenfc(0x4, &host->regs->wrprot); -#elif defined(MXC_NFC_V3_2) - writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1); - writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc); - - /* Unlock the internal RAM Buffer */ - writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, - &host->ip_regs->wrprot); - - /* Blocks to be unlocked */ - for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++) - writenfc(0x0 | 0xFFFF << 16, - &host->ip_regs->wrprot_unlock_blkaddr[tmp]); - - writenfc(0, &host->ip_regs->ipc); - - tmp = readnfc(&host->ip_regs->config2); - tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK | - NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK); - tmp |= NFC_V3_CONFIG2_ONE_CYCLE; - - if (host->pagesize_2k) { - tmp |= NFC_V3_CONFIG2_SPAS(64/2); - tmp |= NFC_V3_CONFIG2_PS_2048; - } else { - tmp |= NFC_V3_CONFIG2_SPAS(16/2); - tmp |= NFC_V3_CONFIG2_PS_512; - } - - writenfc(tmp, &host->ip_regs->config2); - - tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | - NFC_V3_CONFIG3_NO_SDMA | - NFC_V3_CONFIG3_RBB_MODE | - NFC_V3_CONFIG3_SBB(6) | /* Reset default */ - NFC_V3_CONFIG3_ADD_OP(0); - - if (!(this->options & NAND_BUSWIDTH_16)) - tmp |= NFC_V3_CONFIG3_FW8; - - writenfc(tmp, &host->ip_regs->config3); - - writenfc(0, &host->ip_regs->delay_line); -#endif - - return 0; -} diff --git a/drivers/mtd/nand/mxc_nand.h b/drivers/mtd/nand/mxc_nand.h deleted file mode 100644 index 1c7f3a2e22..0000000000 --- a/drivers/mtd/nand/mxc_nand.h +++ /dev/null @@ -1,208 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (c) 2009 Magnus Lilja - */ - -#ifndef __MXC_NAND_H -#define __MXC_NAND_H - -/* - * Register map and bit definitions for the Freescale NAND Flash Controller - * present in various i.MX devices. - * - * MX31 and MX27 have version 1, which has: - * 4 512-byte main buffers and - * 4 16-byte spare buffers - * to support up to 2K byte pagesize nand. - * Reading or writing a 2K page requires 4 FDI/FDO cycles. - * - * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which - * have: - * 8 512-byte main buffers and - * 8 64-byte spare buffers - * to support up to 4K byte pagesize nand. - * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle. - * Also some of registers are moved and/or changed meaning as seen below. - */ -#if defined(CONFIG_MX27) || defined(CONFIG_MX31) -#define MXC_NFC_V1 -#define is_mxc_nfc_1() 1 -#define is_mxc_nfc_21() 0 -#define is_mxc_nfc_32() 0 -#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) -#define MXC_NFC_V2_1 -#define is_mxc_nfc_1() 0 -#define is_mxc_nfc_21() 1 -#define is_mxc_nfc_32() 0 -#elif defined(CONFIG_MX51) || defined(CONFIG_MX53) -#define MXC_NFC_V3 -#define MXC_NFC_V3_2 -#define is_mxc_nfc_1() 0 -#define is_mxc_nfc_21() 0 -#define is_mxc_nfc_32() 1 -#else -#error "MXC NFC implementation not supported" -#endif -#define is_mxc_nfc_3() is_mxc_nfc_32() - -#if defined(MXC_NFC_V1) -#define NAND_MXC_NR_BUFS 4 -#define NAND_MXC_SPARE_BUF_SIZE 16 -#define NAND_MXC_REG_OFFSET 0xe00 -#define NAND_MXC_2K_MULTI_CYCLE -#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) -#define NAND_MXC_NR_BUFS 8 -#define NAND_MXC_SPARE_BUF_SIZE 64 -#define NAND_MXC_REG_OFFSET 0x1e00 -#endif - -struct mxc_nand_regs { - u8 main_area[NAND_MXC_NR_BUFS][0x200]; - u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; - /* - * reserved size is offset of nfc registers - * minus total main and spare sizes - */ - u8 reserved1[NAND_MXC_REG_OFFSET - - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; -#if defined(MXC_NFC_V1) - u16 buf_size; - u16 reserved2; - u16 buf_addr; - u16 flash_addr; - u16 flash_cmd; - u16 config; - u16 ecc_status_result; - u16 rsltmain_area; - u16 rsltspare_area; - u16 wrprot; - u16 unlockstart_blkaddr; - u16 unlockend_blkaddr; - u16 nf_wrprst; - u16 config1; - u16 config2; -#elif defined(MXC_NFC_V2_1) - u16 reserved2[2]; - u16 buf_addr; - u16 flash_addr; - u16 flash_cmd; - u16 config; - u32 ecc_status_result; - u16 spare_area_size; - u16 wrprot; - u16 reserved3[2]; - u16 nf_wrprst; - u16 config1; - u16 config2; - u16 reserved4; - u16 unlockstart_blkaddr; - u16 unlockend_blkaddr; - u16 unlockstart_blkaddr1; - u16 unlockend_blkaddr1; - u16 unlockstart_blkaddr2; - u16 unlockend_blkaddr2; - u16 unlockstart_blkaddr3; - u16 unlockend_blkaddr3; -#elif defined(MXC_NFC_V3_2) - u32 flash_cmd; - u32 flash_addr[12]; - u32 config1; - u32 ecc_status_result; - u32 status_sum; - u32 launch; -#endif -}; - -#ifdef MXC_NFC_V3_2 -struct mxc_nand_ip_regs { - u32 wrprot; - u32 wrprot_unlock_blkaddr[8]; - u32 config2; - u32 config3; - u32 ipc; - u32 err_addr; - u32 delay_line; -}; -#endif - -/* Set FCMD to 1, rest to 0 for Command operation */ -#define NFC_CMD 0x1 - -/* Set FADD to 1, rest to 0 for Address operation */ -#define NFC_ADDR 0x2 - -/* Set FDI to 1, rest to 0 for Input operation */ -#define NFC_INPUT 0x4 - -/* Set FDO to 001, rest to 0 for Data Output operation */ -#define NFC_OUTPUT 0x8 - -/* Set FDO to 010, rest to 0 for Read ID operation */ -#define NFC_ID 0x10 - -/* Set FDO to 100, rest to 0 for Read Status operation */ -#define NFC_STATUS 0x20 - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) -#define NFC_CONFIG1_SP_EN (1 << 2) -#define NFC_CONFIG1_RST (1 << 6) -#define NFC_CONFIG1_CE (1 << 7) -#elif defined(MXC_NFC_V3_2) -#define NFC_CONFIG1_SP_EN (1 << 0) -#define NFC_CONFIG1_CE (1 << 1) -#define NFC_CONFIG1_RST (1 << 2) -#endif -#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) -#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) -#define NFC_V1_V2_CONFIG1_BIG (1 << 5) -#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) -#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) -#define NFC_V2_CONFIG1_FP_INT (1 << 11) -#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4) -#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4) - -#define NFC_V1_V2_CONFIG2_INT (1 << 15) -#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0) -#define NFC_V3_CONFIG2_PS_512 (0 << 0) -#define NFC_V3_CONFIG2_PS_2048 (1 << 0) -#define NFC_V3_CONFIG2_PS_4096 (2 << 0) -#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) -#define NFC_V3_CONFIG2_ECC_EN (1 << 3) -#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) -#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5) -#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) -#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7) -#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) -#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9) -#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9) -#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12) -#define NFC_V3_CONFIG2_INT_MSK (1 << 15) -#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16) -#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) -#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24) -#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) - -#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) -#define NFC_V3_CONFIG3_FW8 (1 << 3) -#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) -#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12) -#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) -#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) - -#define NFC_V3_WRPROT_UNLOCK (1 << 2) -#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) - -#define NFC_V3_IPC_CREQ (1 << 0) -#define NFC_V3_IPC_INT (1 << 31) - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) -#define operation config2 -#define readnfc readw -#define writenfc writew -#elif defined(MXC_NFC_V3_2) -#define operation launch -#define readnfc readl -#define writenfc writel -#endif - -#endif /* __MXC_NAND_H */ diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c deleted file mode 100644 index 6c03db8428..0000000000 --- a/drivers/mtd/nand/mxc_nand_spl.c +++ /dev/null @@ -1,350 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2009 - * Magnus Lilja - * - * (C) Copyright 2008 - * Maxim Artamonov, - * - * (C) Copyright 2006-2008 - * Stefan Roese, DENX Software Engineering, sr at denx.de. - */ - -#include -#include -#include -#include -#include "mxc_nand.h" - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) -static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR; -#elif defined(MXC_NFC_V3_2) -static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI; -static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR; -#endif - -static void nfc_wait_ready(void) -{ - uint32_t tmp; - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT)) - ; - - /* Reset interrupt flag */ - tmp = readnfc(&nfc->config2); - tmp &= ~NFC_V1_V2_CONFIG2_INT; - writenfc(tmp, &nfc->config2); -#elif defined(MXC_NFC_V3_2) - while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT)) - ; - - /* Reset interrupt flag */ - tmp = readnfc(&nfc_ip->ipc); - tmp &= ~NFC_V3_IPC_INT; - writenfc(tmp, &nfc_ip->ipc); -#endif -} - -static void nfc_nand_init(void) -{ -#if defined(MXC_NFC_V3_2) - int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; - int tmp; - - tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK | - NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) | - NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) | - NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN | - NFC_V3_CONFIG2_ONE_CYCLE; - if (CONFIG_SYS_NAND_PAGE_SIZE == 4096) - tmp |= NFC_V3_CONFIG2_PS_4096; - else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048) - tmp |= NFC_V3_CONFIG2_PS_2048; - else if (CONFIG_SYS_NAND_PAGE_SIZE == 512) - tmp |= NFC_V3_CONFIG2_PS_512; - /* - * if spare size is larger that 16 bytes per 512 byte hunk - * then use 8 symbol correction instead of 4 - */ - if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) - tmp |= NFC_V3_CONFIG2_ECC_MODE_8; - else - tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8; - writenfc(tmp, &nfc_ip->config2); - - tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | - NFC_V3_CONFIG3_NO_SDMA | - NFC_V3_CONFIG3_RBB_MODE | - NFC_V3_CONFIG3_SBB(6) | /* Reset default */ - NFC_V3_CONFIG3_ADD_OP(0); -#ifndef CONFIG_SYS_NAND_BUSWIDTH_16 - tmp |= NFC_V3_CONFIG3_FW8; -#endif - writenfc(tmp, &nfc_ip->config3); - - writenfc(0, &nfc_ip->delay_line); -#elif defined(MXC_NFC_V2_1) - int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; - int config1; - - writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size); - - /* unlocking RAM Buff */ - writenfc(0x2, &nfc->config); - - /* hardware ECC checking and correct */ - config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN | - NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE | - NFC_V2_CONFIG1_FP_INT; - /* - * if spare size is larger that 16 bytes per 512 byte hunk - * then use 8 symbol correction instead of 4 - */ - if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) - config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4; - else - config1 |= NFC_V2_CONFIG1_ECC_MODE_4; - writenfc(config1, &nfc->config1); -#elif defined(MXC_NFC_V1) - /* unlocking RAM Buff */ - writenfc(0x2, &nfc->config); - - /* hardware ECC checking and correct */ - writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK, - &nfc->config1); -#endif -} - -static void nfc_nand_command(unsigned short command) -{ - writenfc(command, &nfc->flash_cmd); - writenfc(NFC_CMD, &nfc->operation); - nfc_wait_ready(); -} - -static void nfc_nand_address(unsigned short address) -{ - writenfc(address, &nfc->flash_addr); - writenfc(NFC_ADDR, &nfc->operation); - nfc_wait_ready(); -} - -static void nfc_nand_page_address(unsigned int page_address) -{ - unsigned int page_count; - - nfc_nand_address(0x00); - - /* code only for large page flash */ - if (CONFIG_SYS_NAND_PAGE_SIZE > 512) - nfc_nand_address(0x00); - - page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE; - - if (page_address <= page_count) { - page_count--; /* transform 0x01000000 to 0x00ffffff */ - do { - nfc_nand_address(page_address & 0xff); - page_address = page_address >> 8; - page_count = page_count >> 8; - } while (page_count); - } - - nfc_nand_address(0x00); -} - -static void nfc_nand_data_output(void) -{ -#ifdef NAND_MXC_2K_MULTI_CYCLE - int i; -#endif - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(0, &nfc->buf_addr); -#elif defined(MXC_NFC_V3_2) - int config1 = readnfc(&nfc->config1); - config1 &= ~NFC_V3_CONFIG1_RBA_MASK; - writenfc(config1, &nfc->config1); -#endif - writenfc(NFC_OUTPUT, &nfc->operation); - nfc_wait_ready(); -#ifdef NAND_MXC_2K_MULTI_CYCLE - /* - * This NAND controller requires multiple input commands - * for pages larger than 512 bytes. - */ - for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) { - writenfc(i, &nfc->buf_addr); - writenfc(NFC_OUTPUT, &nfc->operation); - nfc_wait_ready(); - } -#endif -} - -static int nfc_nand_check_ecc(void) -{ -#if defined(MXC_NFC_V1) - u16 ecc_status = readw(&nfc->ecc_status_result); - return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2; -#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) - u32 ecc_status = readl(&nfc->ecc_status_result); - int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; - int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4; - int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512; - - do { - if ((ecc_status & 0xf) > err_limit) - return 1; - ecc_status >>= 4; - } while (--subpages); - - return 0; -#endif -} - -static void nfc_nand_read_page(unsigned int page_address) -{ - /* read in first 0 buffer */ -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(0, &nfc->buf_addr); -#elif defined(MXC_NFC_V3_2) - int config1 = readnfc(&nfc->config1); - config1 &= ~NFC_V3_CONFIG1_RBA_MASK; - writenfc(config1, &nfc->config1); -#endif - nfc_nand_command(NAND_CMD_READ0); - nfc_nand_page_address(page_address); - - if (CONFIG_SYS_NAND_PAGE_SIZE > 512) - nfc_nand_command(NAND_CMD_READSTART); - - nfc_nand_data_output(); /* fill the main buffer 0 */ -} - -static int nfc_read_page(unsigned int page_address, unsigned char *buf) -{ - int i; - u32 *src; - u32 *dst; - - nfc_nand_read_page(page_address); - - if (nfc_nand_check_ecc()) - return -EBADMSG; - - src = (u32 *)&nfc->main_area[0][0]; - dst = (u32 *)buf; - - /* main copy loop from NAND-buffer to SDRAM memory */ - for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) { - writel(readl(src), dst); - src++; - dst++; - } - - return 0; -} - -static int is_badblock(int pagenumber) -{ - int page = pagenumber; - u32 badblock; - u32 *src; - - /* Check the first two pages for bad block markers */ - for (page = pagenumber; page < pagenumber + 2; page++) { - nfc_nand_read_page(page); - - src = (u32 *)&nfc->spare_area[0][0]; - - /* - * IMPORTANT NOTE: The nand flash controller uses a non- - * standard layout for large page devices. This can - * affect the position of the bad block marker. - */ - /* Get the bad block marker */ - badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]); - badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4); - badblock &= 0xff; - - /* bad block marker verify */ - if (badblock != 0xff) - return 1; /* potential bad block */ - } - - return 0; -} - -int nand_spl_load_image(uint32_t from, unsigned int size, void *buf) -{ - int i; - unsigned int page; - unsigned int maxpages = CONFIG_SYS_NAND_SIZE / - CONFIG_SYS_NAND_PAGE_SIZE; - - nfc_nand_init(); - - /* Convert to page number */ - page = from / CONFIG_SYS_NAND_PAGE_SIZE; - i = 0; - - size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE); - while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) { - if (nfc_read_page(page, buf) < 0) - return -1; - - page++; - i++; - buf = buf + CONFIG_SYS_NAND_PAGE_SIZE; - - /* - * Check if we have crossed a block boundary, and if so - * check for bad block. - */ - if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { - /* - * Yes, new block. See if this block is good. If not, - * loop until we find a good block. - */ - while (is_badblock(page)) { - page = page + CONFIG_SYS_NAND_PAGE_COUNT; - /* Check i we've reached the end of flash. */ - if (page >= maxpages) - return -1; - } - } - } - - return 0; -} - -#ifndef CONFIG_SPL_FRAMEWORK -/* - * The main entry for NAND booting. It's necessary that SDRAM is already - * configured and available since this code loads the main U-Boot image - * from NAND into SDRAM and starts it from there. - */ -void nand_boot(void) -{ - __attribute__((noreturn)) void (*uboot)(void); - - /* - * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must - * be aligned to full pages - */ - if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, - CONFIG_SYS_NAND_U_BOOT_SIZE, - (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { - /* Copy from NAND successful, start U-Boot */ - uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; - uboot(); - } else { - /* Unrecoverable error when copying from NAND */ - hang(); - } -} -#endif - -void nand_init(void) {} -void nand_deselect(void) {} diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c deleted file mode 100644 index e3341812a2..0000000000 --- a/drivers/mtd/nand/mxs_nand.c +++ /dev/null @@ -1,1302 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Freescale i.MX28 NAND flash driver - * - * Copyright (C) 2011 Marek Vasut - * on behalf of DENX Software Engineering GmbH - * - * Based on code from LTIB: - * Freescale GPMI NFC NAND Flash Driver - * - * Copyright (C) 2010 Freescale Semiconductor, Inc. - * Copyright (C) 2008 Embedded Alley Solutions, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mxs_nand.h" - -#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 - -#if (defined(CONFIG_MX6) || defined(CONFIG_MX7)) -#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 -#else -#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 -#endif -#define MXS_NAND_METADATA_SIZE 10 -#define MXS_NAND_BITS_PER_ECC_LEVEL 13 - -#if !defined(CONFIG_SYS_CACHELINE_SIZE) || CONFIG_SYS_CACHELINE_SIZE < 32 -#define MXS_NAND_COMMAND_BUFFER_SIZE 32 -#else -#define MXS_NAND_COMMAND_BUFFER_SIZE CONFIG_SYS_CACHELINE_SIZE -#endif - -#define MXS_NAND_BCH_TIMEOUT 10000 - -struct nand_ecclayout fake_ecc_layout; - -/* - * Cache management functions - */ -#ifndef CONFIG_SYS_DCACHE_OFF -static void mxs_nand_flush_data_buf(struct mxs_nand_info *info) -{ - uint32_t addr = (uint32_t)info->data_buf; - - flush_dcache_range(addr, addr + info->data_buf_size); -} - -static void mxs_nand_inval_data_buf(struct mxs_nand_info *info) -{ - uint32_t addr = (uint32_t)info->data_buf; - - invalidate_dcache_range(addr, addr + info->data_buf_size); -} - -static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) -{ - uint32_t addr = (uint32_t)info->cmd_buf; - - flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE); -} -#else -static inline void mxs_nand_flush_data_buf(struct mxs_nand_info *info) {} -static inline void mxs_nand_inval_data_buf(struct mxs_nand_info *info) {} -static inline void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) {} -#endif - -static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) -{ - struct mxs_dma_desc *desc; - - if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) { - printf("MXS NAND: Too many DMA descriptors requested\n"); - return NULL; - } - - desc = info->desc[info->desc_index]; - info->desc_index++; - - return desc; -} - -static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) -{ - int i; - struct mxs_dma_desc *desc; - - for (i = 0; i < info->desc_index; i++) { - desc = info->desc[i]; - memset(desc, 0, sizeof(struct mxs_dma_desc)); - desc->address = (dma_addr_t)desc; - } - - info->desc_index = 0; -} - -static uint32_t mxs_nand_aux_status_offset(void) -{ - return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; -} - -static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, - uint32_t page_data_size) -{ - uint32_t chunk_data_size_in_bits = geo->ecc_chunk_size * 8; - uint32_t chunk_ecc_size_in_bits = geo->ecc_strength * geo->gf_len; - uint32_t chunk_total_size_in_bits; - uint32_t block_mark_chunk_number; - uint32_t block_mark_chunk_bit_offset; - uint32_t block_mark_bit_offset; - - chunk_total_size_in_bits = - chunk_data_size_in_bits + chunk_ecc_size_in_bits; - - /* Compute the bit offset of the block mark within the physical page. */ - block_mark_bit_offset = page_data_size * 8; - - /* Subtract the metadata bits. */ - block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; - - /* - * Compute the chunk number (starting at zero) in which the block mark - * appears. - */ - block_mark_chunk_number = - block_mark_bit_offset / chunk_total_size_in_bits; - - /* - * Compute the bit offset of the block mark within its chunk, and - * validate it. - */ - block_mark_chunk_bit_offset = block_mark_bit_offset - - (block_mark_chunk_number * chunk_total_size_in_bits); - - if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) - return -EINVAL; - - /* - * Now that we know the chunk number in which the block mark appears, - * we can subtract all the ECC bits that appear before it. - */ - block_mark_bit_offset -= - block_mark_chunk_number * chunk_ecc_size_in_bits; - - geo->block_mark_byte_offset = block_mark_bit_offset >> 3; - geo->block_mark_bit_offset = block_mark_bit_offset & 0x7; - - return 0; -} - -static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, - struct mtd_info *mtd, - unsigned int ecc_strength, - unsigned int ecc_step) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - - switch (ecc_step) { - case SZ_512: - geo->gf_len = 13; - break; - case SZ_1K: - geo->gf_len = 14; - break; - default: - return -EINVAL; - } - - geo->ecc_chunk_size = ecc_step; - geo->ecc_strength = round_up(ecc_strength, 2); - - /* Keep the C >= O */ - if (geo->ecc_chunk_size < mtd->oobsize) - return -EINVAL; - - if (geo->ecc_strength > nand_info->max_ecc_strength_supported) - return -EINVAL; - - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; - - return 0; -} - -static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, - struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - - /* The default for the length of Galois Field. */ - geo->gf_len = 13; - - /* The default for chunk size. */ - geo->ecc_chunk_size = 512; - - if (geo->ecc_chunk_size < mtd->oobsize) { - geo->gf_len = 14; - geo->ecc_chunk_size *= 2; - } - - if (mtd->oobsize > geo->ecc_chunk_size) { - printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", - geo->ecc_chunk_size); - return -EINVAL; - } - - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; - - /* - * Determine the ECC layout with the formula: - * ECC bits per chunk = (total page spare data bits) / - * (bits per ECC level) / (chunks per page) - * where: - * total page spare data bits = - * (page oob size - meta data size) * (bits per byte) - */ - geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8) - / (geo->gf_len * geo->ecc_chunk_count); - - geo->ecc_strength = min(round_down(geo->ecc_strength, 2), - nand_info->max_ecc_strength_supported); - - return 0; -} - -/* - * Wait for BCH complete IRQ and clear the IRQ - */ -static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info) -{ - int timeout = MXS_NAND_BCH_TIMEOUT; - int ret; - - ret = mxs_wait_mask_set(&nand_info->bch_regs->hw_bch_ctrl_reg, - BCH_CTRL_COMPLETE_IRQ, timeout); - - writel(BCH_CTRL_COMPLETE_IRQ, &nand_info->bch_regs->hw_bch_ctrl_clr); - - return ret; -} - -/* - * This is the function that we install in the cmd_ctrl function pointer of the - * owning struct nand_chip. The only functions in the reference implementation - * that use these functions pointers are cmdfunc and select_chip. - * - * In this driver, we implement our own select_chip, so this function will only - * be called by the reference implementation's cmdfunc. For this reason, we can - * ignore the chip enable bit and concentrate only on sending bytes to the NAND - * Flash. - */ -static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct mxs_dma_desc *d; - uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; - int ret; - - /* - * If this condition is true, something is _VERY_ wrong in MTD - * subsystem! - */ - if (nand_info->cmd_queue_len == MXS_NAND_COMMAND_BUFFER_SIZE) { - printf("MXS NAND: Command queue too long\n"); - return; - } - - /* - * Every operation begins with a command byte and a series of zero or - * more address bytes. These are distinguished by either the Address - * Latch Enable (ALE) or Command Latch Enable (CLE) signals being - * asserted. When MTD is ready to execute the command, it will - * deasert both latch enables. - * - * Rather than run a separate DMA operation for every single byte, we - * queue them up and run a single DMA operation for the entire series - * of command and data bytes. - */ - if (ctrl & (NAND_ALE | NAND_CLE)) { - if (data != NAND_CMD_NONE) - nand_info->cmd_buf[nand_info->cmd_queue_len++] = data; - return; - } - - /* - * If control arrives here, MTD has deasserted both the ALE and CLE, - * which means it's ready to run an operation. Check if we have any - * bytes to send. - */ - if (nand_info->cmd_queue_len == 0) - return; - - /* Compile the DMA descriptor -- a descriptor that sends command. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM | - MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET); - - d->cmd.address = (dma_addr_t)nand_info->cmd_buf; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WRITE | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_CLE | - GPMI_CTRL0_ADDRESS_INCREMENT | - nand_info->cmd_queue_len; - - mxs_dma_desc_append(channel, d); - - /* Flush caches */ - mxs_nand_flush_cmd_buf(nand_info); - - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); - if (ret) - printf("MXS NAND: Error sending command\n"); - - mxs_nand_return_dma_descs(nand_info); - - /* Reset the command queue. */ - nand_info->cmd_queue_len = 0; -} - -/* - * Test if the NAND flash is ready. - */ -static int mxs_nand_device_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - uint32_t tmp; - - tmp = readl(&nand_info->gpmi_regs->hw_gpmi_stat); - tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip); - - return tmp & 1; -} - -/* - * Select the NAND chip. - */ -static void mxs_nand_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - - nand_info->cur_chip = chip; -} - -/* - * Handle block mark swapping. - * - * Note that, when this function is called, it doesn't know whether it's - * swapping the block mark, or swapping it *back* -- but it doesn't matter - * because the the operation is the same. - */ -static void mxs_nand_swap_block_mark(struct bch_geometry *geo, - uint8_t *data_buf, uint8_t *oob_buf) -{ - uint32_t bit_offset = geo->block_mark_bit_offset; - uint32_t buf_offset = geo->block_mark_byte_offset; - - uint32_t src; - uint32_t dst; - - /* - * Get the byte from the data area that overlays the block mark. Since - * the ECC engine applies its own view to the bits in the page, the - * physical block mark won't (in general) appear on a byte boundary in - * the data. - */ - src = data_buf[buf_offset] >> bit_offset; - src |= data_buf[buf_offset + 1] << (8 - bit_offset); - - dst = oob_buf[0]; - - oob_buf[0] = src; - - data_buf[buf_offset] &= ~(0xff << bit_offset); - data_buf[buf_offset + 1] &= 0xff << bit_offset; - - data_buf[buf_offset] |= dst << bit_offset; - data_buf[buf_offset + 1] |= dst >> (8 - bit_offset); -} - -/* - * Read data from NAND. - */ -static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct mxs_dma_desc *d; - uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; - int ret; - - if (length > NAND_MAX_PAGESIZE) { - printf("MXS NAND: DMA buffer too big\n"); - return; - } - - if (!buf) { - printf("MXS NAND: DMA buffer is NULL\n"); - return; - } - - /* Compile the DMA descriptor - a descriptor that reads data. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (length << MXS_DMA_DESC_BYTES_OFFSET); - - d->cmd.address = (dma_addr_t)nand_info->data_buf; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_READ | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA | - length; - - mxs_dma_desc_append(channel, d); - - /* - * A DMA descriptor that waits for the command to end and the chip to - * become ready. - * - * I think we actually should *not* be waiting for the chip to become - * ready because, after all, we don't care. I think the original code - * did that and no one has re-thought it yet. - */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | - MXS_DMA_DESC_WAIT4END | (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - - d->cmd.address = 0; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA; - - mxs_dma_desc_append(channel, d); - - /* Invalidate caches */ - mxs_nand_inval_data_buf(nand_info); - - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); - if (ret) { - printf("MXS NAND: DMA read error\n"); - goto rtn; - } - - /* Invalidate caches */ - mxs_nand_inval_data_buf(nand_info); - - memcpy(buf, nand_info->data_buf, length); - -rtn: - mxs_nand_return_dma_descs(nand_info); -} - -/* - * Write data to NAND. - */ -static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int length) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct mxs_dma_desc *d; - uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; - int ret; - - if (length > NAND_MAX_PAGESIZE) { - printf("MXS NAND: DMA buffer too big\n"); - return; - } - - if (!buf) { - printf("MXS NAND: DMA buffer is NULL\n"); - return; - } - - memcpy(nand_info->data_buf, buf, length); - - /* Compile the DMA descriptor - a descriptor that writes data. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (length << MXS_DMA_DESC_BYTES_OFFSET); - - d->cmd.address = (dma_addr_t)nand_info->data_buf; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WRITE | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA | - length; - - mxs_dma_desc_append(channel, d); - - /* Flush caches */ - mxs_nand_flush_data_buf(nand_info); - - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); - if (ret) - printf("MXS NAND: DMA write error\n"); - - mxs_nand_return_dma_descs(nand_info); -} - -/* - * Read a single byte from NAND. - */ -static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) -{ - uint8_t buf; - mxs_nand_read_buf(mtd, &buf, 1); - return buf; -} - -/* - * Read a page from NAND. - */ -static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int oob_required, - int page) -{ - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct bch_geometry *geo = &nand_info->bch_geometry; - struct mxs_dma_desc *d; - uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; - uint32_t corrected = 0, failed = 0; - uint8_t *status; - int i, ret; - - /* Compile the DMA descriptor - wait for ready. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | - MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - - d->cmd.address = 0; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA; - - mxs_dma_desc_append(channel, d); - - /* Compile the DMA descriptor - enable the BCH block and read. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | - MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - - d->cmd.address = 0; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_READ | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA | - (mtd->writesize + mtd->oobsize); - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = - GPMI_ECCCTRL_ENABLE_ECC | - GPMI_ECCCTRL_ECC_CMD_DECODE | - GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; - d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; - - mxs_dma_desc_append(channel, d); - - /* Compile the DMA descriptor - disable the BCH block. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | - MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | - (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - - d->cmd.address = 0; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA | - (mtd->writesize + mtd->oobsize); - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = 0; - - mxs_dma_desc_append(channel, d); - - /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_DEC_SEM; - - d->cmd.address = 0; - - mxs_dma_desc_append(channel, d); - - /* Invalidate caches */ - mxs_nand_inval_data_buf(nand_info); - - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); - if (ret) { - printf("MXS NAND: DMA read error\n"); - goto rtn; - } - - ret = mxs_nand_wait_for_bch_complete(nand_info); - if (ret) { - printf("MXS NAND: BCH read timeout\n"); - goto rtn; - } - - /* Invalidate caches */ - mxs_nand_inval_data_buf(nand_info); - - /* Read DMA completed, now do the mark swapping. */ - mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); - - /* Loop over status bytes, accumulating ECC status. */ - status = nand_info->oob_buf + mxs_nand_aux_status_offset(); - for (i = 0; i < geo->ecc_chunk_count; i++) { - if (status[i] == 0x00) - continue; - - if (status[i] == 0xff) - continue; - - if (status[i] == 0xfe) { - failed++; - continue; - } - - corrected += status[i]; - } - - /* Propagate ECC status to the owning MTD. */ - mtd->ecc_stats.failed += failed; - mtd->ecc_stats.corrected += corrected; - - /* - * It's time to deliver the OOB bytes. See mxs_nand_ecc_read_oob() for - * details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the block - * mark to the caller's buffer. Note that, if block mark swapping was - * necessary, it has already been done, so we can rely on the first - * byte of the auxiliary buffer to contain the block mark. - */ - memset(nand->oob_poi, 0xff, mtd->oobsize); - - nand->oob_poi[0] = nand_info->oob_buf[0]; - - memcpy(buf, nand_info->data_buf, mtd->writesize); - -rtn: - mxs_nand_return_dma_descs(nand_info); - - return ret; -} - -/* - * Write a page to NAND. - */ -static int mxs_nand_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *nand, const uint8_t *buf, - int oob_required, int page) -{ - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct bch_geometry *geo = &nand_info->bch_geometry; - struct mxs_dma_desc *d; - uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; - int ret; - - memcpy(nand_info->data_buf, buf, mtd->writesize); - memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize); - - /* Handle block mark swapping. */ - mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); - - /* Compile the DMA descriptor - write data. */ - d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = - MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | - MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - - d->cmd.address = 0; - - d->cmd.pio_words[0] = - GPMI_CTRL0_COMMAND_MODE_WRITE | - GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | - GPMI_CTRL0_ADDRESS_NAND_DATA; - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = - GPMI_ECCCTRL_ENABLE_ECC | - GPMI_ECCCTRL_ECC_CMD_ENCODE | - GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); - d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; - - mxs_dma_desc_append(channel, d); - - /* Flush caches */ - mxs_nand_flush_data_buf(nand_info); - - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); - if (ret) { - printf("MXS NAND: DMA write error\n"); - goto rtn; - } - - ret = mxs_nand_wait_for_bch_complete(nand_info); - if (ret) { - printf("MXS NAND: BCH write timeout\n"); - goto rtn; - } - -rtn: - mxs_nand_return_dma_descs(nand_info); - return 0; -} - -/* - * Read OOB from NAND. - * - * This function is a veneer that replaces the function originally installed by - * the NAND Flash MTD code. - */ -static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - int ret; - - if (ops->mode == MTD_OPS_RAW) - nand_info->raw_oob_mode = 1; - else - nand_info->raw_oob_mode = 0; - - ret = nand_info->hooked_read_oob(mtd, from, ops); - - nand_info->raw_oob_mode = 0; - - return ret; -} - -/* - * Write OOB to NAND. - * - * This function is a veneer that replaces the function originally installed by - * the NAND Flash MTD code. - */ -static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - int ret; - - if (ops->mode == MTD_OPS_RAW) - nand_info->raw_oob_mode = 1; - else - nand_info->raw_oob_mode = 0; - - ret = nand_info->hooked_write_oob(mtd, to, ops); - - nand_info->raw_oob_mode = 0; - - return ret; -} - -/* - * Mark a block bad in NAND. - * - * This function is a veneer that replaces the function originally installed by - * the NAND Flash MTD code. - */ -static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - int ret; - - nand_info->marking_block_bad = 1; - - ret = nand_info->hooked_block_markbad(mtd, ofs); - - nand_info->marking_block_bad = 0; - - return ret; -} - -/* - * There are several places in this driver where we have to handle the OOB and - * block marks. This is the function where things are the most complicated, so - * this is where we try to explain it all. All the other places refer back to - * here. - * - * These are the rules, in order of decreasing importance: - * - * 1) Nothing the caller does can be allowed to imperil the block mark, so all - * write operations take measures to protect it. - * - * 2) In read operations, the first byte of the OOB we return must reflect the - * true state of the block mark, no matter where that block mark appears in - * the physical page. - * - * 3) ECC-based read operations return an OOB full of set bits (since we never - * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads - * return). - * - * 4) "Raw" read operations return a direct view of the physical bytes in the - * page, using the conventional definition of which bytes are data and which - * are OOB. This gives the caller a way to see the actual, physical bytes - * in the page, without the distortions applied by our ECC engine. - * - * What we do for this specific read operation depends on whether we're doing - * "raw" read, or an ECC-based read. - * - * It turns out that knowing whether we want an "ECC-based" or "raw" read is not - * easy. When reading a page, for example, the NAND Flash MTD code calls our - * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an - * ECC-based or raw view of the page is implicit in which function it calls - * (there is a similar pair of ECC-based/raw functions for writing). - * - * Since MTD assumes the OOB is not covered by ECC, there is no pair of - * ECC-based/raw functions for reading or or writing the OOB. The fact that the - * caller wants an ECC-based or raw view of the page is not propagated down to - * this driver. - * - * Since our OOB *is* covered by ECC, we need this information. So, we hook the - * ecc.read_oob and ecc.write_oob function pointers in the owning - * struct mtd_info with our own functions. These hook functions set the - * raw_oob_mode field so that, when control finally arrives here, we'll know - * what to do. - */ -static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page) -{ - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - - /* - * First, fill in the OOB buffer. If we're doing a raw read, we need to - * get the bytes from the physical page. If we're not doing a raw read, - * we need to fill the buffer with set bits. - */ - if (nand_info->raw_oob_mode) { - /* - * If control arrives here, we're doing a "raw" read. Send the - * command to read the conventional OOB and read it. - */ - nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); - nand->read_buf(mtd, nand->oob_poi, mtd->oobsize); - } else { - /* - * If control arrives here, we're not doing a "raw" read. Fill - * the OOB buffer with set bits and correct the block mark. - */ - memset(nand->oob_poi, 0xff, mtd->oobsize); - - nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); - mxs_nand_read_buf(mtd, nand->oob_poi, 1); - } - - return 0; - -} - -/* - * Write OOB data to NAND. - */ -static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page) -{ - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - uint8_t block_mark = 0; - - /* - * There are fundamental incompatibilities between the i.MX GPMI NFC and - * the NAND Flash MTD model that make it essentially impossible to write - * the out-of-band bytes. - * - * We permit *ONE* exception. If the *intent* of writing the OOB is to - * mark a block bad, we can do that. - */ - - if (!nand_info->marking_block_bad) { - printf("NXS NAND: Writing OOB isn't supported\n"); - return -EIO; - } - - /* Write the block mark. */ - nand->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - nand->write_buf(mtd, &block_mark, 1); - nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - /* Check if it worked. */ - if (nand->waitfunc(mtd, nand) & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} - -/* - * Claims all blocks are good. - * - * In principle, this function is *only* called when the NAND Flash MTD system - * isn't allowed to keep an in-memory bad block table, so it is forced to ask - * the driver for bad block information. - * - * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so - * this function is *only* called when we take it away. - * - * Thus, this function is only called when we want *all* blocks to look good, - * so it *always* return success. - */ -static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - return 0; -} - -static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - - if (chip->ecc.strength > 0 && chip->ecc.size > 0) - return mxs_nand_calc_ecc_layout_by_info(geo, mtd, - chip->ecc.strength, chip->ecc.size); - - if (nand_info->use_minimum_ecc || - mxs_nand_calc_ecc_layout(geo, mtd)) { - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -EINVAL; - - return mxs_nand_calc_ecc_layout_by_info(geo, mtd, - chip->ecc_strength_ds, chip->ecc_step_ds); - } - - return 0; -} - -/* - * At this point, the physical NAND Flash chips have been identified and - * counted, so we know the physical geometry. This enables us to make some - * important configuration decisions. - * - * The return value of this function propagates directly back to this driver's - * board_nand_init(). Anything other than zero will cause this driver to - * tear everything down and declare failure. - */ -int mxs_nand_setup_ecc(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct mxs_nand_info *nand_info = nand_get_controller_data(nand); - struct bch_geometry *geo = &nand_info->bch_geometry; - struct mxs_bch_regs *bch_regs = nand_info->bch_regs; - uint32_t tmp; - int ret; - - ret = mxs_nand_set_geometry(mtd, geo); - if (ret) - return ret; - - mxs_nand_calc_mark_offset(geo, mtd->writesize); - - /* Configure BCH and set NFC geometry */ - mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); - - /* Configure layout 0 */ - tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; - tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; - tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; - tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; - tmp |= (geo->gf_len == 14 ? 1 : 0) << - BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; - writel(tmp, &bch_regs->hw_bch_flash0layout0); - - tmp = (mtd->writesize + mtd->oobsize) - << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; - tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; - tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; - tmp |= (geo->gf_len == 14 ? 1 : 0) << - BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; - writel(tmp, &bch_regs->hw_bch_flash0layout1); - - /* Set *all* chip selects to use layout 0 */ - writel(0, &bch_regs->hw_bch_layoutselect); - - /* Enable BCH complete interrupt */ - writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); - - /* Hook some operations at the MTD level. */ - if (mtd->_read_oob != mxs_nand_hook_read_oob) { - nand_info->hooked_read_oob = mtd->_read_oob; - mtd->_read_oob = mxs_nand_hook_read_oob; - } - - if (mtd->_write_oob != mxs_nand_hook_write_oob) { - nand_info->hooked_write_oob = mtd->_write_oob; - mtd->_write_oob = mxs_nand_hook_write_oob; - } - - if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { - nand_info->hooked_block_markbad = mtd->_block_markbad; - mtd->_block_markbad = mxs_nand_hook_block_markbad; - } - - return 0; -} - -/* - * Allocate DMA buffers - */ -int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) -{ - uint8_t *buf; - const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; - - nand_info->data_buf_size = roundup(size, MXS_DMA_ALIGNMENT); - - /* DMA buffers */ - buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size); - if (!buf) { - printf("MXS NAND: Error allocating DMA buffers\n"); - return -ENOMEM; - } - - memset(buf, 0, nand_info->data_buf_size); - - nand_info->data_buf = buf; - nand_info->oob_buf = buf + NAND_MAX_PAGESIZE; - /* Command buffers */ - nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT, - MXS_NAND_COMMAND_BUFFER_SIZE); - if (!nand_info->cmd_buf) { - free(buf); - printf("MXS NAND: Error allocating command buffers\n"); - return -ENOMEM; - } - memset(nand_info->cmd_buf, 0, MXS_NAND_COMMAND_BUFFER_SIZE); - nand_info->cmd_queue_len = 0; - - return 0; -} - -/* - * Initializes the NFC hardware. - */ -int mxs_nand_init_dma(struct mxs_nand_info *info) -{ - int i = 0, j, ret = 0; - - info->desc = malloc(sizeof(struct mxs_dma_desc *) * - MXS_NAND_DMA_DESCRIPTOR_COUNT); - if (!info->desc) { - ret = -ENOMEM; - goto err1; - } - - /* Allocate the DMA descriptors. */ - for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { - info->desc[i] = mxs_dma_desc_alloc(); - if (!info->desc[i]) { - ret = -ENOMEM; - goto err2; - } - } - - /* Init the DMA controller. */ - mxs_dma_init(); - for (j = MXS_DMA_CHANNEL_AHB_APBH_GPMI0; - j <= MXS_DMA_CHANNEL_AHB_APBH_GPMI7; j++) { - ret = mxs_dma_init_channel(j); - if (ret) - goto err3; - } - - /* Reset the GPMI block. */ - mxs_reset_block(&info->gpmi_regs->hw_gpmi_ctrl0_reg); - mxs_reset_block(&info->bch_regs->hw_bch_ctrl_reg); - - /* - * Choose NAND mode, set IRQ polarity, disable write protection and - * select BCH ECC. - */ - clrsetbits_le32(&info->gpmi_regs->hw_gpmi_ctrl1, - GPMI_CTRL1_GPMI_MODE, - GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | - GPMI_CTRL1_BCH_MODE); - - return 0; - -err3: - for (--j; j >= MXS_DMA_CHANNEL_AHB_APBH_GPMI0; j--) - mxs_dma_release(j); -err2: - for (--i; i >= 0; i--) - mxs_dma_desc_free(info->desc[i]); - free(info->desc); -err1: - if (ret == -ENOMEM) - printf("MXS NAND: Unable to allocate DMA descriptors\n"); - return ret; -} - -int mxs_nand_init_spl(struct nand_chip *nand) -{ - struct mxs_nand_info *nand_info; - int err; - - nand_info = malloc(sizeof(struct mxs_nand_info)); - if (!nand_info) { - printf("MXS NAND: Failed to allocate private data\n"); - return -ENOMEM; - } - memset(nand_info, 0, sizeof(struct mxs_nand_info)); - - nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; - nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; - err = mxs_nand_alloc_buffers(nand_info); - if (err) - return err; - - err = mxs_nand_init_dma(nand_info); - if (err) - return err; - - nand_set_controller_data(nand, nand_info); - - nand->options |= NAND_NO_SUBPAGE_WRITE; - - nand->cmd_ctrl = mxs_nand_cmd_ctrl; - nand->dev_ready = mxs_nand_device_ready; - nand->select_chip = mxs_nand_select_chip; - - nand->read_byte = mxs_nand_read_byte; - nand->read_buf = mxs_nand_read_buf; - - nand->ecc.read_page = mxs_nand_ecc_read_page; - - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.bytes = 9; - nand->ecc.size = 512; - nand->ecc.strength = 8; - - return 0; -} - -int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) -{ - struct mtd_info *mtd; - struct nand_chip *nand; - int err; - - nand = &nand_info->chip; - mtd = nand_to_mtd(nand); - err = mxs_nand_alloc_buffers(nand_info); - if (err) - return err; - - err = mxs_nand_init_dma(nand_info); - if (err) - goto err_free_buffers; - - memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; -#endif - - nand_set_controller_data(nand, nand_info); - nand->options |= NAND_NO_SUBPAGE_WRITE; - - if (nand_info->dev) - nand->flash_node = dev_of_offset(nand_info->dev); - - nand->cmd_ctrl = mxs_nand_cmd_ctrl; - - nand->dev_ready = mxs_nand_device_ready; - nand->select_chip = mxs_nand_select_chip; - nand->block_bad = mxs_nand_block_bad; - - nand->read_byte = mxs_nand_read_byte; - - nand->read_buf = mxs_nand_read_buf; - nand->write_buf = mxs_nand_write_buf; - - /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) - goto err_free_buffers; - - if (mxs_nand_setup_ecc(mtd)) - goto err_free_buffers; - - nand->ecc.read_page = mxs_nand_ecc_read_page; - nand->ecc.write_page = mxs_nand_ecc_write_page; - nand->ecc.read_oob = mxs_nand_ecc_read_oob; - nand->ecc.write_oob = mxs_nand_ecc_write_oob; - - nand->ecc.layout = &fake_ecc_layout; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size; - nand->ecc.strength = nand_info->bch_geometry.ecc_strength; - - /* second phase scan */ - err = nand_scan_tail(mtd); - if (err) - goto err_free_buffers; - - err = nand_register(0, mtd); - if (err) - goto err_free_buffers; - - return 0; - -err_free_buffers: - free(nand_info->data_buf); - free(nand_info->cmd_buf); - - return err; -} - -#ifndef CONFIG_NAND_MXS_DT -void board_nand_init(void) -{ - struct mxs_nand_info *nand_info; - - nand_info = malloc(sizeof(struct mxs_nand_info)); - if (!nand_info) { - printf("MXS NAND: Failed to allocate private data\n"); - return; - } - memset(nand_info, 0, sizeof(struct mxs_nand_info)); - - nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; - nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; - - /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ - if (is_mx6sx() || is_mx7()) - nand_info->max_ecc_strength_supported = 62; - else - nand_info->max_ecc_strength_supported = 40; - -#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC - nand_info->use_minimum_ecc = true; -#endif - - if (mxs_nand_init_ctrl(nand_info) < 0) - goto err; - - return; - -err: - free(nand_info); -} -#endif diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h deleted file mode 100644 index 4bd65cded9..0000000000 --- a/drivers/mtd/nand/mxs_nand.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * NXP GPMI NAND flash driver - * - * Copyright (C) 2018 Toradex - * Authors: - * Stefan Agner - */ - -#include -#include -#include -#include - -/** - * @gf_len: The length of Galois Field. (e.g., 13 or 14) - * @ecc_strength: A number that describes the strength of the ECC - * algorithm. - * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note - * the first chunk in the page includes both data and - * metadata, so it's a bit larger than this value. - * @ecc_chunk_count: The number of ECC chunks in the page, - * @block_mark_byte_offset: The byte offset in the ECC-based page view at - * which the underlying physical block mark appears. - * @block_mark_bit_offset: The bit offset into the ECC-based page view at - * which the underlying physical block mark appears. - */ -struct bch_geometry { - unsigned int gf_len; - unsigned int ecc_strength; - unsigned int ecc_chunk_size; - unsigned int ecc_chunk_count; - unsigned int block_mark_byte_offset; - unsigned int block_mark_bit_offset; -}; - -struct mxs_nand_info { - struct nand_chip chip; - struct udevice *dev; - unsigned int max_ecc_strength_supported; - bool use_minimum_ecc; - int cur_chip; - - uint32_t cmd_queue_len; - uint32_t data_buf_size; - struct bch_geometry bch_geometry; - - uint8_t *cmd_buf; - uint8_t *data_buf; - uint8_t *oob_buf; - - uint8_t marking_block_bad; - uint8_t raw_oob_mode; - - struct mxs_gpmi_regs *gpmi_regs; - struct mxs_bch_regs *bch_regs; - - /* Functions with altered behaviour */ - int (*hooked_read_oob)(struct mtd_info *mtd, - loff_t from, struct mtd_oob_ops *ops); - int (*hooked_write_oob)(struct mtd_info *mtd, - loff_t to, struct mtd_oob_ops *ops); - int (*hooked_block_markbad)(struct mtd_info *mtd, - loff_t ofs); - - /* DMA descriptors */ - struct mxs_dma_desc **desc; - uint32_t desc_index; -}; - -int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info); -int mxs_nand_init_spl(struct nand_chip *nand); -int mxs_nand_setup_ecc(struct mtd_info *mtd); diff --git a/drivers/mtd/nand/mxs_nand_dt.c b/drivers/mtd/nand/mxs_nand_dt.c deleted file mode 100644 index 44dec5dedf..0000000000 --- a/drivers/mtd/nand/mxs_nand_dt.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * NXP GPMI NAND flash driver (DT initialization) - * - * Copyright (C) 2018 Toradex - * Authors: - * Stefan Agner - * - * Based on denali_dt.c - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include - -#include "mxs_nand.h" - -struct mxs_nand_dt_data { - unsigned int max_ecc_strength_supported; -}; - -static const struct mxs_nand_dt_data mxs_nand_imx6q_data = { - .max_ecc_strength_supported = 40, -}; - -static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { - .max_ecc_strength_supported = 62, -}; - -static const struct udevice_id mxs_nand_dt_ids[] = { - { - .compatible = "fsl,imx6q-gpmi-nand", - .data = (unsigned long)&mxs_nand_imx6q_data, - }, - { - .compatible = "fsl,imx7d-gpmi-nand", - .data = (unsigned long)&mxs_nand_imx7d_data, - }, - { /* sentinel */ } -}; - -static int mxs_nand_dt_probe(struct udevice *dev) -{ - struct mxs_nand_info *info = dev_get_priv(dev); - const struct mxs_nand_dt_data *data; - struct resource res; - int ret; - - data = (void *)dev_get_driver_data(dev); - if (data) - info->max_ecc_strength_supported = data->max_ecc_strength_supported; - - info->dev = dev; - - ret = dev_read_resource_byname(dev, "gpmi-nand", &res); - if (ret) - return ret; - - info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res)); - - - ret = dev_read_resource_byname(dev, "bch", &res); - if (ret) - return ret; - - info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res)); - - info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc"); - - return mxs_nand_init_ctrl(info); -} - -U_BOOT_DRIVER(mxs_nand_dt) = { - .name = "mxs-nand-dt", - .id = UCLASS_MTD, - .of_match = mxs_nand_dt_ids, - .probe = mxs_nand_dt_probe, - .priv_auto_alloc_size = sizeof(struct mxs_nand_info), -}; - -void board_nand_init(void) -{ - struct udevice *dev; - int ret; - - ret = uclass_get_device_by_driver(UCLASS_MTD, - DM_GET_DRIVER(mxs_nand_dt), - &dev); - if (ret && ret != -ENODEV) - pr_err("Failed to initialize MXS NAND controller. (error %d)\n", - ret); -} diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c deleted file mode 100644 index 2d7bbe83cc..0000000000 --- a/drivers/mtd/nand/mxs_nand_spl.c +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2014 Gateworks Corporation - * Author: Tim Harvey - */ -#include -#include -#include -#include "mxs_nand.h" - -static struct mtd_info *mtd; -static struct nand_chip nand_chip; - -static void mxs_nand_command(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - u32 timeo, time_start; - - /* write out the command to the device */ - chip->cmd_ctrl(mtd, command, NAND_CLE); - - /* Serially input address */ - if (column != -1) { - chip->cmd_ctrl(mtd, column, NAND_ALE); - chip->cmd_ctrl(mtd, column >> 8, NAND_ALE); - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, NAND_ALE); - chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE); - /* One more address cycle for devices > 128MiB */ - if (chip->chipsize > (128 << 20)) - chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE); - } - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0); - - if (command == NAND_CMD_READ0) { - chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0); - } - - /* wait for nand ready */ - ndelay(100); - timeo = (CONFIG_SYS_HZ * 20) / 1000; - time_start = get_timer(0); - while (get_timer(time_start) < timeo) { - if (chip->dev_ready(mtd)) - break; - } -} - -#if defined (CONFIG_SPL_NAND_IDENT) - -/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */ -static int mxs_flash_full_ident(struct mtd_info *mtd) -{ - int nand_maf_id, nand_dev_id; - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_flash_dev *type; - - type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL); - - if (IS_ERR(type)) { - chip->select_chip(mtd, -1); - return PTR_ERR(type); - } - - return 0; -} - -#else - -/* Trying to detect the NAND flash using ONFi only */ -static int mxs_flash_onfi_ident(struct mtd_info *mtd) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - int i; - u8 mfg_id, dev_id; - u8 id_data[8]; - struct nand_onfi_params *p = &chip->onfi_params; - - /* Reset the chip */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - /* Read manufacturer and device IDs */ - mfg_id = chip->read_byte(mtd); - dev_id = chip->read_byte(mtd); - - /* Try again to make sure */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - for (i = 0; i < 8; i++) - id_data[i] = chip->read_byte(mtd); - if (id_data[0] != mfg_id || id_data[1] != dev_id) { - printf("second ID read did not match"); - return -1; - } - debug("0x%02x:0x%02x ", mfg_id, dev_id); - - /* read ONFI */ - chip->onfi_version = 0; - chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); - if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || - chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') { - return -2; - } - - /* we have ONFI, probe it */ - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); - chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); - mtd->name = p->model; - mtd->writesize = le32_to_cpu(p->byte_per_page); - mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; - mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = le32_to_cpu(p->blocks_per_lun); - chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - /* Calculate the address shift from the page size */ - chip->page_shift = ffs(mtd->writesize) - 1; - chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - /* Convert chipsize to number of pages per chip -1 */ - chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; - chip->badblockbits = 8; - - debug("erasesize=%d (>>%d)\n", mtd->erasesize, chip->phys_erase_shift); - debug("writesize=%d (>>%d)\n", mtd->writesize, chip->page_shift); - debug("oobsize=%d\n", mtd->oobsize); - debug("chipsize=%lld\n", chip->chipsize); - - return 0; -} - -#endif /* CONFIG_SPL_NAND_IDENT */ - -static int mxs_flash_ident(struct mtd_info *mtd) -{ - int ret; -#if defined (CONFIG_SPL_NAND_IDENT) - ret = mxs_flash_full_ident(mtd); -#else - ret = mxs_flash_onfi_ident(mtd); -#endif - return ret; -} - -static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page); - ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page); - if (ret < 0) { - printf("read_page failed %d\n", ret); - return -1; - } - return 0; -} - -static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - unsigned int block = offs >> chip->phys_erase_shift; - unsigned int page = offs >> chip->page_shift; - - debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block, - page); - chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); - memset(chip->oob_poi, 0, mtd->oobsize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return chip->oob_poi[0] != 0xff; -} - -/* setup mtd and nand structs and init mxs_nand driver */ -static int mxs_nand_init(void) -{ - /* return if already initalized */ - if (nand_chip.numchips) - return 0; - - /* init mxs nand driver */ - mxs_nand_init_spl(&nand_chip); - mtd = nand_to_mtd(&nand_chip); - /* set mtd functions */ - nand_chip.cmdfunc = mxs_nand_command; - nand_chip.numchips = 1; - - /* identify flash device */ - if (mxs_flash_ident(mtd)) { - printf("Failed to identify\n"); - return -1; - } - - /* allocate and initialize buffers */ - nand_chip.buffers = memalign(ARCH_DMA_MINALIGN, - sizeof(*nand_chip.buffers)); - nand_chip.oob_poi = nand_chip.buffers->databuf + mtd->writesize; - /* setup flash layout (does not scan as we override that) */ - mtd->size = nand_chip.chipsize; - nand_chip.scan_bbt(mtd); - - return 0; -} - -int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) -{ - struct nand_chip *chip; - unsigned int page; - unsigned int nand_page_per_block; - unsigned int sz = 0; - - if (mxs_nand_init()) - return -ENODEV; - chip = mtd_to_nand(mtd); - page = offs >> chip->page_shift; - nand_page_per_block = mtd->erasesize / mtd->writesize; - - debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page); - - size = roundup(size, mtd->writesize); - while (sz < size) { - if (mxs_read_page_ecc(mtd, buf, page) < 0) - return -1; - sz += mtd->writesize; - offs += mtd->writesize; - page++; - buf += mtd->writesize; - - /* - * Check if we have crossed a block boundary, and if so - * check for bad block. - */ - if (!(page % nand_page_per_block)) { - /* - * Yes, new block. See if this block is good. If not, - * loop until we find a good block. - */ - while (is_badblock(mtd, offs, 1)) { - page = page + nand_page_per_block; - /* Check i we've reached the end of flash. */ - if (page >= mtd->size >> chip->page_shift) - return -ENOMEM; - } - } - } - - return 0; -} - -int nand_default_bbt(struct mtd_info *mtd) -{ - return 0; -} - -void nand_init(void) -{ -} - -void nand_deselect(void) -{ -} - diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c deleted file mode 100644 index bca51ffbf2..0000000000 --- a/drivers/mtd/nand/nand.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * (C) Copyright 2005 - * 2N Telekomunikace, a.s. - * Ladislav Michl - */ - -#include -#include -#include -#include - -#ifndef CONFIG_SYS_NAND_BASE_LIST -#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } -#endif - -int nand_curr_device = -1; - -static struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; - -#ifndef CONFIG_SYS_NAND_SELF_INIT -static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; -static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; -#endif - -static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; - -static unsigned long total_nand_size; /* in kiB */ - -struct mtd_info *get_nand_dev_by_index(int dev) -{ - if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev] || - !nand_info[dev]->name) - return NULL; - - return nand_info[dev]; -} - -int nand_mtd_to_devnum(struct mtd_info *mtd) -{ - int i; - - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { - if (mtd && get_nand_dev_by_index(i) == mtd) - return i; - } - - return -ENODEV; -} - -/* Register an initialized NAND mtd device with the U-Boot NAND command. */ -int nand_register(int devnum, struct mtd_info *mtd) -{ - if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE) - return -EINVAL; - - nand_info[devnum] = mtd; - - sprintf(dev_name[devnum], "nand%d", devnum); - mtd->name = dev_name[devnum]; - -#ifdef CONFIG_MTD_DEVICE - /* - * Add MTD device so that we can reference it later - * via the mtdcore infrastructure (e.g. ubi). - */ - add_mtd_device(mtd); -#endif - - total_nand_size += mtd->size / 1024; - - if (nand_curr_device == -1) - nand_curr_device = devnum; - - return 0; -} - -#ifndef CONFIG_SYS_NAND_SELF_INIT -static void nand_init_chip(int i) -{ - struct nand_chip *nand = &nand_chip[i]; - struct mtd_info *mtd = nand_to_mtd(nand); - ulong base_addr = base_address[i]; - int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; - - if (maxchips < 1) - maxchips = 1; - - nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; - - if (board_nand_init(nand)) - return; - - if (nand_scan(mtd, maxchips)) - return; - - nand_register(i, mtd); -} -#endif - -#ifdef CONFIG_MTD_CONCAT -static void create_mtd_concat(void) -{ - struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE]; - int nand_devices_found = 0; - int i; - - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { - struct mtd_info *mtd = get_nand_dev_by_index(i); - if (mtd != NULL) { - nand_info_list[nand_devices_found] = mtd; - nand_devices_found++; - } - } - if (nand_devices_found > 1) { - struct mtd_info *mtd; - char c_mtd_name[16]; - - /* - * We detected multiple devices. Concatenate them together. - */ - sprintf(c_mtd_name, "nand%d", nand_devices_found); - mtd = mtd_concat_create(nand_info_list, nand_devices_found, - c_mtd_name); - - if (mtd == NULL) - return; - - nand_register(nand_devices_found, mtd); - } - - return; -} -#else -static void create_mtd_concat(void) -{ -} -#endif - -unsigned long nand_size(void) -{ - return total_nand_size; -} - -void nand_init(void) -{ - static int initialized; - - /* - * Avoid initializing NAND Flash multiple times, - * otherwise it will calculate a wrong total size. - */ - if (initialized) - return; - initialized = 1; - -#ifdef CONFIG_SYS_NAND_SELF_INIT - board_nand_init(); -#else - int i; - - for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) - nand_init_chip(i); -#endif - -#ifdef CONFIG_SYS_NAND_SELECT_DEVICE - /* - * Select the chip in the board/cpu specific driver - */ - board_nand_select_device(mtd_to_nand(get_nand_dev_by_index(nand_curr_device)), - nand_curr_device); -#endif - - create_mtd_concat(); -} diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c deleted file mode 100644 index 92daebe120..0000000000 --- a/drivers/mtd/nand/nand_base.c +++ /dev/null @@ -1,4619 +0,0 @@ -/* - * Overview: - * This is the generic MTD driver for NAND flash devices. It should be - * capable of working with almost all NAND chips currently available. - * - * Additional technical information is available on - * http://www.linux-mtd.infradead.org/doc/nand.html - * - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * 2002-2006 Thomas Gleixner (tglx@linutronix.de) - * - * Credits: - * David Woodhouse for adding multichip support - * - * Aleph One Ltd. and Toby Churchill Ltd. for supporting the - * rework for 2K page size chips - * - * TODO: - * Enable cached programming for 2k page size chips - * Check, if mtd->ecctype should be set to MTD_ECC_HW - * if we have HW ECC support. - * BBT table is not serialized, has to be fixed - * - * This program 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. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#if CONFIG_IS_ENABLED(OF_CONTROL) -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_MTD_PARTITIONS -#include -#endif -#include -#include - -/* Define default oob placement schemes for large and small page devices */ -static struct nand_ecclayout nand_oob_8 = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - {.offset = 3, - .length = 2}, - {.offset = 6, - .length = 2} } -}; - -static struct nand_ecclayout nand_oob_16 = { - .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = { - {.offset = 8, - . length = 8} } -}; - -static struct nand_ecclayout nand_oob_64 = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 38} } -}; - -static struct nand_ecclayout nand_oob_128 = { - .eccbytes = 48, - .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { - {.offset = 2, - .length = 78} } -}; - -static int nand_get_device(struct mtd_info *mtd, int new_state); - -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); - -/* - * For devices which display every fart in the system on a separate LED. Is - * compiled away when LED support is disabled. - */ -DEFINE_LED_TRIGGER(nand_led_trigger); - -static int check_offs_len(struct mtd_info *mtd, - loff_t ofs, uint64_t len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int ret = 0; - - /* Start address must align on block boundary */ - if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { - pr_debug("%s: unaligned address\n", __func__); - ret = -EINVAL; - } - - /* Length must align on block boundary */ - if (len & ((1ULL << chip->phys_erase_shift) - 1)) { - pr_debug("%s: length not block aligned\n", __func__); - ret = -EINVAL; - } - - return ret; -} - -/** - * nand_release_device - [GENERIC] release chip - * @mtd: MTD device structure - * - * Release chip lock and wake up anyone waiting on the device. - */ -static void nand_release_device(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - /* De-select the NAND device */ - chip->select_chip(mtd, -1); -} - -/** - * nand_read_byte - [DEFAULT] read one byte from the chip - * @mtd: MTD device structure - * - * Default read function for 8bit buswidth - */ -uint8_t nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return readb(chip->IO_ADDR_R); -} - -/** - * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip - * @mtd: MTD device structure - * - * Default read function for 16bit buswidth with endianness conversion. - * - */ -static uint8_t nand_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); -} - -/** - * nand_read_word - [DEFAULT] read one word from the chip - * @mtd: MTD device structure - * - * Default read function for 16bit buswidth without endianness conversion. - */ -static u16 nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - return readw(chip->IO_ADDR_R); -} - -/** - * nand_select_chip - [DEFAULT] control CE line - * @mtd: MTD device structure - * @chipnr: chipnumber to select, -1 for deselect - * - * Default select function for 1 chip devices. - */ -static void nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - switch (chipnr) { - case -1: - chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); - break; - case 0: - break; - - default: - BUG(); - } -} - -/** - * nand_write_byte - [DEFAULT] write single byte to chip - * @mtd: MTD device structure - * @byte: value to write - * - * Default function to write a byte to I/O[7:0] - */ -static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - chip->write_buf(mtd, &byte, 1); -} - -/** - * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 - * @mtd: MTD device structure - * @byte: value to write - * - * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. - */ -static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - uint16_t word = byte; - - /* - * It's not entirely clear what should happen to I/O[15:8] when writing - * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: - * - * When the host supports a 16-bit bus width, only data is - * transferred at the 16-bit width. All address and command line - * transfers shall use only the lower 8-bits of the data bus. During - * command transfers, the host may place any value on the upper - * 8-bits of the data bus. During address transfers, the host shall - * set the upper 8-bits of the data bus to 00h. - * - * One user of the write_byte callback is nand_onfi_set_features. The - * four parameters are specified to be written to I/O[7:0], but this is - * neither an address nor a command transfer. Let's assume a 0 on the - * upper I/O lines is OK. - */ - chip->write_buf(mtd, (uint8_t *)&word, 2); -} - -static void iowrite8_rep(void *addr, const uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - writeb(buf[i], addr); -} -static void ioread8_rep(void *addr, uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - buf[i] = readb(addr); -} - -static void ioread16_rep(void *addr, void *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - - for (i = 0; i < len; i++) - p[i] = readw(addr); -} - -static void iowrite16_rep(void *addr, void *buf, int len) -{ - int i; - u16 *p = (u16 *) buf; - - for (i = 0; i < len; i++) - writew(p[i], addr); -} - -/** - * nand_write_buf - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 8bit buswidth. - */ -void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - iowrite8_rep(chip->IO_ADDR_W, buf, len); -} - -/** - * nand_read_buf - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * Default read function for 8bit buswidth. - */ -void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - ioread8_rep(chip->IO_ADDR_R, buf, len); -} - -/** - * nand_write_buf16 - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 16bit buswidth. - */ -void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - - iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); -} - -/** - * nand_read_buf16 - [DEFAULT] read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - * - * Default read function for 16bit buswidth. - */ -void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u16 *p = (u16 *) buf; - - ioread16_rep(chip->IO_ADDR_R, p, len >> 1); -} - -/** - * nand_block_bad - [DEFAULT] Read bad block marker from the chip - * @mtd: MTD device structure - * @ofs: offset from device start - * - * Check, if the block is bad. - */ -static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) -{ - int page, res = 0, i = 0; - struct nand_chip *chip = mtd_to_nand(mtd); - u16 bad; - - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; - - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - - do { - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, - chip->badblockpos & 0xFE, page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, - page); - bad = chip->read_byte(mtd); - } - - if (likely(chip->badblockbits == 8)) - res = bad != 0xFF; - else - res = hweight8(bad) < chip->badblockbits; - ofs += mtd->writesize; - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - i++; - } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); - - return res; -} - -/** - * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This is the default implementation, which can be overridden by a hardware - * specific driver. It provides the details for writing a bad block marker to a - * block. - */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtd_oob_ops ops; - uint8_t buf[2] = { 0, 0 }; - int ret = 0, res, i = 0; - - memset(&ops, 0, sizeof(ops)); - ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - ops.ooboffs &= ~0x01; - ops.len = ops.ooblen = 2; - } else { - ops.len = ops.ooblen = 1; - } - ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; - do { - res = nand_do_write_oob(mtd, ofs, &ops); - if (!ret) - ret = res; - - i++; - ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - - return ret; -} - -/** - * nand_block_markbad_lowlevel - mark a block bad - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This function performs the generic NAND bad block marking steps (i.e., bad - * block table(s) and/or marker(s)). We only allow the hardware driver to - * specify how to write bad block markers to OOB (chip->block_markbad). - * - * We try operations in the following order: - * (1) erase the affected block, to allow OOB marker to be written cleanly - * (2) write bad block marker to OOB area of affected block (unless flag - * NAND_BBT_NO_OOB_BBM is present) - * (3) update the BBT - * Note that we retain the first error encountered in (2) or (3), finish the - * procedures, and dump the error in the end. -*/ -static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int res, ret = 0; - - if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { - struct erase_info einfo; - - /* Attempt erase before marking OOB */ - memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; - einfo.addr = ofs; - einfo.len = 1ULL << chip->phys_erase_shift; - nand_erase_nand(mtd, &einfo, 0); - - /* Write bad block marker to OOB */ - nand_get_device(mtd, FL_WRITING); - ret = chip->block_markbad(mtd, ofs); - nand_release_device(mtd); - } - - /* Mark block bad in BBT */ - if (chip->bbt) { - res = nand_markbad_bbt(mtd, ofs); - if (!ret) - ret = res; - } - - if (!ret) - mtd->ecc_stats.badblocks++; - - return ret; -} - -/** - * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd: MTD device structure - * - * Check, if the device is write protected. The function expects, that the - * device is already selected. - */ -static int nand_check_wp(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - /* Broken xD cards report WP despite being writable */ - if (chip->options & NAND_BROKEN_XD) - return 0; - - /* Check the WP bit */ - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; -} - -/** - * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. - * @mtd: MTD device structure - * @ofs: offset from device start - * - * Check if the block is marked as reserved. - */ -static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (!chip->bbt) - return 0; - /* Return info from the table */ - return nand_isreserved_bbt(mtd, ofs); -} - -/** - * nand_block_checkbad - [GENERIC] Check if a block is marked bad - * @mtd: MTD device structure - * @ofs: offset from device start - * @allowbbt: 1, if its allowed to access the bbt area - * - * Check, if the block is bad. Either by reading the bad block table or - * calling of the scan function. - */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (!(chip->options & NAND_SKIP_BBTSCAN) && - !(chip->options & NAND_BBT_SCANNED)) { - chip->options |= NAND_BBT_SCANNED; - chip->scan_bbt(mtd); - } - - if (!chip->bbt) - return chip->block_bad(mtd, ofs); - - /* Return info from the table */ - return nand_isbad_bbt(mtd, ofs, allowbbt); -} - -/** - * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. - * @mtd: MTD device structure - * - * Wait for the ready pin after a command, and warn if a timeout occurs. - */ -void nand_wait_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - u32 timeo = (CONFIG_SYS_HZ * 400) / 1000; - u32 time_start; - - time_start = get_timer(0); - /* Wait until command is processed or timeout occurs */ - while (get_timer(time_start) < timeo) { - if (chip->dev_ready) - if (chip->dev_ready(mtd)) - break; - } - - if (!chip->dev_ready(mtd)) - pr_warn("timeout while waiting for chip to become ready\n"); -} -EXPORT_SYMBOL_GPL(nand_wait_ready); - -/** - * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. - * @mtd: MTD device structure - * @timeo: Timeout in ms - * - * Wait for status ready (i.e. command done) or timeout. - */ -static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - u32 time_start; - - timeo = (CONFIG_SYS_HZ * timeo) / 1000; - time_start = get_timer(0); - while (get_timer(time_start) < timeo) { - if ((chip->read_byte(mtd) & NAND_STATUS_READY)) - break; - WATCHDOG_RESET(); - } -}; - -/** - * nand_command - [DEFAULT] Send command to NAND device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - * - * Send command to NAND device. This function is used for small page devices - * (512 Bytes per page). - */ -static void nand_command(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; - - /* Write out the command to the device */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->writesize) { - /* OOB area */ - column -= mtd->writesize; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - chip->cmd_ctrl(mtd, readcmd, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - } - chip->cmd_ctrl(mtd, command, ctrl); - - /* Address cycle, when necessary */ - ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - chip->cmd_ctrl(mtd, column, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); - if (chip->options & NAND_ROW_ADDR_3) - chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); - } - chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Program and erase have their own busy handlers status and sequential - * in needs no delay - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - case NAND_CMD_READID: - case NAND_CMD_SET_FEATURES: - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - chip->cmd_ctrl(mtd, NAND_CMD_STATUS, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, - NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); - return; - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - /* - * Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. - */ - ndelay(100); - - nand_wait_ready(mtd); -} - -/** - * nand_command_lp - [DEFAULT] Send command to NAND large page device - * @mtd: MTD device structure - * @command: the command to be sent - * @column: the column address for this command, -1 if none - * @page_addr: the page address for this command, -1 if none - * - * Send command to NAND device. This is the version for the new large page - * devices. We don't have the separate regions as we have in the small page - * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. - */ -static void nand_command_lp(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - register struct nand_chip *chip = mtd_to_nand(mtd); - - /* Emulate NAND_CMD_READOOB */ - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* Command latch cycle */ - chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - - if (column != -1 || page_addr != -1) { - int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; - - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - chip->cmd_ctrl(mtd, column, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - chip->cmd_ctrl(mtd, column >> 8, ctrl); - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, ctrl); - chip->cmd_ctrl(mtd, page_addr >> 8, - NAND_NCE | NAND_ALE); - if (chip->options & NAND_ROW_ADDR_3) - chip->cmd_ctrl(mtd, page_addr >> 16, - NAND_NCE | NAND_ALE); - } - } - chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Program and erase have their own busy handlers status, sequential - * in and status need no delay. - */ - switch (command) { - - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - case NAND_CMD_READID: - case NAND_CMD_SET_FEATURES: - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - chip->cmd_ctrl(mtd, NAND_CMD_STATUS, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ - nand_wait_status_ready(mtd, 250); - return; - - case NAND_CMD_RNDOUT: - /* No ready / busy check necessary */ - chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - return; - - case NAND_CMD_READ0: - chip->cmd_ctrl(mtd, NAND_CMD_READSTART, - NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); - chip->cmd_ctrl(mtd, NAND_CMD_NONE, - NAND_NCE | NAND_CTRL_CHANGE); - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay. - */ - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - - /* - * Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. - */ - ndelay(100); - - nand_wait_ready(mtd); -} - -/** - * panic_nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested - * - * Used when in panic, no locks are taken. - */ -static void panic_nand_get_device(struct nand_chip *chip, - struct mtd_info *mtd, int new_state) -{ - /* Hardware controller shared among independent devices */ - chip->controller->active = chip; - chip->state = new_state; -} - -/** - * nand_get_device - [GENERIC] Get chip for selected access - * @mtd: MTD device structure - * @new_state: the state which is requested - * - * Get the device and lock it for exclusive access - */ -static int -nand_get_device(struct mtd_info *mtd, int new_state) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - chip->state = new_state; - return 0; -} - -/** - * panic_nand_wait - [GENERIC] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * @timeo: timeout - * - * Wait for command done. This is a helper function for nand_wait used when - * we are in interrupt context. May happen when in panic and trying to write - * an oops through mtdoops. - */ -static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, - unsigned long timeo) -{ - int i; - for (i = 0; i < timeo; i++) { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) - break; - } - mdelay(1); - } -} - -/** - * nand_wait - [DEFAULT] wait until the command is done - * @mtd: MTD device structure - * @chip: NAND chip structure - * - * Wait for command done. This applies to erase and program only. - */ -static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) -{ - int status; - unsigned long timeo = 400; - - led_trigger_event(nand_led_trigger, LED_FULL); - - /* - * Apply this short delay always to ensure that we do wait tWB in any - * case on any machine. - */ - ndelay(100); - - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - - u32 timer = (CONFIG_SYS_HZ * timeo) / 1000; - u32 time_start; - - time_start = get_timer(0); - while (get_timer(time_start) < timer) { - if (chip->dev_ready) { - if (chip->dev_ready(mtd)) - break; - } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) - break; - } - } - led_trigger_event(nand_led_trigger, LED_OFF); - - status = (int)chip->read_byte(mtd); - /* This can happen if in case of timeout or buggy dev_ready */ - WARN_ON(!(status & NAND_STATUS_READY)); - return status; -} - -/** - * nand_reset_data_interface - Reset data interface and timings - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Reset the Data interface and timings to ONFI mode 0. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_data_interface *conf; - int ret; - - if (!chip->setup_data_interface) - return 0; - - /* - * The ONFI specification says: - * " - * To transition from NV-DDR or NV-DDR2 to the SDR data - * interface, the host shall use the Reset (FFh) command - * using SDR timing mode 0. A device in any timing mode is - * required to recognize Reset (FFh) command issued in SDR - * timing mode 0. - * " - * - * Configure the data interface in SDR mode and set the - * timings to timing mode 0. - */ - - conf = nand_get_default_data_interface(); - ret = chip->setup_data_interface(mtd, chipnr, conf); - if (ret) - pr_err("Failed to configure data interface to SDR timing mode 0\n"); - - return ret; -} - -/** - * nand_setup_data_interface - Setup the best data interface and timings - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Find and configure the best data interface and NAND timings supported by - * the chip and the driver. - * First tries to retrieve supported timing modes from ONFI information, - * and if the NAND chip does not support ONFI, relies on the - * ->onfi_timing_mode_default specified in the nand_ids table. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - if (!chip->setup_data_interface || !chip->data_interface) - return 0; - - /* - * Ensure the timing mode has been changed on the chip side - * before changing timings on the controller side. - */ - if (chip->onfi_version) { - u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { - chip->onfi_timing_mode_default, - }; - - ret = chip->onfi_set_features(mtd, chip, - ONFI_FEATURE_ADDR_TIMING_MODE, - tmode_param); - if (ret) - goto err; - } - - ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface); -err: - return ret; -} - -/** - * nand_init_data_interface - find the best data interface and timings - * @chip: The NAND chip - * - * Find the best data interface and NAND timings supported by the chip - * and the driver. - * First tries to retrieve supported timing modes from ONFI information, - * and if the NAND chip does not support ONFI, relies on the - * ->onfi_timing_mode_default specified in the nand_ids table. After this - * function nand_chip->data_interface is initialized with the best timing mode - * available. - * - * Returns 0 for success or negative error code otherwise. - */ -static int nand_init_data_interface(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int modes, mode, ret; - - if (!chip->setup_data_interface) - return 0; - - /* - * First try to identify the best timings from ONFI parameters and - * if the NAND does not support ONFI, fallback to the default ONFI - * timing mode. - */ - modes = onfi_get_async_timing_mode(chip); - if (modes == ONFI_TIMING_MODE_UNKNOWN) { - if (!chip->onfi_timing_mode_default) - return 0; - - modes = GENMASK(chip->onfi_timing_mode_default, 0); - } - - chip->data_interface = kzalloc(sizeof(*chip->data_interface), - GFP_KERNEL); - if (!chip->data_interface) - return -ENOMEM; - - for (mode = fls(modes) - 1; mode >= 0; mode--) { - ret = onfi_init_data_interface(chip, chip->data_interface, - NAND_SDR_IFACE, mode); - if (ret) - continue; - - /* Pass -1 to only */ - ret = chip->setup_data_interface(mtd, - NAND_DATA_IFACE_CHECK_ONLY, - chip->data_interface); - if (!ret) { - chip->onfi_timing_mode_default = mode; - break; - } - } - - return 0; -} - -static void __maybe_unused nand_release_data_interface(struct nand_chip *chip) -{ - kfree(chip->data_interface); -} - -/** - * nand_reset - Reset and initialize a NAND device - * @chip: The NAND chip - * @chipnr: Internal die id - * - * Returns 0 for success or negative error code otherwise - */ -int nand_reset(struct nand_chip *chip, int chipnr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - - ret = nand_reset_data_interface(chip, chipnr); - if (ret) - return ret; - - /* - * The CS line has to be released before we can apply the new NAND - * interface settings, hence this weird ->select_chip() dance. - */ - chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->select_chip(mtd, -1); - - chip->select_chip(mtd, chipnr); - ret = nand_setup_data_interface(chip, chipnr); - chip->select_chip(mtd, -1); - if (ret) - return ret; - - return 0; -} - -/** - * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data - * @buf: buffer to test - * @len: buffer length - * @bitflips_threshold: maximum number of bitflips - * - * Check if a buffer contains only 0xff, which means the underlying region - * has been erased and is ready to be programmed. - * The bitflips_threshold specify the maximum number of bitflips before - * considering the region is not erased. - * Note: The logic of this function has been extracted from the memweight - * implementation, except that nand_check_erased_buf function exit before - * testing the whole buffer if the number of bitflips exceed the - * bitflips_threshold value. - * - * Returns a positive number of bitflips less than or equal to - * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the - * threshold. - */ -static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) -{ - const unsigned char *bitmap = buf; - int bitflips = 0; - int weight; - - for (; len && ((uintptr_t)bitmap) % sizeof(long); - len--, bitmap++) { - weight = hweight8(*bitmap); - bitflips += BITS_PER_BYTE - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - for (; len >= 4; len -= 4, bitmap += 4) { - weight = hweight32(*((u32 *)bitmap)); - bitflips += 32 - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - for (; len > 0; len--, bitmap++) { - weight = hweight8(*bitmap); - bitflips += BITS_PER_BYTE - weight; - if (unlikely(bitflips > bitflips_threshold)) - return -EBADMSG; - } - - return bitflips; -} - -/** - * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only - * 0xff data - * @data: data buffer to test - * @datalen: data length - * @ecc: ECC buffer - * @ecclen: ECC length - * @extraoob: extra OOB buffer - * @extraooblen: extra OOB length - * @bitflips_threshold: maximum number of bitflips - * - * Check if a data buffer and its associated ECC and OOB data contains only - * 0xff pattern, which means the underlying region has been erased and is - * ready to be programmed. - * The bitflips_threshold specify the maximum number of bitflips before - * considering the region as not erased. - * - * Note: - * 1/ ECC algorithms are working on pre-defined block sizes which are usually - * different from the NAND page size. When fixing bitflips, ECC engines will - * report the number of errors per chunk, and the NAND core infrastructure - * expect you to return the maximum number of bitflips for the whole page. - * This is why you should always use this function on a single chunk and - * not on the whole page. After checking each chunk you should update your - * max_bitflips value accordingly. - * 2/ When checking for bitflips in erased pages you should not only check - * the payload data but also their associated ECC data, because a user might - * have programmed almost all bits to 1 but a few. In this case, we - * shouldn't consider the chunk as erased, and checking ECC bytes prevent - * this case. - * 3/ The extraoob argument is optional, and should be used if some of your OOB - * data are protected by the ECC engine. - * It could also be used if you support subpages and want to attach some - * extra OOB data to an ECC chunk. - * - * Returns a positive number of bitflips less than or equal to - * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the - * threshold. In case of success, the passed buffers are filled with 0xff. - */ -int nand_check_erased_ecc_chunk(void *data, int datalen, - void *ecc, int ecclen, - void *extraoob, int extraooblen, - int bitflips_threshold) -{ - int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; - - data_bitflips = nand_check_erased_buf(data, datalen, - bitflips_threshold); - if (data_bitflips < 0) - return data_bitflips; - - bitflips_threshold -= data_bitflips; - - ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); - if (ecc_bitflips < 0) - return ecc_bitflips; - - bitflips_threshold -= ecc_bitflips; - - extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, - bitflips_threshold); - if (extraoob_bitflips < 0) - return extraoob_bitflips; - - if (data_bitflips) - memset(data, 0xff, datalen); - - if (ecc_bitflips) - memset(ecc, 0xff, ecclen); - - if (extraoob_bitflips) - memset(extraoob, 0xff, extraooblen); - - return data_bitflips + ecc_bitflips + extraoob_bitflips; -} -EXPORT_SYMBOL(nand_check_erased_ecc_chunk); - -/** - * nand_read_page_raw - [INTERN] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Not for syndrome calculating ECC controllers, which use a special oob layout. - */ -static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - chip->read_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; -} - -/** - * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * We need a special oob layout and handling even when OOB isn't used. - */ -static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - uint8_t *oob = chip->oob_poi; - int steps, size; - - for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); - buf += eccsize; - - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->read_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); - - return 0; -} - -/** - * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - */ -static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - unsigned int max_bitflips = 0; - - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @data_offs: offset of requested data within the page - * @readlen: data length - * @bufpoi: buffer to store read data - * @page: page number to read - */ -static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, - int page) -{ - int start_step, end_step, num_steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint8_t *p; - int data_col_addr, i, gaps = 0; - int datafrag_len, eccfrag_len, aligned_len, aligned_pos; - int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index; - unsigned int max_bitflips = 0; - - /* Column address within the page aligned to ECC size (256bytes) */ - start_step = data_offs / chip->ecc.size; - end_step = (data_offs + readlen - 1) / chip->ecc.size; - num_steps = end_step - start_step + 1; - index = start_step * chip->ecc.bytes; - - /* Data size aligned to ECC ecc.size */ - datafrag_len = num_steps * chip->ecc.size; - eccfrag_len = num_steps * chip->ecc.bytes; - - data_col_addr = start_step * chip->ecc.size; - /* If we read not a page aligned data */ - if (data_col_addr != 0) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); - - p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); - - /* Calculate ECC */ - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); - - /* - * The performance is faster if we position offsets according to - * ecc.pos. Let's make sure that there are no gaps in ECC positions. - */ - for (i = 0; i < eccfrag_len - 1; i++) { - if (eccpos[i + index] + 1 != eccpos[i + index + 1]) { - gaps = 1; - break; - } - } - if (gaps) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - } else { - /* - * Send the command to read the particular ECC bytes take care - * about buswidth alignment in read_buf. - */ - aligned_pos = eccpos[index] & ~(busw - 1); - aligned_len = eccfrag_len; - if (eccpos[index] & (busw - 1)) - aligned_len++; - if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) - aligned_len++; - - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + aligned_pos, -1); - chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); - } - - for (i = 0; i < eccfrag_len; i++) - chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; - - p = bufpoi + data_col_addr; - for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { - int stat; - - stat = chip->ecc.correct(mtd, p, - &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, - &chip->buffers->ecccode[i], - chip->ecc.bytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Not for syndrome calculating ECC controllers which need a special oob layout. - */ -static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - unsigned int max_bitflips = 0; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, eccsize, - &ecc_code[i], eccbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * Hardware ECC for large page chips, require OOB to be read first. For this - * ECC mode, the write_page method is re-used from ECC_HW. These methods - * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with - * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from - * the data area, by overwriting the NAND manufacturer bad block markings. - */ -static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint8_t *ecc_calc = chip->buffers->ecccalc; - unsigned int max_bitflips = 0; - - /* Read the OOB area first */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, eccsize, - &ecc_code[i], eccbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - return max_bitflips; -} - -/** - * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller requires OOB data read to chip->oob_poi - * @page: page number to read - * - * The hw generator calculates the error syndrome automatically. Therefore we - * need a special oob layout and handling. - */ -static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - unsigned int max_bitflips = 0; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); - stat = chip->ecc.correct(mtd, p, oob, NULL); - - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - - if (stat == -EBADMSG && - (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { - /* check for empty pages with bitflips */ - stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, - oob - eccpadbytes, - eccpadbytes, - NULL, 0, - chip->ecc.strength); - } - - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); - - return max_bitflips; -} - -/** - * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @chip: nand chip structure - * @oob: oob destination address - * @ops: oob ops structure - * @len: size of oob to transfer - */ -static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops, size_t len) -{ - switch (ops->mode) { - - case MTD_OPS_PLACE_OOB: - case MTD_OPS_RAW: - memcpy(oob, chip->oob_poi + ops->ooboffs, len); - return oob + len; - - case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, roffs = ops->ooboffs; - size_t bytes = 0; - - for (; free->length && len; free++, len -= bytes) { - /* Read request not from offset 0? */ - if (unlikely(roffs)) { - if (roffs >= free->length) { - roffs -= free->length; - continue; - } - boffs = free->offset + roffs; - bytes = min_t(size_t, len, - (free->length - roffs)); - roffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(oob, chip->oob_poi + boffs, bytes); - oob += bytes; - } - return oob; - } - default: - BUG(); - } - return NULL; -} - -/** - * nand_setup_read_retry - [INTERN] Set the READ RETRY mode - * @mtd: MTD device structure - * @retry_mode: the retry mode to use - * - * Some vendors supply a special command to shift the Vt threshold, to be used - * when there are too many bitflips in a page (i.e., ECC error). After setting - * a new threshold, the host should retry reading the page. - */ -static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("setting READ RETRY mode %d\n", retry_mode); - - if (retry_mode >= chip->read_retries) - return -EINVAL; - - if (!chip->setup_read_retry) - return -EOPNOTSUPP; - - return chip->setup_read_retry(mtd, retry_mode); -} - -/** - * nand_do_read_ops - [INTERN] Read data with ECC - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob ops structure - * - * Internal function. Called with chip held. - */ -static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - int chipnr, page, realpage, col, bytes, aligned, oob_required; - struct nand_chip *chip = mtd_to_nand(mtd); - int ret = 0; - uint32_t readlen = ops->len; - uint32_t oobreadlen = ops->ooblen; - uint32_t max_oobsize = mtd_oobavail(mtd, ops); - - uint8_t *bufpoi, *oob, *buf; - int use_bufpoi; - unsigned int max_bitflips = 0; - int retry_mode = 0; - bool ecc_fail = false; - - chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - realpage = (int)(from >> chip->page_shift); - page = realpage & chip->pagemask; - - col = (int)(from & (mtd->writesize - 1)); - - buf = ops->datbuf; - oob = ops->oobbuf; - oob_required = oob ? 1 : 0; - - while (1) { - unsigned int ecc_failures = mtd->ecc_stats.failed; - - WATCHDOG_RESET(); - bytes = min(mtd->writesize - col, readlen); - aligned = (bytes == mtd->writesize); - - if (!aligned) - use_bufpoi = 1; - else if (chip->options & NAND_USE_BOUNCE_BUFFER) - use_bufpoi = !IS_ALIGNED((unsigned long)buf, - chip->buf_align); - else - use_bufpoi = 0; - - /* Is the current page in the buffer? */ - if (realpage != chip->pagebuf || oob) { - bufpoi = use_bufpoi ? chip->buffers->databuf : buf; - - if (use_bufpoi && aligned) - pr_debug("%s: using read bounce buffer for buf@%p\n", - __func__, buf); - -read_retry: - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - - /* - * Now read the page into the buffer. Absent an error, - * the read methods return max bitflips per ecc step. - */ - if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - oob_required, - page); - else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && - !oob) - ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi, - page); - else - ret = chip->ecc.read_page(mtd, chip, bufpoi, - oob_required, page); - if (ret < 0) { - if (use_bufpoi) - /* Invalidate page cache */ - chip->pagebuf = -1; - break; - } - - max_bitflips = max_t(unsigned int, max_bitflips, ret); - - /* Transfer not aligned data */ - if (use_bufpoi) { - if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && - !(mtd->ecc_stats.failed - ecc_failures) && - (ops->mode != MTD_OPS_RAW)) { - chip->pagebuf = realpage; - chip->pagebuf_bitflips = ret; - } else { - /* Invalidate page cache */ - chip->pagebuf = -1; - } - memcpy(buf, chip->buffers->databuf + col, bytes); - } - - if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); - - if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); - oobreadlen -= toread; - } - } - - if (chip->options & NAND_NEED_READRDY) { - /* Apply delay or wait for ready/busy pin */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } - - if (mtd->ecc_stats.failed - ecc_failures) { - if (retry_mode + 1 < chip->read_retries) { - retry_mode++; - ret = nand_setup_read_retry(mtd, - retry_mode); - if (ret < 0) - break; - - /* Reset failures; retry */ - mtd->ecc_stats.failed = ecc_failures; - goto read_retry; - } else { - /* No more retry modes; real failure */ - ecc_fail = true; - } - } - - buf += bytes; - } else { - memcpy(buf, chip->buffers->databuf + col, bytes); - buf += bytes; - max_bitflips = max_t(unsigned int, max_bitflips, - chip->pagebuf_bitflips); - } - - readlen -= bytes; - - /* Reset to retry mode 0 */ - if (retry_mode) { - ret = nand_setup_read_retry(mtd, 0); - if (ret < 0) - break; - retry_mode = 0; - } - - if (!readlen) - break; - - /* For subsequent reads align to page boundary */ - col = 0; - /* Increment page address */ - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - chip->select_chip(mtd, -1); - - ops->retlen = ops->len - (size_t) readlen; - if (oob) - ops->oobretlen = ops->ooblen - oobreadlen; - - if (ret < 0) - return ret; - - if (ecc_fail) - return -EBADMSG; - - return max_bitflips; -} - -/** - * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; -} - -/** - * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC - * with syndromes - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - */ -static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; - uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos; - - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); - for (i = 0; i < chip->ecc.steps; i++) { - if (sndrnd) { - pos = eccsize + i * (eccsize + chunk); - if (mtd->writesize > 512) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); - else - chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); - } else - sndrnd = 1; - toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); - - return 0; -} - -/** - * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int status = 0; - const uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/** - * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC - * with syndrome - only for large page flash - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -static int nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; - const uint8_t *bufpoi = chip->oob_poi; - - /* - * data-ecc-data-ecc ... ecc-oob - * or - * data-pad-ecc-pad-data-pad .... ecc-pad-oob - */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { - pos = steps * (eccsize + chunk); - steps = 0; - } else - pos = eccsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); - for (i = 0; i < steps; i++) { - if (sndcmd) { - if (mtd->writesize <= 512) { - uint32_t fill = 0xFFFFFFFF; - - len = eccsize; - while (len > 0) { - int num = min_t(int, len, 4); - chip->write_buf(mtd, (uint8_t *)&fill, - num); - len -= num; - } - } else { - pos = eccsize + i * (eccsize + chunk); - chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); - } - } else - sndcmd = 1; - len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); - bufpoi += len; - length -= len; - } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/** - * nand_do_read_oob - [INTERN] NAND read out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operations description structure - * - * NAND read out-of-band data from the spare area. - */ -static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - int page, realpage, chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtd_ecc_stats stats; - int readlen = ops->ooblen; - int len; - uint8_t *buf = ops->oobbuf; - int ret = 0; - - pr_debug("%s: from = 0x%08Lx, len = %i\n", - __func__, (unsigned long long)from, readlen); - - stats = mtd->ecc_stats; - - len = mtd_oobavail(mtd, ops); - - if (unlikely(ops->ooboffs >= len)) { - pr_debug("%s: attempt to start read outside oob\n", - __func__); - return -EINVAL; - } - - /* Do not allow reads past end of device */ - if (unlikely(from >= mtd->size || - ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - - (from >> chip->page_shift)) * len)) { - pr_debug("%s: attempt to read beyond end of device\n", - __func__); - return -EINVAL; - } - - chipnr = (int)(from >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Shift to get page */ - realpage = (int)(from >> chip->page_shift); - page = realpage & chip->pagemask; - - while (1) { - WATCHDOG_RESET(); - - if (ops->mode == MTD_OPS_RAW) - ret = chip->ecc.read_oob_raw(mtd, chip, page); - else - ret = chip->ecc.read_oob(mtd, chip, page); - - if (ret < 0) - break; - - len = min(len, readlen); - buf = nand_transfer_oob(chip, buf, ops, len); - - if (chip->options & NAND_NEED_READRDY) { - /* Apply delay or wait for ready/busy pin */ - if (!chip->dev_ready) - udelay(chip->chip_delay); - else - nand_wait_ready(mtd); - } - - readlen -= len; - if (!readlen) - break; - - /* Increment page address */ - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - chip->select_chip(mtd, -1); - - ops->oobretlen = ops->ooblen - readlen; - - if (ret < 0) - return ret; - - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; -} - -/** - * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operation description structure - * - * NAND read data and/or out-of-band data. - */ -static int nand_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - int ret = -ENOTSUPP; - - ops->retlen = 0; - - /* Do not allow reads past end of device */ - if (ops->datbuf && (from + ops->len) > mtd->size) { - pr_debug("%s: attempt to read beyond end of device\n", - __func__); - return -EINVAL; - } - - nand_get_device(mtd, FL_READING); - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - case MTD_OPS_RAW: - break; - - default: - goto out; - } - - if (!ops->datbuf) - ret = nand_do_read_oob(mtd, from, ops); - else - ret = nand_do_read_ops(mtd, from, ops); - -out: - nand_release_device(mtd); - return ret; -} - - -/** - * nand_write_page_raw - [INTERN] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * Not for syndrome calculating ECC controllers, which use a special oob layout. - */ -static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - chip->write_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -/** - * nand_write_page_raw_syndrome - [INTERN] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * We need a special oob layout and handling even when ECC isn't checked. - */ -static int nand_write_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - uint8_t *oob = chip->oob_poi; - int steps, size; - - for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); - buf += eccsize; - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->write_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); - - return 0; -} -/** - * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; - const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - /* Software ECC calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); -} - -/** - * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; - const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - - -/** - * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @offset: column address of subpage within the page - * @data_len: data length - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - */ -static int nand_write_subpage_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint32_t offset, - uint32_t data_len, const uint8_t *buf, - int oob_required, int page) -{ - uint8_t *oob_buf = chip->oob_poi; - uint8_t *ecc_calc = chip->buffers->ecccalc; - int ecc_size = chip->ecc.size; - int ecc_bytes = chip->ecc.bytes; - int ecc_steps = chip->ecc.steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint32_t start_step = offset / ecc_size; - uint32_t end_step = (offset + data_len - 1) / ecc_size; - int oob_bytes = mtd->oobsize / ecc_steps; - int step, i; - - for (step = 0; step < ecc_steps; step++) { - /* configure controller for WRITE access */ - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); - - /* mask ECC of un-touched subpages by padding 0xFF */ - if ((step < start_step) || (step > end_step)) - memset(ecc_calc, 0xff, ecc_bytes); - else - chip->ecc.calculate(mtd, buf, ecc_calc); - - /* mask OOB of un-touched subpages by padding 0xFF */ - /* if oob_required, preserve OOB metadata of written subpage */ - if (!oob_required || (step < start_step) || (step > end_step)) - memset(oob_buf, 0xff, oob_bytes); - - buf += ecc_size; - ecc_calc += ecc_bytes; - oob_buf += oob_bytes; - } - - /* copy calculated ECC for whole page to chip->buffer->oob */ - /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->buffers->ecccalc; - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - - -/** - * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * - * The hw generator calculates the error syndrome automatically. Therefore we - * need a special oob layout and handling. - */ -static int nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); - - return 0; -} - -/** - * nand_write_page - [REPLACEABLE] write one page - * @mtd: MTD device structure - * @chip: NAND chip descriptor - * @offset: address offset within the page - * @data_len: length of actual data to be written - * @buf: the data to write - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to write - * @raw: use _raw version of write_page - */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offset, int data_len, const uint8_t *buf, - int oob_required, int page, int raw) -{ - int status, subpage; - - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - chip->ecc.write_subpage) - subpage = offset || (data_len < mtd->writesize); - else - subpage = 0; - - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - status = chip->ecc.write_page_raw(mtd, chip, buf, - oob_required, page); - else if (subpage) - status = chip->ecc.write_subpage(mtd, chip, offset, data_len, - buf, oob_required, page); - else - status = chip->ecc.write_page(mtd, chip, buf, oob_required, - page); - - if (status < 0) - return status; - - if (nand_standard_page_accessors(&chip->ecc)) { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - } - - return 0; -} - -/** - * nand_fill_oob - [INTERN] Transfer client buffer to oob - * @mtd: MTD device structure - * @oob: oob data buffer - * @len: oob data write length - * @ops: oob ops structure - */ -static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - /* - * Initialise to all 0xFF, to avoid the possibility of left over OOB - * data from a previous OOB read. - */ - memset(chip->oob_poi, 0xff, mtd->oobsize); - - switch (ops->mode) { - - case MTD_OPS_PLACE_OOB: - case MTD_OPS_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; - - for (; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0? */ - if (unlikely(woffs)) { - if (woffs >= free->length) { - woffs -= free->length; - continue; - } - boffs = free->offset + woffs; - bytes = min_t(size_t, len, - (free->length - woffs)); - woffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(chip->oob_poi + boffs, oob, bytes); - oob += bytes; - } - return oob; - } - default: - BUG(); - } - return NULL; -} - -#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) - -/** - * nand_do_write_ops - [INTERN] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operations description structure - * - * NAND write with ECC. - */ -static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, realpage, page, column; - struct nand_chip *chip = mtd_to_nand(mtd); - uint32_t writelen = ops->len; - - uint32_t oobwritelen = ops->ooblen; - uint32_t oobmaxlen = mtd_oobavail(mtd, ops); - - uint8_t *oob = ops->oobbuf; - uint8_t *buf = ops->datbuf; - int ret; - int oob_required = oob ? 1 : 0; - - ops->retlen = 0; - if (!writelen) - return 0; - - /* Reject writes, which are not page aligned */ - if (NOTALIGNED(to)) { - pr_notice("%s: attempt to write non page aligned data\n", - __func__); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - ret = -EIO; - goto err_out; - } - - realpage = (int)(to >> chip->page_shift); - page = realpage & chip->pagemask; - - /* Invalidate the page cache, when we write to the cached page */ - if (to <= ((loff_t)chip->pagebuf << chip->page_shift) && - ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) - chip->pagebuf = -1; - - /* Don't allow multipage oob writes with offset */ - if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { - ret = -EINVAL; - goto err_out; - } - - while (1) { - int bytes = mtd->writesize; - uint8_t *wbuf = buf; - int use_bufpoi; - int part_pagewr = (column || writelen < mtd->writesize); - - if (part_pagewr) - use_bufpoi = 1; - else if (chip->options & NAND_USE_BOUNCE_BUFFER) - use_bufpoi = !IS_ALIGNED((unsigned long)buf, - chip->buf_align); - else - use_bufpoi = 0; - - WATCHDOG_RESET(); - /* Partial page write?, or need to use bounce buffer */ - if (use_bufpoi) { - pr_debug("%s: using write bounce buffer for buf@%p\n", - __func__, buf); - if (part_pagewr) - bytes = min_t(int, bytes - column, writelen); - chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - memcpy(&chip->buffers->databuf[column], buf, bytes); - wbuf = chip->buffers->databuf; - } - - if (unlikely(oob)) { - size_t len = min(oobwritelen, oobmaxlen); - oob = nand_fill_oob(mtd, oob, len, ops); - oobwritelen -= len; - } else { - /* We still need to erase leftover OOB data */ - memset(chip->oob_poi, 0xff, mtd->oobsize); - } - ret = chip->write_page(mtd, chip, column, bytes, wbuf, - oob_required, page, - (ops->mode == MTD_OPS_RAW)); - if (ret) - break; - - writelen -= bytes; - if (!writelen) - break; - - column = 0; - buf += bytes; - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - - ops->retlen = ops->len - writelen; - if (unlikely(oob)) - ops->oobretlen = ops->ooblen; - -err_out: - chip->select_chip(mtd, -1); - return ret; -} - -/** - * panic_nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * NAND write with ECC. Used when performing writes in interrupt context, this - * may for example be called by mtdoops when writing an oops while in panic. - */ -static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct mtd_oob_ops ops; - int ret; - - /* Wait for the device to get ready */ - panic_nand_wait(mtd, chip, 400); - - /* Grab the device */ - panic_nand_get_device(chip, mtd, FL_WRITING); - - memset(&ops, 0, sizeof(ops)); - ops.len = len; - ops.datbuf = (uint8_t *)buf; - ops.mode = MTD_OPS_PLACE_OOB; - - ret = nand_do_write_ops(mtd, to, &ops); - - *retlen = ops.retlen; - return ret; -} - -/** - * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - * - * NAND write out-of-band. - */ -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, page, status, len; - struct nand_chip *chip = mtd_to_nand(mtd); - - pr_debug("%s: to = 0x%08x, len = %i\n", - __func__, (unsigned int)to, (int)ops->ooblen); - - len = mtd_oobavail(mtd, ops); - - /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > len) { - pr_debug("%s: attempt to write past end of page\n", - __func__); - return -EINVAL; - } - - if (unlikely(ops->ooboffs >= len)) { - pr_debug("%s: attempt to start write outside oob\n", - __func__); - return -EINVAL; - } - - /* Do not allow write past end of device */ - if (unlikely(to >= mtd->size || - ops->ooboffs + ops->ooblen > - ((mtd->size >> chip->page_shift) - - (to >> chip->page_shift)) * len)) { - pr_debug("%s: attempt to write beyond end of device\n", - __func__); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - - /* - * Reset the chip. Some chips (like the Toshiba TC5832DC found in one - * of my DiskOnChip 2000 test units) will clear the whole data page too - * if we don't do this. I have no clue why, but I seem to have 'fixed' - * it in the doc2000 driver in August 1999. dwmw2. - */ - nand_reset(chip, chipnr); - - chip->select_chip(mtd, chipnr); - - /* Shift to get page */ - page = (int)(to >> chip->page_shift); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - chip->select_chip(mtd, -1); - return -EROFS; - } - - /* Invalidate the page cache, if we write to the cached page */ - if (page == chip->pagebuf) - chip->pagebuf = -1; - - nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); - - if (ops->mode == MTD_OPS_RAW) - status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); - else - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); - - chip->select_chip(mtd, -1); - - if (status) - return status; - - ops->oobretlen = ops->ooblen; - - return 0; -} - -/** - * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int ret = -ENOTSUPP; - - ops->retlen = 0; - - /* Do not allow writes past end of device */ - if (ops->datbuf && (to + ops->len) > mtd->size) { - pr_debug("%s: attempt to write beyond end of device\n", - __func__); - return -EINVAL; - } - - nand_get_device(mtd, FL_WRITING); - - switch (ops->mode) { - case MTD_OPS_PLACE_OOB: - case MTD_OPS_AUTO_OOB: - case MTD_OPS_RAW: - break; - - default: - goto out; - } - - if (!ops->datbuf) - ret = nand_do_write_oob(mtd, to, ops); - else - ret = nand_do_write_ops(mtd, to, ops); - -out: - nand_release_device(mtd); - return ret; -} - -/** - * single_erase - [GENERIC] NAND standard block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * Standard erase command for NAND chips. Returns NAND status. - */ -static int single_erase(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); - - return chip->waitfunc(mtd, chip); -} - -/** - * nand_erase - [MTD Interface] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * - * Erase one ore more blocks. - */ -static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - return nand_erase_nand(mtd, instr, 0); -} - -/** - * nand_erase_nand - [INTERN] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * @allowbbt: allow erasing the bbt area - * - * Erase one ore more blocks. - */ -int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, - int allowbbt) -{ - int page, status, pages_per_block, ret, chipnr; - struct nand_chip *chip = mtd_to_nand(mtd); - loff_t len; - - pr_debug("%s: start = 0x%012llx, len = %llu\n", - __func__, (unsigned long long)instr->addr, - (unsigned long long)instr->len); - - if (check_offs_len(mtd, instr->addr, instr->len)) - return -EINVAL; - - /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_ERASING); - - /* Shift to get first page */ - page = (int)(instr->addr >> chip->page_shift); - chipnr = (int)(instr->addr >> chip->chip_shift); - - /* Calculate pages in each block */ - pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); - - /* Select the NAND device */ - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - pr_debug("%s: device is write protected!\n", - __func__); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* Loop through the pages */ - len = instr->len; - - instr->state = MTD_ERASING; - - while (len) { - WATCHDOG_RESET(); - - /* Check if we have a bad block, we do not erase bad blocks! */ - if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << - chip->page_shift, allowbbt)) { - pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", - __func__, page); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* - * Invalidate the page cache, if we erase the block which - * contains the current cached page. - */ - if (page <= chip->pagebuf && chip->pagebuf < - (page + pages_per_block)) - chip->pagebuf = -1; - - status = chip->erase(mtd, page & chip->pagemask); - - /* See if block erase succeeded */ - if (status & NAND_STATUS_FAIL) { - pr_debug("%s: failed erase, page 0x%08x\n", - __func__, page); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = - ((loff_t)page << chip->page_shift); - goto erase_exit; - } - - /* Increment page address and decrement length */ - len -= (1ULL << chip->phys_erase_shift); - page += pages_per_block; - - /* Check, if we cross a chip boundary */ - if (len && !(page & chip->pagemask)) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - instr->state = MTD_ERASE_DONE; - -erase_exit: - - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - - /* Deselect and wake up anyone waiting on the device */ - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - - /* Return more or less happy */ - return ret; -} - -/** - * nand_sync - [MTD Interface] sync - * @mtd: MTD device structure - * - * Sync is actually a wait for chip ready function. - */ -static void nand_sync(struct mtd_info *mtd) -{ - pr_debug("%s: called\n", __func__); - - /* Grab the lock and see if the device is available */ - nand_get_device(mtd, FL_SYNCING); - /* Release it and go back */ - nand_release_device(mtd); -} - -/** - * nand_block_isbad - [MTD Interface] Check if block at offset is bad - * @mtd: MTD device structure - * @offs: offset relative to mtd start - */ -static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int chipnr = (int)(offs >> chip->chip_shift); - int ret; - - /* Select the NAND device */ - nand_get_device(mtd, FL_READING); - chip->select_chip(mtd, chipnr); - - ret = nand_block_checkbad(mtd, offs, 0); - - chip->select_chip(mtd, -1); - nand_release_device(mtd); - - return ret; -} - -/** - * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd: MTD device structure - * @ofs: offset relative to mtd start - */ -static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - int ret; - - ret = nand_block_isbad(mtd, ofs); - if (ret) { - /* If it was bad already, return success and do nothing */ - if (ret > 0) - return 0; - return ret; - } - - return nand_block_markbad_lowlevel(mtd, ofs); -} - -/** - * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand - * @mtd: MTD device structure - * @chip: nand chip info structure - * @addr: feature address. - * @subfeature_param: the subfeature parameters, a four bytes array. - */ -static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) -{ - int status; - int i; - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -ENOTSUPP; -#endif - - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, subfeature_param[i]); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - return 0; -} - -/** - * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand - * @mtd: MTD device structure - * @chip: nand chip info structure - * @addr: feature address. - * @subfeature_param: the subfeature parameters, a four bytes array. - */ -static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, - int addr, uint8_t *subfeature_param) -{ - int i; - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION - if (!chip->onfi_version || - !(le16_to_cpu(chip->onfi_params.opt_cmd) - & ONFI_OPT_CMD_SET_GET_FEATURES)) - return -ENOTSUPP; -#endif - - chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - *subfeature_param++ = chip->read_byte(mtd); - return 0; -} - -/* Set default functions */ -static void nand_set_defaults(struct nand_chip *chip, int busw) -{ - /* check for proper chip_delay setup, set 20us if not */ - if (!chip->chip_delay) - chip->chip_delay = 20; - - /* check, if a user supplied command function given */ - if (chip->cmdfunc == NULL) - chip->cmdfunc = nand_command; - - /* check, if a user supplied wait function given */ - if (chip->waitfunc == NULL) - chip->waitfunc = nand_wait; - - if (!chip->select_chip) - chip->select_chip = nand_select_chip; - - /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; - - /* If called twice, pointers that depend on busw may need to be reset */ - if (!chip->read_byte || chip->read_byte == nand_read_byte) - chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; - if (!chip->read_word) - chip->read_word = nand_read_word; - if (!chip->block_bad) - chip->block_bad = nand_block_bad; - if (!chip->block_markbad) - chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf || chip->write_buf == nand_write_buf) - chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->write_byte || chip->write_byte == nand_write_byte) - chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; - if (!chip->read_buf || chip->read_buf == nand_read_buf) - chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->scan_bbt) - chip->scan_bbt = nand_default_bbt; - - if (!chip->controller) { - chip->controller = &chip->hwcontrol; - spin_lock_init(&chip->controller->lock); - init_waitqueue_head(&chip->controller->wq); - } - - if (!chip->buf_align) - chip->buf_align = 1; -} - -/* Sanitize ONFI strings so we can safely print them */ -static void sanitize_string(char *s, size_t len) -{ - ssize_t i; - - /* Null terminate */ - s[len - 1] = 0; - - /* Remove non printable chars */ - for (i = 0; i < len - 1; i++) { - if (s[i] < ' ' || s[i] > 127) - s[i] = '?'; - } - - /* Remove trailing spaces */ - strim(s); -} - -static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) -{ - int i; - while (len--) { - crc ^= *p++ << 8; - for (i = 0; i < 8; i++) - crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); - } - - return crc; -} - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION -/* Parse the Extended Parameter Page. */ -static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, - struct nand_chip *chip, struct nand_onfi_params *p) -{ - struct onfi_ext_param_page *ep; - struct onfi_ext_section *s; - struct onfi_ext_ecc_info *ecc; - uint8_t *cursor; - int ret = -EINVAL; - int len; - int i; - - len = le16_to_cpu(p->ext_param_page_length) * 16; - ep = kmalloc(len, GFP_KERNEL); - if (!ep) - return -ENOMEM; - - /* Send our own NAND_CMD_PARAM. */ - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); - - /* Use the Change Read Column command to skip the ONFI param pages. */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - sizeof(*p) * p->num_of_param_pages , -1); - - /* Read out the Extended Parameter Page. */ - chip->read_buf(mtd, (uint8_t *)ep, len); - if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) - != le16_to_cpu(ep->crc))) { - pr_debug("fail in the CRC.\n"); - goto ext_out; - } - - /* - * Check the signature. - * Do not strictly follow the ONFI spec, maybe changed in future. - */ - if (strncmp((char *)ep->sig, "EPPS", 4)) { - pr_debug("The signature is invalid.\n"); - goto ext_out; - } - - /* find the ECC section. */ - cursor = (uint8_t *)(ep + 1); - for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { - s = ep->sections + i; - if (s->type == ONFI_SECTION_TYPE_2) - break; - cursor += s->length * 16; - } - if (i == ONFI_EXT_SECTION_MAX) { - pr_debug("We can not find the ECC section.\n"); - goto ext_out; - } - - /* get the info we want. */ - ecc = (struct onfi_ext_ecc_info *)cursor; - - if (!ecc->codeword_size) { - pr_debug("Invalid codeword size\n"); - goto ext_out; - } - - chip->ecc_strength_ds = ecc->ecc_bits; - chip->ecc_step_ds = 1 << ecc->codeword_size; - ret = 0; - -ext_out: - kfree(ep); - return ret; -} - -static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; - - return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, - feature); -} - -/* - * Configure chip properties from Micron vendor-specific ONFI table - */ -static void nand_onfi_detect_micron(struct nand_chip *chip, - struct nand_onfi_params *p) -{ - struct nand_onfi_vendor_micron *micron = (void *)p->vendor; - - if (le16_to_cpu(p->vendor_revision) < 1) - return; - - chip->read_retries = micron->read_retry_options; - chip->setup_read_retry = nand_setup_read_retry_micron; -} - -/* - * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. - */ -static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, - int *busw) -{ - struct nand_onfi_params *p = &chip->onfi_params; - int i, j; - int val; - - /* Try ONFI for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); - if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || - chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') - return 0; - - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); - for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == - le16_to_cpu(p->crc)) { - break; - } - } - - if (i == 3) { - pr_err("Could not find valid ONFI parameter page; aborting\n"); - return 0; - } - - /* Check version */ - val = le16_to_cpu(p->revision); - if (val & (1 << 5)) - chip->onfi_version = 23; - else if (val & (1 << 4)) - chip->onfi_version = 22; - else if (val & (1 << 3)) - chip->onfi_version = 21; - else if (val & (1 << 2)) - chip->onfi_version = 20; - else if (val & (1 << 1)) - chip->onfi_version = 10; - - if (!chip->onfi_version) { - pr_info("unsupported ONFI version: %d\n", val); - return 0; - } - - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; - - mtd->writesize = le32_to_cpu(p->byte_per_page); - - /* - * pages_per_block and blocks_per_lun may not be a power-of-2 size - * (don't ask me who thought of this...). MTD assumes that these - * dimensions will be power-of-2, so just truncate the remaining area. - */ - mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); - mtd->erasesize *= mtd->writesize; - - mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - - /* See erasesize comment */ - chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); - chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - chip->bits_per_cell = p->bits_per_cell; - - if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) - *busw = NAND_BUSWIDTH_16; - else - *busw = 0; - - if (p->ecc_bits != 0xff) { - chip->ecc_strength_ds = p->ecc_bits; - chip->ecc_step_ds = 512; - } else if (chip->onfi_version >= 21 && - (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { - - /* - * The nand_flash_detect_ext_param_page() uses the - * Change Read Column command which maybe not supported - * by the chip->cmdfunc. So try to update the chip->cmdfunc - * now. We do not replace user supplied command function. - */ - if (mtd->writesize > 512 && chip->cmdfunc == nand_command) - chip->cmdfunc = nand_command_lp; - - /* The Extended Parameter Page is supported since ONFI 2.1. */ - if (nand_flash_detect_ext_param_page(mtd, chip, p)) - pr_warn("Failed to detect ONFI extended param page\n"); - } else { - pr_warn("Could not retrieve ONFI ECC requirements\n"); - } - - if (p->jedec_id == NAND_MFR_MICRON) - nand_onfi_detect_micron(chip, p); - - return 1; -} -#else -static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, - int *busw) -{ - return 0; -} -#endif - -/* - * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. - */ -static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, - int *busw) -{ - struct nand_jedec_params *p = &chip->jedec_params; - struct jedec_ecc_info *ecc; - int val; - int i, j; - - /* Try JEDEC for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); - if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'C') - return 0; - - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); - for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); - - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == - le16_to_cpu(p->crc)) - break; - } - - if (i == 3) { - pr_err("Could not find valid JEDEC parameter page; aborting\n"); - return 0; - } - - /* Check version */ - val = le16_to_cpu(p->revision); - if (val & (1 << 2)) - chip->jedec_version = 10; - else if (val & (1 << 1)) - chip->jedec_version = 1; /* vendor specific version */ - - if (!chip->jedec_version) { - pr_info("unsupported JEDEC version: %d\n", val); - return 0; - } - - sanitize_string(p->manufacturer, sizeof(p->manufacturer)); - sanitize_string(p->model, sizeof(p->model)); - if (!mtd->name) - mtd->name = p->model; - - mtd->writesize = le32_to_cpu(p->byte_per_page); - - /* Please reference to the comment for nand_flash_detect_onfi. */ - mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); - mtd->erasesize *= mtd->writesize; - - mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - - /* Please reference to the comment for nand_flash_detect_onfi. */ - chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); - chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - chip->bits_per_cell = p->bits_per_cell; - - if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) - *busw = NAND_BUSWIDTH_16; - else - *busw = 0; - - /* ECC info */ - ecc = &p->ecc_info[0]; - - if (ecc->codeword_size >= 9) { - chip->ecc_strength_ds = ecc->ecc_bits; - chip->ecc_step_ds = 1 << ecc->codeword_size; - } else { - pr_warn("Invalid codeword size\n"); - } - - return 1; -} - -/* - * nand_id_has_period - Check if an ID string has a given wraparound period - * @id_data: the ID string - * @arrlen: the length of the @id_data array - * @period: the period of repitition - * - * Check if an ID string is repeated within a given sequence of bytes at - * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a - * period of 3). This is a helper function for nand_id_len(). Returns non-zero - * if the repetition has a period of @period; otherwise, returns zero. - */ -static int nand_id_has_period(u8 *id_data, int arrlen, int period) -{ - int i, j; - for (i = 0; i < period; i++) - for (j = i + period; j < arrlen; j += period) - if (id_data[i] != id_data[j]) - return 0; - return 1; -} - -/* - * nand_id_len - Get the length of an ID string returned by CMD_READID - * @id_data: the ID string - * @arrlen: the length of the @id_data array - - * Returns the length of the ID string, according to known wraparound/trailing - * zero patterns. If no pattern exists, returns the length of the array. - */ -static int nand_id_len(u8 *id_data, int arrlen) -{ - int last_nonzero, period; - - /* Find last non-zero byte */ - for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) - if (id_data[last_nonzero]) - break; - - /* All zeros */ - if (last_nonzero < 0) - return 0; - - /* Calculate wraparound period */ - for (period = 1; period < arrlen; period++) - if (nand_id_has_period(id_data, arrlen, period)) - break; - - /* There's a repeated pattern */ - if (period < arrlen) - return period; - - /* There are trailing zeros */ - if (last_nonzero < arrlen - 1) - return last_nonzero + 1; - - /* No pattern detected */ - return arrlen; -} - -/* Extract the bits of per cell from the 3rd byte of the extended ID */ -static int nand_get_bits_per_cell(u8 cellinfo) -{ - int bits; - - bits = cellinfo & NAND_CI_CELLTYPE_MSK; - bits >>= NAND_CI_CELLTYPE_SHIFT; - return bits + 1; -} - -/* - * Many new NAND share similar device ID codes, which represent the size of the - * chip. The rest of the parameters must be decoded according to generic or - * manufacturer-specific "extended ID" decoding patterns. - */ -static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, - u8 id_data[8], int *busw) -{ - int extid, id_len; - /* The 3rd id byte holds MLC / multichip data */ - chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); - /* The 4th id byte is the important one */ - extid = id_data[3]; - - id_len = nand_id_len(id_data, 8); - - /* - * Field definitions are in the following datasheets: - * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) - * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) - * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22) - * - * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung - * ID to decide what to do. - */ - if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && - !nand_is_slc(chip) && id_data[5] != 0x00) { - /* Calc pagesize */ - mtd->writesize = 2048 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - switch (((extid >> 2) & 0x04) | (extid & 0x03)) { - case 1: - mtd->oobsize = 128; - break; - case 2: - mtd->oobsize = 218; - break; - case 3: - mtd->oobsize = 400; - break; - case 4: - mtd->oobsize = 436; - break; - case 5: - mtd->oobsize = 512; - break; - case 6: - mtd->oobsize = 640; - break; - case 7: - default: /* Other cases are "reserved" (unknown) */ - mtd->oobsize = 1024; - break; - } - extid >>= 2; - /* Calc blocksize */ - mtd->erasesize = (128 * 1024) << - (((extid >> 1) & 0x04) | (extid & 0x03)); - *busw = 0; - } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && - !nand_is_slc(chip)) { - unsigned int tmp; - - /* Calc pagesize */ - mtd->writesize = 2048 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - switch (((extid >> 2) & 0x04) | (extid & 0x03)) { - case 0: - mtd->oobsize = 128; - break; - case 1: - mtd->oobsize = 224; - break; - case 2: - mtd->oobsize = 448; - break; - case 3: - mtd->oobsize = 64; - break; - case 4: - mtd->oobsize = 32; - break; - case 5: - mtd->oobsize = 16; - break; - default: - mtd->oobsize = 640; - break; - } - extid >>= 2; - /* Calc blocksize */ - tmp = ((extid >> 1) & 0x04) | (extid & 0x03); - if (tmp < 0x03) - mtd->erasesize = (128 * 1024) << tmp; - else if (tmp == 0x03) - mtd->erasesize = 768 * 1024; - else - mtd->erasesize = (64 * 1024) << tmp; - *busw = 0; - } else { - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x03); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * - (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; - - /* - * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per - * 512B page. For Toshiba SLC, we decode the 5th/6th byte as - * follows: - * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, - * 110b -> 24nm - * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC - */ - if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && - nand_is_slc(chip) && - (id_data[5] & 0x7) == 0x6 /* 24nm */ && - !(id_data[4] & 0x80) /* !BENAND */) { - mtd->oobsize = 32 * mtd->writesize >> 9; - } - - } -} - -/* - * Old devices have chip data hardcoded in the device ID table. nand_decode_id - * decodes a matching ID table entry and assigns the MTD size parameters for - * the chip. - */ -static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, - struct nand_flash_dev *type, u8 id_data[8], - int *busw) -{ - int maf_id = id_data[0]; - - mtd->erasesize = type->erasesize; - mtd->writesize = type->pagesize; - mtd->oobsize = mtd->writesize / 32; - *busw = type->options & NAND_BUSWIDTH_16; - - /* All legacy ID NAND are small-page, SLC */ - chip->bits_per_cell = 1; - - /* - * Check for Spansion/AMD ID + repeating 5th, 6th byte since - * some Spansion chips have erasesize that conflicts with size - * listed in nand_ids table. - * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) - */ - if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 - && id_data[6] == 0x00 && id_data[7] == 0x00 - && mtd->writesize == 512) { - mtd->erasesize = 128 * 1024; - mtd->erasesize <<= ((id_data[3] & 0x03) << 1); - } -} - -/* - * Set the bad block marker/indicator (BBM/BBI) patterns according to some - * heuristic patterns using various detected parameters (e.g., manufacturer, - * page size, cell-type information). - */ -static void nand_decode_bbm_options(struct mtd_info *mtd, - struct nand_chip *chip, u8 id_data[8]) -{ - int maf_id = id_data[0]; - - /* Set the bad block position */ - if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) - chip->badblockpos = NAND_LARGE_BADBLOCK_POS; - else - chip->badblockpos = NAND_SMALL_BADBLOCK_POS; - - /* - * Bad block marker is stored in the last page of each block on Samsung - * and Hynix MLC devices; stored in first two pages of each block on - * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, - * AMD/Spansion, and Macronix. All others scan only the first page. - */ - if (!nand_is_slc(chip) && - (maf_id == NAND_MFR_SAMSUNG || - maf_id == NAND_MFR_HYNIX)) - chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else if ((nand_is_slc(chip) && - (maf_id == NAND_MFR_SAMSUNG || - maf_id == NAND_MFR_HYNIX || - maf_id == NAND_MFR_TOSHIBA || - maf_id == NAND_MFR_AMD || - maf_id == NAND_MFR_MACRONIX)) || - (mtd->writesize == 2048 && - maf_id == NAND_MFR_MICRON)) - chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; -} - -static inline bool is_full_id_nand(struct nand_flash_dev *type) -{ - return type->id_len; -} - -static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, - struct nand_flash_dev *type, u8 *id_data, int *busw) -{ - if (!strncmp((char *)type->id, (char *)id_data, type->id_len)) { - mtd->writesize = type->pagesize; - mtd->erasesize = type->erasesize; - mtd->oobsize = type->oobsize; - - chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); - chip->chipsize = (uint64_t)type->chipsize << 20; - chip->options |= type->options; - chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); - chip->ecc_step_ds = NAND_ECC_STEP(type); - chip->onfi_timing_mode_default = - type->onfi_timing_mode_default; - - *busw = type->options & NAND_BUSWIDTH_16; - - if (!mtd->name) - mtd->name = type->name; - - return true; - } - return false; -} - -/* - * Get the flash and manufacturer id and lookup if the type is supported. - */ -struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, - struct nand_chip *chip, - int *maf_id, int *dev_id, - struct nand_flash_dev *type) -{ - int busw; - int i, maf_idx; - u8 id_data[8]; - - /* - * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) - * after power-up. - */ - nand_reset(chip, 0); - - /* Select the device */ - chip->select_chip(mtd, 0); - - /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - /* Read manufacturer and device IDs */ - *maf_id = chip->read_byte(mtd); - *dev_id = chip->read_byte(mtd); - - /* - * Try again to make sure, as some systems the bus-hold or other - * interface concerns can cause random data which looks like a - * possibly credible NAND flash to appear. If the two results do - * not match, ignore the device completely. - */ - - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - /* Read entire ID string */ - for (i = 0; i < 8; i++) - id_data[i] = chip->read_byte(mtd); - - if (id_data[0] != *maf_id || id_data[1] != *dev_id) { - pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", - *maf_id, *dev_id, id_data[0], id_data[1]); - return ERR_PTR(-ENODEV); - } - - if (!type) - type = nand_flash_ids; - - for (; type->name != NULL; type++) { - if (is_full_id_nand(type)) { - if (find_full_id_nand(mtd, chip, type, id_data, &busw)) - goto ident_done; - } else if (*dev_id == type->dev_id) { - break; - } - } - - chip->onfi_version = 0; - if (!type->name || !type->pagesize) { - /* Check if the chip is ONFI compliant */ - if (nand_flash_detect_onfi(mtd, chip, &busw)) - goto ident_done; - - /* Check if the chip is JEDEC compliant */ - if (nand_flash_detect_jedec(mtd, chip, &busw)) - goto ident_done; - } - - if (!type->name) - return ERR_PTR(-ENODEV); - - if (!mtd->name) - mtd->name = type->name; - - chip->chipsize = (uint64_t)type->chipsize << 20; - - if (!type->pagesize) { - /* Decode parameters from extended ID */ - nand_decode_ext_id(mtd, chip, id_data, &busw); - } else { - nand_decode_id(mtd, chip, type, id_data, &busw); - } - /* Get chip options */ - chip->options |= type->options; - - /* - * Check if chip is not a Samsung device. Do not clear the - * options for chips which do not have an extended id. - */ - if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) - chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; -ident_done: - - /* Try to identify manufacturer */ - for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { - if (nand_manuf_ids[maf_idx].id == *maf_id) - break; - } - - if (chip->options & NAND_BUSWIDTH_AUTO) { - WARN_ON(chip->options & NAND_BUSWIDTH_16); - chip->options |= busw; - nand_set_defaults(chip, busw); - } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { - /* - * Check, if buswidth is correct. Hardware drivers should set - * chip correct! - */ - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); - pr_warn("bus width %d instead %d bit\n", - (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, - busw ? 16 : 8); - return ERR_PTR(-EINVAL); - } - - nand_decode_bbm_options(mtd, chip, id_data); - - /* Calculate the address shift from the page size */ - chip->page_shift = ffs(mtd->writesize) - 1; - /* Convert chipsize to number of pages per chip -1 */ - chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; - - chip->bbt_erase_shift = chip->phys_erase_shift = - ffs(mtd->erasesize) - 1; - if (chip->chipsize & 0xffffffff) - chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; - else { - chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); - chip->chip_shift += 32 - 1; - } - - if (chip->chip_shift - chip->page_shift > 16) - chip->options |= NAND_ROW_ADDR_3; - - chip->badblockbits = 8; - chip->erase = single_erase; - - /* Do not replace user supplied command function! */ - if (mtd->writesize > 512 && chip->cmdfunc == nand_command) - chip->cmdfunc = nand_command_lp; - - pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - *maf_id, *dev_id); - -#ifdef CONFIG_SYS_NAND_ONFI_DETECTION - if (chip->onfi_version) - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - chip->onfi_params.model); - else if (chip->jedec_version) - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - chip->jedec_params.model); - else - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - type->name); -#else - if (chip->jedec_version) - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - chip->jedec_params.model); - else - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - type->name); - - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - type->name); -#endif - - pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", - (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", - mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); - return type; -} -EXPORT_SYMBOL(nand_get_flash_type); - -#if CONFIG_IS_ENABLED(OF_CONTROL) -DECLARE_GLOBAL_DATA_PTR; - -static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node) -{ - int ret, ecc_mode = -1, ecc_strength, ecc_step; - const void *blob = gd->fdt_blob; - const char *str; - - ret = fdtdec_get_int(blob, node, "nand-bus-width", -1); - if (ret == 16) - chip->options |= NAND_BUSWIDTH_16; - - if (fdtdec_get_bool(blob, node, "nand-on-flash-bbt")) - chip->bbt_options |= NAND_BBT_USE_FLASH; - - str = fdt_getprop(blob, node, "nand-ecc-mode", NULL); - if (str) { - if (!strcmp(str, "none")) - ecc_mode = NAND_ECC_NONE; - else if (!strcmp(str, "soft")) - ecc_mode = NAND_ECC_SOFT; - else if (!strcmp(str, "hw")) - ecc_mode = NAND_ECC_HW; - else if (!strcmp(str, "hw_syndrome")) - ecc_mode = NAND_ECC_HW_SYNDROME; - else if (!strcmp(str, "hw_oob_first")) - ecc_mode = NAND_ECC_HW_OOB_FIRST; - else if (!strcmp(str, "soft_bch")) - ecc_mode = NAND_ECC_SOFT_BCH; - } - - - ecc_strength = fdtdec_get_int(blob, node, "nand-ecc-strength", -1); - ecc_step = fdtdec_get_int(blob, node, "nand-ecc-step-size", -1); - - if ((ecc_step >= 0 && !(ecc_strength >= 0)) || - (!(ecc_step >= 0) && ecc_strength >= 0)) { - pr_err("must set both strength and step size in DT\n"); - return -EINVAL; - } - - if (ecc_mode >= 0) - chip->ecc.mode = ecc_mode; - - if (ecc_strength >= 0) - chip->ecc.strength = ecc_strength; - - if (ecc_step > 0) - chip->ecc.size = ecc_step; - - if (fdt_getprop(blob, node, "nand-ecc-maximize", NULL)) - chip->ecc.options |= NAND_ECC_MAXIMIZE; - - return 0; -} -#else -static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node) -{ - return 0; -} -#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ - -/** - * nand_scan_ident - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: number of chips to scan for - * @table: alternative NAND ID table - * - * This is the first phase of the normal nand_scan() function. It reads the - * flash ID and sets up MTD fields accordingly. - * - */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips, - struct nand_flash_dev *table) -{ - int i, nand_maf_id, nand_dev_id; - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_flash_dev *type; - int ret; - - if (chip->flash_node) { - ret = nand_dt_init(mtd, chip, chip->flash_node); - if (ret) - return ret; - } - - /* Set the default functions */ - nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); - - /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, &nand_maf_id, - &nand_dev_id, table); - - if (IS_ERR(type)) { - if (!(chip->options & NAND_SCAN_SILENT_NODEV)) - pr_warn("No NAND device found\n"); - chip->select_chip(mtd, -1); - return PTR_ERR(type); - } - - /* Initialize the ->data_interface field. */ - ret = nand_init_data_interface(chip); - if (ret) - return ret; - - /* - * Setup the data interface correctly on the chip and controller side. - * This explicit call to nand_setup_data_interface() is only required - * for the first die, because nand_reset() has been called before - * ->data_interface and ->default_onfi_timing_mode were set. - * For the other dies, nand_reset() will automatically switch to the - * best mode for us. - */ - ret = nand_setup_data_interface(chip, 0); - if (ret) - return ret; - - chip->select_chip(mtd, -1); - - /* Check for a chip array */ - for (i = 1; i < maxchips; i++) { - /* See comment in nand_get_flash_type for reset */ - nand_reset(chip, i); - - chip->select_chip(mtd, i); - /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read manufacturer and device IDs */ - if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) { - chip->select_chip(mtd, -1); - break; - } - chip->select_chip(mtd, -1); - } - -#ifdef DEBUG - if (i > 1) - pr_info("%d chips detected\n", i); -#endif - - /* Store the number of chips and calc total size for mtd */ - chip->numchips = i; - mtd->size = i * chip->chipsize; - - return 0; -} -EXPORT_SYMBOL(nand_scan_ident); - -/** - * nand_check_ecc_caps - check the sanity of preset ECC settings - * @chip: nand chip info structure - * @caps: ECC caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * When ECC step size and strength are already set, check if they are supported - * by the controller and the calculated ECC bytes fit within the chip's OOB. - * On success, the calculated ECC bytes is set. - */ -int nand_check_ecc_caps(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int preset_step = chip->ecc.size; - int preset_strength = chip->ecc.strength; - int nsteps, ecc_bytes; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - if (!preset_step || !preset_strength) - return -ENODATA; - - nsteps = mtd->writesize / preset_step; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - - if (stepinfo->stepsize != preset_step) - continue; - - for (j = 0; j < stepinfo->nstrengths; j++) { - if (stepinfo->strengths[j] != preset_strength) - continue; - - ecc_bytes = caps->calc_ecc_bytes(preset_step, - preset_strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - return ecc_bytes; - - if (ecc_bytes * nsteps > oobavail) { - pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", - preset_step, preset_strength); - return -ENOSPC; - } - - chip->ecc.bytes = ecc_bytes; - - return 0; - } - } - - pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", - preset_step, preset_strength); - - return -ENOTSUPP; -} -EXPORT_SYMBOL_GPL(nand_check_ecc_caps); - -/** - * nand_match_ecc_req - meet the chip's requirement with least ECC bytes - * @chip: nand chip info structure - * @caps: ECC engine caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * If a chip's ECC requirement is provided, try to meet it with the least - * number of ECC bytes (i.e. with the largest number of OOB-free bytes). - * On success, the chosen ECC settings are set. - */ -int nand_match_ecc_req(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int req_step = chip->ecc_step_ds; - int req_strength = chip->ecc_strength_ds; - int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; - int best_step, best_strength, best_ecc_bytes; - int best_ecc_bytes_total = INT_MAX; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - /* No information provided by the NAND chip */ - if (!req_step || !req_strength) - return -ENOTSUPP; - - /* number of correctable bits the chip requires in a page */ - req_corr = mtd->writesize / req_step * req_strength; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - step_size = stepinfo->stepsize; - - for (j = 0; j < stepinfo->nstrengths; j++) { - strength = stepinfo->strengths[j]; - - /* - * If both step size and strength are smaller than the - * chip's requirement, it is not easy to compare the - * resulted reliability. - */ - if (step_size < req_step && strength < req_strength) - continue; - - if (mtd->writesize % step_size) - continue; - - nsteps = mtd->writesize / step_size; - - ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - continue; - ecc_bytes_total = ecc_bytes * nsteps; - - if (ecc_bytes_total > oobavail || - strength * nsteps < req_corr) - continue; - - /* - * We assume the best is to meet the chip's requrement - * with the least number of ECC bytes. - */ - if (ecc_bytes_total < best_ecc_bytes_total) { - best_ecc_bytes_total = ecc_bytes_total; - best_step = step_size; - best_strength = strength; - best_ecc_bytes = ecc_bytes; - } - } - } - - if (best_ecc_bytes_total == INT_MAX) - return -ENOTSUPP; - - chip->ecc.size = best_step; - chip->ecc.strength = best_strength; - chip->ecc.bytes = best_ecc_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_match_ecc_req); - -/** - * nand_maximize_ecc - choose the max ECC strength available - * @chip: nand chip info structure - * @caps: ECC engine caps info structure - * @oobavail: OOB size that the ECC engine can use - * - * Choose the max ECC strength that is supported on the controller, and can fit - * within the chip's OOB. On success, the chosen ECC settings are set. - */ -int nand_maximize_ecc(struct nand_chip *chip, - const struct nand_ecc_caps *caps, int oobavail) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_ecc_step_info *stepinfo; - int step_size, strength, nsteps, ecc_bytes, corr; - int best_corr = 0; - int best_step = 0; - int best_strength, best_ecc_bytes; - int i, j; - - if (WARN_ON(oobavail < 0)) - return -EINVAL; - - for (i = 0; i < caps->nstepinfos; i++) { - stepinfo = &caps->stepinfos[i]; - step_size = stepinfo->stepsize; - - /* If chip->ecc.size is already set, respect it */ - if (chip->ecc.size && step_size != chip->ecc.size) - continue; - - for (j = 0; j < stepinfo->nstrengths; j++) { - strength = stepinfo->strengths[j]; - - if (mtd->writesize % step_size) - continue; - - nsteps = mtd->writesize / step_size; - - ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ON_ONCE(ecc_bytes < 0)) - continue; - - if (ecc_bytes * nsteps > oobavail) - continue; - - corr = strength * nsteps; - - /* - * If the number of correctable bits is the same, - * bigger step_size has more reliability. - */ - if (corr > best_corr || - (corr == best_corr && step_size > best_step)) { - best_corr = corr; - best_step = step_size; - best_strength = strength; - best_ecc_bytes = ecc_bytes; - } - } - } - - if (!best_corr) - return -ENOTSUPP; - - chip->ecc.size = best_step; - chip->ecc.strength = best_strength; - chip->ecc.bytes = best_ecc_bytes; - - return 0; -} -EXPORT_SYMBOL_GPL(nand_maximize_ecc); - -/* - * Check if the chip configuration meet the datasheet requirements. - - * If our configuration corrects A bits per B bytes and the minimum - * required correction level is X bits per Y bytes, then we must ensure - * both of the following are true: - * - * (1) A / B >= X / Y - * (2) A >= X - * - * Requirement (1) ensures we can correct for the required bitflip density. - * Requirement (2) ensures we can correct even when all bitflips are clumped - * in the same sector. - */ -static bool nand_ecc_strength_good(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int corr, ds_corr; - - if (ecc->size == 0 || chip->ecc_step_ds == 0) - /* Not enough information */ - return true; - - /* - * We get the number of corrected bits per page to compare - * the correction density. - */ - corr = (mtd->writesize * ecc->strength) / ecc->size; - ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; - - return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; -} - -static bool invalid_ecc_page_accessors(struct nand_chip *chip) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (nand_standard_page_accessors(ecc)) - return false; - - /* - * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND - * controller driver implements all the page accessors because - * default helpers are not suitable when the core does not - * send the READ0/PAGEPROG commands. - */ - return (!ecc->read_page || !ecc->write_page || - !ecc->read_page_raw || !ecc->write_page_raw || - (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) || - (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage && - ecc->hwctl && ecc->calculate)); -} - -/** - * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * - * This is the second phase of the normal nand_scan() function. It fills out - * all the uninitialized function pointers with the defaults and scans for a - * bad block table if appropriate. - */ -int nand_scan_tail(struct mtd_info *mtd) -{ - int i; - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_buffers *nbuf; - - /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH)); - - if (invalid_ecc_page_accessors(chip)) { - pr_err("Invalid ECC page accessors setup\n"); - return -EINVAL; - } - - if (!(chip->options & NAND_OWN_BUFFERS)) { - nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL); - chip->buffers = nbuf; - } else { - if (!chip->buffers) - return -ENOMEM; - } - - /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers->databuf + mtd->writesize; - - /* - * If no default placement scheme is given, select an appropriate one. - */ - if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { - switch (mtd->oobsize) { - case 8: - ecc->layout = &nand_oob_8; - break; - case 16: - ecc->layout = &nand_oob_16; - break; - case 64: - ecc->layout = &nand_oob_64; - break; - case 128: - ecc->layout = &nand_oob_128; - break; - default: - pr_warn("No oob scheme defined for oobsize %d\n", - mtd->oobsize); - BUG(); - } - } - - if (!chip->write_page) - chip->write_page = nand_write_page; - - /* - * Check ECC mode, default to software if 3byte/512byte hardware ECC is - * selected and we have 256 byte pagesize fallback to software ECC - */ - - switch (ecc->mode) { - case NAND_ECC_HW_OOB_FIRST: - /* Similar to NAND_ECC_HW, but a separate read_page handle */ - if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { - pr_warn("No ECC functions supplied; hardware ECC not possible\n"); - BUG(); - } - if (!ecc->read_page) - ecc->read_page = nand_read_page_hwecc_oob_first; - - case NAND_ECC_HW: - /* Use standard hwecc read page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_hwecc; - if (!ecc->write_page) - ecc->write_page = nand_write_page_hwecc; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_std; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_std; - if (!ecc->read_subpage) - ecc->read_subpage = nand_read_subpage; - if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) - ecc->write_subpage = nand_write_subpage_hwecc; - - case NAND_ECC_HW_SYNDROME: - if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && - (!ecc->read_page || - ecc->read_page == nand_read_page_hwecc || - !ecc->write_page || - ecc->write_page == nand_write_page_hwecc)) { - pr_warn("No ECC functions supplied; hardware ECC not possible\n"); - BUG(); - } - /* Use standard syndrome read/write page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_syndrome; - if (!ecc->write_page) - ecc->write_page = nand_write_page_syndrome; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw_syndrome; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw_syndrome; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_syndrome; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= ecc->size) { - if (!ecc->strength) { - pr_warn("Driver must set ecc.strength when using hardware ECC\n"); - BUG(); - } - break; - } - pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", - ecc->size, mtd->writesize); - ecc->mode = NAND_ECC_SOFT; - - case NAND_ECC_SOFT: - ecc->calculate = nand_calculate_ecc; - ecc->correct = nand_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - if (!ecc->size) - ecc->size = 256; - ecc->bytes = 3; - ecc->strength = 1; - break; - - case NAND_ECC_SOFT_BCH: - if (!mtd_nand_has_bch()) { - pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); - BUG(); - } - ecc->calculate = nand_bch_calculate_ecc; - ecc->correct = nand_bch_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - /* - * Board driver should supply ecc.size and ecc.strength values - * to select how many bits are correctable. Otherwise, default - * to 4 bits for large page devices. - */ - if (!ecc->size && (mtd->oobsize >= 64)) { - ecc->size = 512; - ecc->strength = 4; - } - - /* See nand_bch_init() for details. */ - ecc->bytes = 0; - ecc->priv = nand_bch_init(mtd); - if (!ecc->priv) { - pr_warn("BCH ECC initialization failed!\n"); - BUG(); - } - break; - - case NAND_ECC_NONE: - pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); - ecc->read_page = nand_read_page_raw; - ecc->write_page = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->write_oob = nand_write_oob_std; - ecc->size = mtd->writesize; - ecc->bytes = 0; - ecc->strength = 0; - break; - - default: - pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); - BUG(); - } - - /* For many systems, the standard OOB write also works for raw */ - if (!ecc->read_oob_raw) - ecc->read_oob_raw = ecc->read_oob; - if (!ecc->write_oob_raw) - ecc->write_oob_raw = ecc->write_oob; - - /* - * The number of bytes available for a client to place data into - * the out of band area. - */ - mtd->oobavail = 0; - if (ecc->layout) { - for (i = 0; ecc->layout->oobfree[i].length; i++) - mtd->oobavail += ecc->layout->oobfree[i].length; - } - - /* ECC sanity check: warn if it's too weak */ - if (!nand_ecc_strength_good(mtd)) - pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", - mtd->name); - - /* - * Set the number of read / write steps for one page depending on ECC - * mode. - */ - ecc->steps = mtd->writesize / ecc->size; - if (ecc->steps * ecc->size != mtd->writesize) { - pr_warn("Invalid ECC parameters\n"); - BUG(); - } - ecc->total = ecc->steps * ecc->bytes; - - /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { - switch (ecc->steps) { - case 2: - mtd->subpage_sft = 1; - break; - case 4: - case 8: - case 16: - mtd->subpage_sft = 2; - break; - } - } - chip->subpagesize = mtd->writesize >> mtd->subpage_sft; - - /* Initialize state */ - chip->state = FL_READY; - - /* Invalidate the pagebuffer reference */ - chip->pagebuf = -1; - - /* Large page NAND with SOFT_ECC should support subpage reads */ - switch (ecc->mode) { - case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: - if (chip->page_shift > 9) - chip->options |= NAND_SUBPAGE_READ; - break; - - default: - break; - } - - /* Fill in remaining MTD driver data */ - mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; - mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : - MTD_CAP_NANDFLASH; - mtd->_erase = nand_erase; - mtd->_panic_write = panic_nand_write; - mtd->_read_oob = nand_read_oob; - mtd->_write_oob = nand_write_oob; - mtd->_sync = nand_sync; - mtd->_lock = NULL; - mtd->_unlock = NULL; - mtd->_block_isreserved = nand_block_isreserved; - mtd->_block_isbad = nand_block_isbad; - mtd->_block_markbad = nand_block_markbad; - mtd->writebufsize = mtd->writesize; - - /* propagate ecc info to mtd_info */ - mtd->ecclayout = ecc->layout; - mtd->ecc_strength = ecc->strength; - mtd->ecc_step_size = ecc->size; - /* - * Initialize bitflip_threshold to its default prior scan_bbt() call. - * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be - * properly set. - */ - if (!mtd->bitflip_threshold) - mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); - - return 0; -} -EXPORT_SYMBOL(nand_scan_tail); - -/** - * nand_scan - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * @maxchips: number of chips to scan for - * - * This fills out all the uninitialized function pointers with the defaults. - * The flash ID is read and the mtd/chip structures are filled with the - * appropriate values. - */ -int nand_scan(struct mtd_info *mtd, int maxchips) -{ - int ret; - - ret = nand_scan_ident(mtd, maxchips, NULL); - if (!ret) - ret = nand_scan_tail(mtd); - return ret; -} -EXPORT_SYMBOL(nand_scan); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill "); -MODULE_AUTHOR("Thomas Gleixner "); -MODULE_DESCRIPTION("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c deleted file mode 100644 index ba785c5d53..0000000000 --- a/drivers/mtd/nand/nand_bbt.c +++ /dev/null @@ -1,1373 +0,0 @@ -/* - * Overview: - * Bad block table support for the NAND driver - * - * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) - * - * This program 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. - * - * Description: - * - * When nand_scan_bbt is called, then it tries to find the bad block table - * depending on the options in the BBT descriptor(s). If no flash based BBT - * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory - * marked good / bad blocks. This information is used to create a memory BBT. - * Once a new bad block is discovered then the "factory" information is updated - * on the device. - * If a flash based BBT is specified then the function first tries to find the - * BBT on flash. If a BBT is found then the contents are read and the memory - * based BBT is created. If a mirrored BBT is selected then the mirror is - * searched too and the versions are compared. If the mirror has a greater - * version number, then the mirror BBT is used to build the memory based BBT. - * If the tables are not versioned, then we "or" the bad block information. - * If one of the BBTs is out of date or does not exist it is (re)created. - * If no BBT exists at all then the device is scanned for factory marked - * good / bad blocks and the bad block tables are created. - * - * For manufacturer created BBTs like the one found on M-SYS DOC devices - * the BBT is searched and read but never created - * - * The auto generated bad block table is located in the last good blocks - * of the device. The table is mirrored, so it can be updated eventually. - * The table is marked in the OOB area with an ident pattern and a version - * number which indicates which of both tables is more up to date. If the NAND - * controller needs the complete OOB area for the ECC information then the - * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of - * course): it moves the ident pattern and the version byte into the data area - * and the OOB area will remain untouched. - * - * The table uses 2 bits per block - * 11b: block is good - * 00b: block is factory marked bad - * 01b, 10b: block is marked bad due to wear - * - * The memory bad block table uses the following scheme: - * 00b: block is good - * 01b: block is marked bad due to wear - * 10b: block is reserved (to protect the bbt area) - * 11b: block is factory marked bad - * - * Multichip devices like DOC store the bad block info per floor. - * - * Following assumptions are made: - * - bbts start at a page boundary, if autolocated on a block boundary - * - the space necessary for a bbt in FLASH does not exceed a block boundary - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define BBT_BLOCK_GOOD 0x00 -#define BBT_BLOCK_WORN 0x01 -#define BBT_BLOCK_RESERVED 0x02 -#define BBT_BLOCK_FACTORY_BAD 0x03 - -#define BBT_ENTRY_MASK 0x03 -#define BBT_ENTRY_SHIFT 2 - -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); - -static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) -{ - uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; - entry >>= (block & BBT_ENTRY_MASK) * 2; - return entry & BBT_ENTRY_MASK; -} - -static inline void bbt_mark_entry(struct nand_chip *chip, int block, - uint8_t mark) -{ - uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); - chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; -} - -static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) -{ - if (memcmp(buf, td->pattern, td->len)) - return -1; - return 0; -} - -/** - * check_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @len: the length of buffer to search - * @paglen: the pagelength - * @td: search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. - */ -static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) -{ - if (td->options & NAND_BBT_NO_OOB) - return check_pattern_no_oob(buf, td); - - /* Compare the pattern */ - if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) - return -1; - - return 0; -} - -/** - * check_short_pattern - [GENERIC] check if a pattern is in the buffer - * @buf: the buffer to search - * @td: search pattern descriptor - * - * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. Same as check_pattern, but no optional empty - * check. - */ -static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) -{ - /* Compare the pattern */ - if (memcmp(buf + td->offs, td->pattern, td->len)) - return -1; - return 0; -} - -/** - * add_marker_len - compute the length of the marker in data area - * @td: BBT descriptor used for computation - * - * The length will be 0 if the marker is located in OOB area. - */ -static u32 add_marker_len(struct nand_bbt_descr *td) -{ - u32 len; - - if (!(td->options & NAND_BBT_NO_OOB)) - return 0; - - len = td->len; - if (td->options & NAND_BBT_VERSION) - len++; - return len; -} - -/** - * read_bbt - [GENERIC] Read the bad block table starting from page - * @mtd: MTD device structure - * @buf: temporary buffer - * @page: the starting page - * @num: the number of bbt descriptors to read - * @td: the bbt describtion table - * @offs: block number offset in the table - * - * Read the bad block table starting from page. - */ -static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, - struct nand_bbt_descr *td, int offs) -{ - int res, ret = 0, i, j, act = 0; - struct nand_chip *this = mtd_to_nand(mtd); - size_t retlen, len, totlen; - loff_t from; - int bits = td->options & NAND_BBT_NRBITS_MSK; - uint8_t msk = (uint8_t)((1 << bits) - 1); - u32 marker_len; - int reserved_block_code = td->reserved_block_code; - - totlen = (num * bits) >> 3; - marker_len = add_marker_len(td); - from = ((loff_t)page) << this->page_shift; - - while (totlen) { - len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); - if (marker_len) { - /* - * In case the BBT marker is not in the OOB area it - * will be just in the first page. - */ - len -= marker_len; - from += marker_len; - marker_len = 0; - } - res = mtd_read(mtd, from, len, &retlen, buf); - if (res < 0) { - if (mtd_is_eccerr(res)) { - pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", - from & ~mtd->writesize); - return res; - } else if (mtd_is_bitflip(res)) { - pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", - from & ~mtd->writesize); - ret = res; - } else { - pr_info("nand_bbt: error reading BBT\n"); - return res; - } - } - - /* Analyse data */ - for (i = 0; i < len; i++) { - uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act++) { - uint8_t tmp = (dat >> j) & msk; - if (tmp == msk) - continue; - if (reserved_block_code && (tmp == reserved_block_code)) { - pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)(offs + act) << - this->bbt_erase_shift); - bbt_mark_entry(this, offs + act, - BBT_BLOCK_RESERVED); - mtd->ecc_stats.bbtblocks++; - continue; - } - /* - * Leave it for now, if it's matured we can - * move this message to pr_debug. - */ - pr_info("nand_read_bbt: bad block at 0x%012llx\n", - (loff_t)(offs + act) << - this->bbt_erase_shift); - /* Factory marked bad or worn out? */ - if (tmp == 0) - bbt_mark_entry(this, offs + act, - BBT_BLOCK_FACTORY_BAD); - else - bbt_mark_entry(this, offs + act, - BBT_BLOCK_WORN); - mtd->ecc_stats.badblocks++; - } - } - totlen -= len; - from += len; - } - return ret; -} - -/** - * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @chip: read the table for a specific chip, -1 read all chips; applies only if - * NAND_BBT_PERCHIP option is set - * - * Read the bad block table for all chips starting at a given page. We assume - * that the bbt bits are in consecutive order. - */ -static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int res = 0, i; - - if (td->options & NAND_BBT_PERCHIP) { - int offs = 0; - for (i = 0; i < this->numchips; i++) { - if (chip == -1 || chip == i) - res = read_bbt(mtd, buf, td->pages[i], - this->chipsize >> this->bbt_erase_shift, - td, offs); - if (res) - return res; - offs += this->chipsize >> this->bbt_erase_shift; - } - } else { - res = read_bbt(mtd, buf, td->pages[0], - mtd->size >> this->bbt_erase_shift, td, 0); - if (res) - return res; - } - return 0; -} - -/* BBT marker is in the first page, no OOB */ -static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - struct nand_bbt_descr *td) -{ - size_t retlen; - size_t len; - - len = td->len; - if (td->options & NAND_BBT_VERSION) - len++; - - return mtd_read(mtd, offs, len, &retlen, buf); -} - -/** - * scan_read_oob - [GENERIC] Scan data+OOB region to buffer - * @mtd: MTD device structure - * @buf: temporary buffer - * @offs: offset at which to scan - * @len: length of data region to read - * - * Scan read data from data+OOB. May traverse multiple pages, interleaving - * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" - * ECC condition (error or bitflip). May quit on the first (non-ECC) error. - */ -static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len) -{ - struct mtd_oob_ops ops; - int res, ret = 0; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - - while (len > 0) { - ops.datbuf = buf; - ops.len = min(len, (size_t)mtd->writesize); - ops.oobbuf = buf + ops.len; - - res = mtd_read_oob(mtd, offs, &ops); - if (res) { - if (!mtd_is_bitflip_or_eccerr(res)) - return res; - else if (mtd_is_eccerr(res) || !ret) - ret = res; - } - - buf += mtd->oobsize + mtd->writesize; - len -= mtd->writesize; - offs += mtd->writesize; - } - return ret; -} - -static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, - size_t len, struct nand_bbt_descr *td) -{ - if (td->options & NAND_BBT_NO_OOB) - return scan_read_data(mtd, buf, offs, td); - else - return scan_read_oob(mtd, buf, offs, len); -} - -/* Scan write data with oob to flash */ -static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, - uint8_t *buf, uint8_t *oob) -{ - struct mtd_oob_ops ops; - - ops.mode = MTD_OPS_PLACE_OOB; - ops.ooboffs = 0; - ops.ooblen = mtd->oobsize; - ops.datbuf = buf; - ops.oobbuf = oob; - ops.len = len; - - return mtd_write_oob(mtd, offs, &ops); -} - -static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) -{ - u32 ver_offs = td->veroffs; - - if (!(td->options & NAND_BBT_NO_OOB)) - ver_offs += mtd->writesize; - return ver_offs; -} - -/** - * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * - * Read the bad block table(s) for all chips starting at a given page. We - * assume that the bbt bits are in consecutive order. - */ -static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - /* Read the primary version, if available */ - if (td->options & NAND_BBT_VERSION) { - scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, - mtd->writesize, td); - td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; - pr_info("Bad block table at page %d, version 0x%02X\n", - td->pages[0], td->version[0]); - } - - /* Read the mirror version, if available */ - if (md && (md->options & NAND_BBT_VERSION)) { - scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, - mtd->writesize, md); - md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; - pr_info("Bad block table at page %d, version 0x%02X\n", - md->pages[0], md->version[0]); - } -} - -/* Scan a given block partially */ -static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, int numpages) -{ - struct mtd_oob_ops ops; - int j, ret; - - ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.mode = MTD_OPS_PLACE_OOB; - - for (j = 0; j < numpages; j++) { - /* - * Read the full oob until read_oob is fixed to handle single - * byte reads for 16 bit buswidth. - */ - ret = mtd_read_oob(mtd, offs, &ops); - /* Ignore ECC errors when checking for BBM */ - if (ret && !mtd_is_bitflip_or_eccerr(ret)) - return ret; - - if (check_short_pattern(buf, bd)) - return 1; - - offs += mtd->writesize; - } - return 0; -} - -/** - * create_bbt - [GENERIC] Create a bad block table by scanning the device - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern - * @chip: create the table for a specific chip, -1 read all chips; applies only - * if NAND_BBT_PERCHIP option is set - * - * Create a bad block table by scanning the device for the given good/bad block - * identify pattern. - */ -static int create_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *bd, int chip) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, numblocks, numpages; - int startblock; - loff_t from; - - pr_info("Scanning device for bad blocks\n"); - - if (bd->options & NAND_BBT_SCAN2NDPAGE) - numpages = 2; - else - numpages = 1; - - if (chip == -1) { - numblocks = mtd->size >> this->bbt_erase_shift; - startblock = 0; - from = 0; - } else { - if (chip >= this->numchips) { - pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", - chip + 1, this->numchips); - return -EINVAL; - } - numblocks = this->chipsize >> this->bbt_erase_shift; - startblock = chip * numblocks; - numblocks += startblock; - from = (loff_t)startblock << this->bbt_erase_shift; - } - - if (this->bbt_options & NAND_BBT_SCANLASTPAGE) - from += mtd->erasesize - (mtd->writesize * numpages); - - for (i = startblock; i < numblocks; i++) { - int ret; - - BUG_ON(bd->options & NAND_BBT_NO_OOB); - - ret = scan_block_fast(mtd, bd, from, buf, numpages); - if (ret < 0) - return ret; - - if (ret) { - bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); - pr_warn("Bad eraseblock %d at 0x%012llx\n", - i, (unsigned long long)from); - mtd->ecc_stats.badblocks++; - } - - from += (1 << this->bbt_erase_shift); - } - return 0; -} - -/** - * search_bbt - [GENERIC] scan the device for a specific bad block table - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * - * Read the bad block table by searching for a given ident pattern. Search is - * preformed either from the beginning up or from the end of the device - * downwards. The search starts always at the start of a block. If the option - * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains - * the bad block information of this chip. This is necessary to provide support - * for certain DOC devices. - * - * The bbt ident pattern resides in the oob area of the first page in a block. - */ -static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, chips; - int startblock, block, dir; - int scanlen = mtd->writesize + mtd->oobsize; - int bbtblocks; - int blocktopage = this->bbt_erase_shift - this->page_shift; - - /* Search direction top -> down? */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = (mtd->size >> this->bbt_erase_shift) - 1; - dir = -1; - } else { - startblock = 0; - dir = 1; - } - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chips = this->numchips; - bbtblocks = this->chipsize >> this->bbt_erase_shift; - startblock &= bbtblocks - 1; - } else { - chips = 1; - bbtblocks = mtd->size >> this->bbt_erase_shift; - } - - for (i = 0; i < chips; i++) { - /* Reset version information */ - td->version[i] = 0; - td->pages[i] = -1; - /* Scan the maximum number of blocks */ - for (block = 0; block < td->maxblocks; block++) { - - int actblock = startblock + dir * block; - loff_t offs = (loff_t)actblock << this->bbt_erase_shift; - - /* Read first page */ - scan_read(mtd, buf, offs, mtd->writesize, td); - if (!check_pattern(buf, scanlen, mtd->writesize, td)) { - td->pages[i] = actblock << blocktopage; - if (td->options & NAND_BBT_VERSION) { - offs = bbt_get_ver_offs(mtd, td); - td->version[i] = buf[offs]; - } - break; - } - } - startblock += this->chipsize >> this->bbt_erase_shift; - } - /* Check, if we found a bbt for each requested chip */ - for (i = 0; i < chips; i++) { - if (td->pages[i] == -1) - pr_warn("Bad block table not found for chip %d\n", i); - else - pr_info("Bad block table found at page %d, version 0x%02X\n", - td->pages[i], td->version[i]); - } - return 0; -} - -/** - * search_read_bbts - [GENERIC] scan the device for bad block table(s) - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * - * Search and read the bad block table(s). - */ -static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, - struct nand_bbt_descr *md) -{ - /* Search the primary table */ - search_bbt(mtd, buf, td); - - /* Search the mirror table */ - if (md) - search_bbt(mtd, buf, md); -} - -/** - * write_bbt - [GENERIC] (Re)write the bad block table - * @mtd: MTD device structure - * @buf: temporary buffer - * @td: descriptor for the bad block table - * @md: descriptor for the bad block table mirror - * @chipsel: selector for a specific chip, -1 for all - * - * (Re)write the bad block table. - */ -static int write_bbt(struct mtd_info *mtd, uint8_t *buf, - struct nand_bbt_descr *td, struct nand_bbt_descr *md, - int chipsel) -{ - struct nand_chip *this = mtd_to_nand(mtd); - struct erase_info einfo; - int i, res, chip = 0; - int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, pageoffs, ooboffs; - uint8_t msk[4]; - uint8_t rcode = td->reserved_block_code; - size_t retlen, len = 0; - loff_t to; - struct mtd_oob_ops ops; - - ops.ooblen = mtd->oobsize; - ops.ooboffs = 0; - ops.datbuf = NULL; - ops.mode = MTD_OPS_PLACE_OOB; - - if (!rcode) - rcode = 0xff; - /* Write bad block table per chip rather than per device? */ - if (td->options & NAND_BBT_PERCHIP) { - numblocks = (int)(this->chipsize >> this->bbt_erase_shift); - /* Full device write or specific chip? */ - if (chipsel == -1) { - nrchips = this->numchips; - } else { - nrchips = chipsel + 1; - chip = chipsel; - } - } else { - numblocks = (int)(mtd->size >> this->bbt_erase_shift); - nrchips = 1; - } - - /* Loop through the chips */ - for (; chip < nrchips; chip++) { - /* - * There was already a version of the table, reuse the page - * This applies for absolute placement too, as we have the - * page nr. in td->pages. - */ - if (td->pages[chip] != -1) { - page = td->pages[chip]; - goto write; - } - - /* - * Automatic placement of the bad block table. Search direction - * top -> down? - */ - if (td->options & NAND_BBT_LASTBLOCK) { - startblock = numblocks * (chip + 1) - 1; - dir = -1; - } else { - startblock = chip * numblocks; - dir = 1; - } - - for (i = 0; i < td->maxblocks; i++) { - int block = startblock + dir * i; - /* Check, if the block is bad */ - switch (bbt_get_entry(this, block)) { - case BBT_BLOCK_WORN: - case BBT_BLOCK_FACTORY_BAD: - continue; - } - page = block << - (this->bbt_erase_shift - this->page_shift); - /* Check, if the block is used by the mirror table */ - if (!md || md->pages[chip] != page) - goto write; - } - pr_err("No space left to write bad block table\n"); - return -ENOSPC; - write: - - /* Set up shift count and masks for the flash table */ - bits = td->options & NAND_BBT_NRBITS_MSK; - msk[2] = ~rcode; - switch (bits) { - case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; - msk[3] = 0x01; - break; - case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; - msk[3] = 0x03; - break; - case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; - msk[3] = 0x0f; - break; - case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; - msk[3] = 0xff; - break; - default: return -EINVAL; - } - - to = ((loff_t)page) << this->page_shift; - - /* Must we save the block contents? */ - if (td->options & NAND_BBT_SAVECONTENT) { - /* Make it block aligned */ - to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); - len = 1 << this->bbt_erase_shift; - res = mtd_read(mtd, to, len, &retlen, buf); - if (res < 0) { - if (retlen != len) { - pr_info("nand_bbt: error reading block for writing the bad block table\n"); - return res; - } - pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); - } - /* Read oob data */ - ops.ooblen = (len >> this->page_shift) * mtd->oobsize; - ops.oobbuf = &buf[len]; - res = mtd_read_oob(mtd, to + mtd->writesize, &ops); - if (res < 0 || ops.oobretlen != ops.ooblen) - goto outerr; - - /* Calc the byte offset in the buffer */ - pageoffs = page - (int)(to >> this->page_shift); - offs = pageoffs << this->page_shift; - /* Preset the bbt area with 0xff */ - memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); - ooboffs = len + (pageoffs * mtd->oobsize); - - } else if (td->options & NAND_BBT_NO_OOB) { - ooboffs = 0; - offs = td->len; - /* The version byte */ - if (td->options & NAND_BBT_VERSION) - offs++; - /* Calc length */ - len = (size_t)(numblocks >> sft); - len += offs; - /* Make it page aligned! */ - len = ALIGN(len, mtd->writesize); - /* Preset the buffer with 0xff */ - memset(buf, 0xff, len); - /* Pattern is located at the begin of first page */ - memcpy(buf, td->pattern, td->len); - } else { - /* Calc length */ - len = (size_t)(numblocks >> sft); - /* Make it page aligned! */ - len = ALIGN(len, mtd->writesize); - /* Preset the buffer with 0xff */ - memset(buf, 0xff, len + - (len >> this->page_shift)* mtd->oobsize); - offs = 0; - ooboffs = len; - /* Pattern is located in oob area of first page */ - memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); - } - - if (td->options & NAND_BBT_VERSION) - buf[ooboffs + td->veroffs] = td->version[chip]; - - /* Walk through the memory table */ - for (i = 0; i < numblocks; i++) { - uint8_t dat; - int sftcnt = (i << (3 - sft)) & sftmsk; - dat = bbt_get_entry(this, chip * numblocks + i); - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); - } - - memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; - einfo.addr = to; - einfo.len = 1 << this->bbt_erase_shift; - res = nand_erase_nand(mtd, &einfo, 1); - if (res < 0) - goto outerr; - - res = scan_write_bbt(mtd, to, len, buf, - td->options & NAND_BBT_NO_OOB ? NULL : - &buf[len]); - if (res < 0) - goto outerr; - - pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", - (unsigned long long)to, td->version[chip]); - - /* Mark it as used */ - td->pages[chip] = page; - } - return 0; - - outerr: - pr_warn("nand_bbt: error while writing bad block table %d\n", res); - return res; -} - -/** - * nand_memory_bbt - [GENERIC] create a memory based bad block table - * @mtd: MTD device structure - * @bd: descriptor for the good/bad block search pattern - * - * The function creates a memory based bbt by scanning the device for - * manufacturer / software marked good / bad blocks. - */ -static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - return create_bbt(mtd, this->buffers->databuf, bd, -1); -} - -/** - * check_create - [GENERIC] create and write bbt(s) if necessary - * @mtd: MTD device structure - * @buf: temporary buffer - * @bd: descriptor for the good/bad block search pattern - * - * The function checks the results of the previous call to read_bbt and creates - * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found - * for the chip/device. Update is necessary if one of the tables is missing or - * the version nr. of one table is less than the other. - */ -static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) -{ - int i, chips, writeops, create, chipsel, res, res2; - struct nand_chip *this = mtd_to_nand(mtd); - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - struct nand_bbt_descr *rd, *rd2; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) - chips = this->numchips; - else - chips = 1; - - for (i = 0; i < chips; i++) { - writeops = 0; - create = 0; - rd = NULL; - rd2 = NULL; - res = res2 = 0; - /* Per chip or per device? */ - chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; - /* Mirrored table available? */ - if (md) { - if (td->pages[i] == -1 && md->pages[i] == -1) { - create = 1; - writeops = 0x03; - } else if (td->pages[i] == -1) { - rd = md; - writeops = 0x01; - } else if (md->pages[i] == -1) { - rd = td; - writeops = 0x02; - } else if (td->version[i] == md->version[i]) { - rd = td; - if (!(td->options & NAND_BBT_VERSION)) - rd2 = md; - } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { - rd = td; - writeops = 0x02; - } else { - rd = md; - writeops = 0x01; - } - } else { - if (td->pages[i] == -1) { - create = 1; - writeops = 0x01; - } else { - rd = td; - } - } - - if (create) { - /* Create the bad block table by scanning the device? */ - if (!(td->options & NAND_BBT_CREATE)) - continue; - - /* Create the table in memory by scanning the chip(s) */ - if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) - create_bbt(mtd, buf, bd, chipsel); - - td->version[i] = 1; - if (md) - md->version[i] = 1; - } - - /* Read back first? */ - if (rd) { - res = read_abs_bbt(mtd, buf, rd, chipsel); - if (mtd_is_eccerr(res)) { - /* Mark table as invalid */ - rd->pages[i] = -1; - rd->version[i] = 0; - i--; - continue; - } - } - /* If they weren't versioned, read both */ - if (rd2) { - res2 = read_abs_bbt(mtd, buf, rd2, chipsel); - if (mtd_is_eccerr(res2)) { - /* Mark table as invalid */ - rd2->pages[i] = -1; - rd2->version[i] = 0; - i--; - continue; - } - } - - /* Scrub the flash table(s)? */ - if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) - writeops = 0x03; - - /* Update version numbers before writing */ - if (md) { - td->version[i] = max(td->version[i], md->version[i]); - md->version[i] = td->version[i]; - } - - /* Write the bad block table to the device? */ - if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, td, md, chipsel); - if (res < 0) - return res; - } - - /* Write the mirror bad block table to the device? */ - if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); - if (res < 0) - return res; - } - } - return 0; -} - -/** - * mark_bbt_regions - [GENERIC] mark the bad block table regions - * @mtd: MTD device structure - * @td: bad block table descriptor - * - * The bad block table regions are marked as "bad" to prevent accidental - * erasures / writes. The regions are identified by the mark 0x02. - */ -static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int i, j, chips, block, nrblocks, update; - uint8_t oldval; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chips = this->numchips; - nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); - } else { - chips = 1; - nrblocks = (int)(mtd->size >> this->bbt_erase_shift); - } - - for (i = 0; i < chips; i++) { - if ((td->options & NAND_BBT_ABSPAGE) || - !(td->options & NAND_BBT_WRITE)) { - if (td->pages[i] == -1) - continue; - block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - oldval = bbt_get_entry(this, block); - bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); - if ((oldval != BBT_BLOCK_RESERVED) && - td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << - this->bbt_erase_shift); - continue; - } - update = 0; - if (td->options & NAND_BBT_LASTBLOCK) - block = ((i + 1) * nrblocks) - td->maxblocks; - else - block = i * nrblocks; - for (j = 0; j < td->maxblocks; j++) { - oldval = bbt_get_entry(this, block); - bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); - if (oldval != BBT_BLOCK_RESERVED) - update = 1; - block++; - } - /* - * If we want reserved blocks to be recorded to flash, and some - * new ones have been marked, then we need to update the stored - * bbts. This should only happen once. - */ - if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 1) << - this->bbt_erase_shift); - } -} - -/** - * verify_bbt_descr - verify the bad block description - * @mtd: MTD device structure - * @bd: the table to verify - * - * This functions performs a few sanity checks on the bad block description - * table. - */ -static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u32 pattern_len; - u32 bits; - u32 table_size; - - if (!bd) - return; - - pattern_len = bd->len; - bits = bd->options & NAND_BBT_NRBITS_MSK; - - BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && - !(this->bbt_options & NAND_BBT_USE_FLASH)); - BUG_ON(!bits); - - if (bd->options & NAND_BBT_VERSION) - pattern_len++; - - if (bd->options & NAND_BBT_NO_OOB) { - BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); - BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); - BUG_ON(bd->offs); - if (bd->options & NAND_BBT_VERSION) - BUG_ON(bd->veroffs != bd->len); - BUG_ON(bd->options & NAND_BBT_SAVECONTENT); - } - - if (bd->options & NAND_BBT_PERCHIP) - table_size = this->chipsize >> this->bbt_erase_shift; - else - table_size = mtd->size >> this->bbt_erase_shift; - table_size >>= 3; - table_size *= bits; - if (bd->options & NAND_BBT_NO_OOB) - table_size += pattern_len; - BUG_ON(table_size > (1 << this->bbt_erase_shift)); -} - -/** - * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) - * @mtd: MTD device structure - * @bd: descriptor for the good/bad block search pattern - * - * The function checks, if a bad block table(s) is/are already available. If - * not it scans the device for manufacturer marked good / bad blocks and writes - * the bad block table(s) to the selected place. - * - * The bad block table memory is allocated here. It must be freed by calling - * the nand_free_bbt function. - */ -static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int len, res; - uint8_t *buf; - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - - len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; - /* - * Allocate memory (2bit per block) and clear the memory bad block - * table. - */ - this->bbt = kzalloc(len, GFP_KERNEL); - if (!this->bbt) - return -ENOMEM; - - /* - * If no primary table decriptor is given, scan the device to build a - * memory based bad block table. - */ - if (!td) { - if ((res = nand_memory_bbt(mtd, bd))) { - pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); - goto err; - } - return 0; - } - verify_bbt_descr(mtd, td); - verify_bbt_descr(mtd, md); - - /* Allocate a temporary buffer for one eraseblock incl. oob */ - len = (1 << this->bbt_erase_shift); - len += (len >> this->page_shift) * mtd->oobsize; - buf = vmalloc(len); - if (!buf) { - res = -ENOMEM; - goto err; - } - - /* Is the bbt at a given page? */ - if (td->options & NAND_BBT_ABSPAGE) { - read_abs_bbts(mtd, buf, td, md); - } else { - /* Search the bad block table using a pattern in oob */ - search_read_bbts(mtd, buf, td, md); - } - - res = check_create(mtd, buf, bd); - if (res) - goto err; - - /* Prevent the bbt regions from erasing / writing */ - mark_bbt_region(mtd, td); - if (md) - mark_bbt_region(mtd, md); - - vfree(buf); - return 0; - -err: - kfree(this->bbt); - this->bbt = NULL; - return res; -} - -/** - * nand_update_bbt - update bad block table(s) - * @mtd: MTD device structure - * @offs: the offset of the newly marked block - * - * The function updates the bad block table(s). - */ -static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int len, res = 0; - int chip, chipsel; - uint8_t *buf; - struct nand_bbt_descr *td = this->bbt_td; - struct nand_bbt_descr *md = this->bbt_md; - - if (!this->bbt || !td) - return -EINVAL; - - /* Allocate a temporary buffer for one eraseblock incl. oob */ - len = (1 << this->bbt_erase_shift); - len += (len >> this->page_shift) * mtd->oobsize; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* Do we have a bbt per chip? */ - if (td->options & NAND_BBT_PERCHIP) { - chip = (int)(offs >> this->chip_shift); - chipsel = chip; - } else { - chip = 0; - chipsel = -1; - } - - td->version[chip]++; - if (md) - md->version[chip]++; - - /* Write the bad block table to the device? */ - if (td->options & NAND_BBT_WRITE) { - res = write_bbt(mtd, buf, td, md, chipsel); - if (res < 0) - goto out; - } - /* Write the mirror bad block table to the device? */ - if (md && (md->options & NAND_BBT_WRITE)) { - res = write_bbt(mtd, buf, md, td, chipsel); - } - - out: - kfree(buf); - return res; -} - -/* - * Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks. - */ -static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; - -/* Generic flash bbt descriptors */ -static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 8, - .len = 4, - .veroffs = 12, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = mirror_pattern -}; - -static struct nand_bbt_descr bbt_main_no_oob_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP - | NAND_BBT_NO_OOB, - .len = 4, - .veroffs = 4, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_no_oob_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP - | NAND_BBT_NO_OOB, - .len = 4, - .veroffs = 4, - .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, - .pattern = mirror_pattern -}; - -#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) -/** - * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure - * @this: NAND chip to create descriptor for - * - * This function allocates and initializes a nand_bbt_descr for BBM detection - * based on the properties of @this. The new descriptor is stored in - * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when - * passed to this function. - */ -static int nand_create_badblock_pattern(struct nand_chip *this) -{ - struct nand_bbt_descr *bd; - if (this->badblock_pattern) { - pr_warn("Bad block pattern already allocated; not replacing\n"); - return -EINVAL; - } - bd = kzalloc(sizeof(*bd), GFP_KERNEL); - if (!bd) - return -ENOMEM; - bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; - bd->offs = this->badblockpos; - bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; - bd->pattern = scan_ff_pattern; - bd->options |= NAND_BBT_DYNAMICSTRUCT; - this->badblock_pattern = bd; - return 0; -} - -/** - * nand_default_bbt - [NAND Interface] Select a default bad block table for the device - * @mtd: MTD device structure - * - * This function selects the default bad block table support for the device and - * calls the nand_scan_bbt function. - */ -int nand_default_bbt(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int ret; - - /* Is a flash based bad block table requested? */ - if (this->bbt_options & NAND_BBT_USE_FLASH) { - /* Use the default pattern descriptors */ - if (!this->bbt_td) { - if (this->bbt_options & NAND_BBT_NO_OOB) { - this->bbt_td = &bbt_main_no_oob_descr; - this->bbt_md = &bbt_mirror_no_oob_descr; - } else { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - } - } - } else { - this->bbt_td = NULL; - this->bbt_md = NULL; - } - - if (!this->badblock_pattern) { - ret = nand_create_badblock_pattern(this); - if (ret) - return ret; - } - - return nand_scan_bbt(mtd, this->badblock_pattern); -} - -/** - * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved - * @mtd: MTD device structure - * @offs: offset in the device - */ -int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block; - - block = (int)(offs >> this->bbt_erase_shift); - return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; -} - -/** - * nand_isbad_bbt - [NAND Interface] Check if a block is bad - * @mtd: MTD device structure - * @offs: offset in the device - * @allowbbt: allow access to bad block table region - */ -int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block, res; - - block = (int)(offs >> this->bbt_erase_shift); - res = bbt_get_entry(this, block); - - pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, block, res); - - switch (res) { - case BBT_BLOCK_GOOD: - return 0; - case BBT_BLOCK_WORN: - return 1; - case BBT_BLOCK_RESERVED: - return allowbbt ? 0 : 1; - } - return 1; -} - -/** - * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT - * @mtd: MTD device structure - * @offs: offset of the bad block - */ -int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int block, ret = 0; - - block = (int)(offs >> this->bbt_erase_shift); - - /* Mark bad block in memory */ - bbt_mark_entry(this, block, BBT_BLOCK_WORN); - - /* Update flash-based bad block table */ - if (this->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, offs); - - return ret; -} diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c deleted file mode 100644 index afa0418168..0000000000 --- a/drivers/mtd/nand/nand_bch.c +++ /dev/null @@ -1,231 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * This file provides ECC correction for more than 1 bit per block of data, - * using binary BCH codes. It relies on the generic BCH library lib/bch.c. - * - * Copyright © 2011 Ivan Djelic - * - */ - -#include -/*#include */ -#include - -#include -#include -#include -#include -#include -#include - -/** - * struct nand_bch_control - private NAND BCH control structure - * @bch: BCH control structure - * @ecclayout: private ecc layout for this BCH configuration - * @errloc: error location array - * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid - */ -struct nand_bch_control { - struct bch_control *bch; - struct nand_ecclayout ecclayout; - unsigned int *errloc; - unsigned char *eccmask; -}; - -/** - * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block - * @mtd: MTD block structure - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, - unsigned char *code) -{ - const struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int i; - - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); - - /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) - code[i] ^= nbc->eccmask[i]; - - return 0; -} - -/** - * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) - * @mtd: MTD block structure - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct bit errors for a data byte block - */ -int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - const struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int *errloc = nbc->errloc; - int i, count; - - count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); - if (count > 0) { - for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) - /* error is located in data, correct it */ - buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); - /* else error in ecc, no action needed */ - - pr_debug("%s: corrected bitflip %u\n", - __func__, errloc[i]); - } - } else if (count < 0) { - printk(KERN_ERR "ecc unrecoverable error\n"); - count = -EBADMSG; - } - return count; -} - -/** - * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction - * @mtd: MTD block structure - * - * Returns: - * a pointer to a new NAND BCH control structure, or NULL upon failure - * - * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute BCH parameters m (Galois field order) and t (error - * correction capability). @eccbytes should be equal to the number of bytes - * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. - * - * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) - * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) - */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - unsigned int m, t, eccsteps, i; - struct nand_ecclayout *layout = nand->ecc.layout; - struct nand_bch_control *nbc = NULL; - unsigned char *erased_page; - unsigned int eccsize = nand->ecc.size; - unsigned int eccbytes = nand->ecc.bytes; - unsigned int eccstrength = nand->ecc.strength; - - if (!eccbytes && eccstrength) { - eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - nand->ecc.bytes = eccbytes; - } - - if (!eccsize || !eccbytes) { - printk(KERN_WARNING "ecc parameters not supplied\n"); - goto fail; - } - - m = fls(1+8*eccsize); - t = (eccbytes*8)/m; - - nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); - if (!nbc) - goto fail; - - nbc->bch = init_bch(m, t, 0); - if (!nbc->bch) - goto fail; - - /* verify that eccbytes has the expected value */ - if (nbc->bch->ecc_bytes != eccbytes) { - printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); - goto fail; - } - - eccsteps = mtd->writesize/eccsize; - - /* if no ecc placement scheme was provided, build one */ - if (!layout) { - - /* handle large page devices only */ - if (mtd->oobsize < 64) { - printk(KERN_WARNING "must provide an oob scheme for " - "oobsize %d\n", mtd->oobsize); - goto fail; - } - - layout = &nbc->ecclayout; - layout->eccbytes = eccsteps*eccbytes; - - /* reserve 2 bytes for bad block marker */ - if (layout->eccbytes+2 > mtd->oobsize) { - printk(KERN_WARNING "no suitable oob scheme available " - "for oobsize %d eccbytes %u\n", mtd->oobsize, - eccbytes); - goto fail; - } - /* put ecc bytes at oob tail */ - for (i = 0; i < layout->eccbytes; i++) - layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; - - layout->oobfree[0].offset = 2; - layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; - - nand->ecc.layout = layout; - } - - /* sanity checks */ - if (8*(eccsize+eccbytes) >= (1 << m)) { - printk(KERN_WARNING "eccsize %u is too large\n", eccsize); - goto fail; - } - if (layout->eccbytes != (eccsteps*eccbytes)) { - printk(KERN_WARNING "invalid ecc layout\n"); - goto fail; - } - - nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); - nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); - if (!nbc->eccmask || !nbc->errloc) - goto fail; - /* - * compute and store the inverted ecc of an erased ecc block - */ - erased_page = kmalloc(eccsize, GFP_KERNEL); - if (!erased_page) - goto fail; - - memset(erased_page, 0xff, eccsize); - memset(nbc->eccmask, 0, eccbytes); - encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); - kfree(erased_page); - - for (i = 0; i < eccbytes; i++) - nbc->eccmask[i] ^= 0xff; - - if (!eccstrength) - nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); - - return nbc; -fail: - nand_bch_free(nbc); - return NULL; -} - -/** - * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources - * @nbc: NAND BCH control structure - */ -void nand_bch_free(struct nand_bch_control *nbc) -{ - if (nbc) { - free_bch(nbc->bch); - kfree(nbc->errloc); - kfree(nbc->eccmask); - kfree(nbc); - } -} diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c deleted file mode 100644 index 05e55fce9a..0000000000 --- a/drivers/mtd/nand/nand_ecc.c +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * This file contains an ECC algorithm from Toshiba that detects and - * corrects 1 bit errors in a 256 byte block of data. - * - * drivers/mtd/nand/nand_ecc.c - * - * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) - * Toshiba America Electronics Components, Inc. - * - * Copyright (C) 2006 Thomas Gleixner - * - * As a special exception, if other files instantiate templates or use - * macros or inline functions from these files, or you compile these - * files and link them with other works to produce a work based on these - * files, these files do not by themselves cause the resulting work to be - * covered by the GNU General Public License. However the source code for - * these files must still be made available in accordance with section (3) - * of the GNU General Public License. - * - * This exception does not invalidate any other reasons why a work based on - * this file might be covered by the GNU General Public License. - */ - -#include - -#include -#include -#include - -/* - * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), - * only nand_correct_data() is needed - */ - -#if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC) -/* - * Pre-calculated 256-way 1 byte column parity - */ -static const u_char nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 -}; - -/** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block - * @mtd: MTD block structure - * @dat: raw data - * @ecc_code: buffer for ECC - */ -int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; - int i; - - /* Initialize variables */ - reg1 = reg2 = reg3 = 0; - - /* Build up column parity */ - for(i = 0; i < 256; i++) { - /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[*dat++]; - reg1 ^= (idx & 0x3f); - - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (uint8_t) i; - reg2 ^= ~((uint8_t) i); - } - } - - /* Create non-inverted ECC code from line parity */ - tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ - tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ - tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ - tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ - tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ - tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ - tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ - tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ - - tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ - tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ - tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ - tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ - tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ - tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ - tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ - tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ - - /* Calculate final ECC code */ - ecc_code[0] = ~tmp1; - ecc_code[1] = ~tmp2; - ecc_code[2] = ((~reg1) << 2) | 0x03; - - return 0; -} -#endif /* CONFIG_NAND_SPL */ - -static inline int countbits(uint32_t byte) -{ - int res = 0; - - for (;byte; byte >>= 1) - res += byte & 0x01; - return res; -} - -/** - * nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @mtd: MTD block structure - * @dat: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct a 1 bit error for 256 byte block - */ -int nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) -{ - uint8_t s0, s1, s2; - - s1 = calc_ecc[0] ^ read_ecc[0]; - s0 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; - if ((s0 | s1 | s2) == 0) - return 0; - - /* Check for a single bit error */ - if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && - ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && - ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { - - uint32_t byteoffs, bitnum; - - byteoffs = (s1 << 0) & 0x80; - byteoffs |= (s1 << 1) & 0x40; - byteoffs |= (s1 << 2) & 0x20; - byteoffs |= (s1 << 3) & 0x10; - - byteoffs |= (s0 >> 4) & 0x08; - byteoffs |= (s0 >> 3) & 0x04; - byteoffs |= (s0 >> 2) & 0x02; - byteoffs |= (s0 >> 1) & 0x01; - - bitnum = (s2 >> 5) & 0x04; - bitnum |= (s2 >> 4) & 0x02; - bitnum |= (s2 >> 3) & 0x01; - - dat[byteoffs] ^= (1 << bitnum); - - return 1; - } - - if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) - return 1; - - return -EBADMSG; -} diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c deleted file mode 100644 index 4009d64123..0000000000 --- a/drivers/mtd/nand/nand_ids.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) - * - * This program 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 -#include -#include - -#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS -#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - -#define SP_OPTIONS NAND_NEED_READRDY -#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) - -/* - * The chip ID list: - * name, device ID, page size, chip size in MiB, eraseblock size, options - * - * If page size and eraseblock size are 0, the sizes are taken from the - * extended chip ID. - */ -struct nand_flash_dev nand_flash_ids[] = { -#ifdef CONFIG_MTD_NAND_MUSEUM_IDS - LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS), - - LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS), -#endif - /* - * Some incompatible NAND chips share device ID's and so must be - * listed by full ID. We list them first so that we can easily identify - * the most specific match. - */ - {"TC58NVG0S3E 1G 3.3V 8-bit", - { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, - SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), - 2 }, - {"TC58NVG2S0F 4G 3.3V 8-bit", - { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, - {"TC58NVG2S0H 4G 3.3V 8-bit", - { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, - {"TC58NVG3S0F 8G 3.3V 8-bit", - { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, - SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, - {"TC58NVG5D2 32G 3.3V 8-bit", - { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, - SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, - {"TC58NVG6D2 64G 3.3V 8-bit", - { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, - SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, - {"SDTNRGAMA 64G 3.3V 8-bit", - { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, - SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, - {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", - { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, - SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, - NAND_ECC_INFO(40, SZ_1K), 4 }, - {"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit", - { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} }, - SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664, - NAND_ECC_INFO(56, SZ_1K), 1 }, - - LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), - - LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), - LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), - LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), - - LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), - - /* - * These are the new chips with large page size. Their page size and - * eraseblock size are determined from the extended ID bytes. - */ - - /* 512 Megabit */ - EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), - - /* 1 Gigabit */ - EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), - - /* 2 Gigabit */ - EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), - - /* 4 Gigabit */ - EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), - - /* 8 Gigabit */ - EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), - - /* 16 Gigabit */ - EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), - - /* 32 Gigabit */ - EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), - - /* 64 Gigabit */ - EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), - - /* 128 Gigabit */ - EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), - - /* 256 Gigabit */ - EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), - - /* 512 Gigabit */ - EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), - EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), - EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), - - {NULL} -}; - -/* Manufacturer IDs */ -struct nand_manufacturers nand_manuf_ids[] = { - {NAND_MFR_TOSHIBA, "Toshiba"}, - {NAND_MFR_SAMSUNG, "Samsung"}, - {NAND_MFR_FUJITSU, "Fujitsu"}, - {NAND_MFR_NATIONAL, "National"}, - {NAND_MFR_RENESAS, "Renesas"}, - {NAND_MFR_STMICRO, "ST Micro"}, - {NAND_MFR_HYNIX, "Hynix"}, - {NAND_MFR_MICRON, "Micron"}, - {NAND_MFR_AMD, "AMD/Spansion"}, - {NAND_MFR_MACRONIX, "Macronix"}, - {NAND_MFR_EON, "Eon"}, - {NAND_MFR_SANDISK, "SanDisk"}, - {NAND_MFR_INTEL, "Intel"}, - {NAND_MFR_ATO, "ATO"}, - {0x0, "Unknown"} -}; - -EXPORT_SYMBOL(nand_manuf_ids); -EXPORT_SYMBOL(nand_flash_ids); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Thomas Gleixner "); -MODULE_DESCRIPTION("Nand device & manufacturer IDs"); diff --git a/drivers/mtd/nand/nand_plat.c b/drivers/mtd/nand/nand_plat.c deleted file mode 100644 index 335c3e3471..0000000000 --- a/drivers/mtd/nand/nand_plat.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Genericish driver for memory mapped NAND devices - * - * Copyright (c) 2006-2009 Analog Devices Inc. - * Licensed under the GPL-2 or later. - */ - -/* Your board must implement the following macros: - * NAND_PLAT_WRITE_CMD(chip, cmd) - * NAND_PLAT_WRITE_ADR(chip, cmd) - * NAND_PLAT_INIT() - * - * It may also implement the following: - * NAND_PLAT_DEV_READY(chip) - */ - -#include -#include -#ifdef NAND_PLAT_GPIO_DEV_READY -# include -# define NAND_PLAT_DEV_READY(chip) gpio_get_value(NAND_PLAT_GPIO_DEV_READY) -#endif - -#include - -static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *this = mtd_to_nand(mtd); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_CLE) - NAND_PLAT_WRITE_CMD(this, cmd); - else - NAND_PLAT_WRITE_ADR(this, cmd); -} - -#ifdef NAND_PLAT_DEV_READY -static int plat_dev_ready(struct mtd_info *mtd) -{ - return NAND_PLAT_DEV_READY((struct nand_chip *)mtd_to_nand(mtd)); -} -#else -# define plat_dev_ready NULL -#endif - -int board_nand_init(struct nand_chip *nand) -{ -#ifdef NAND_PLAT_GPIO_DEV_READY - gpio_request(NAND_PLAT_GPIO_DEV_READY, "nand-plat"); - gpio_direction_input(NAND_PLAT_GPIO_DEV_READY); -#endif - -#ifdef NAND_PLAT_INIT - NAND_PLAT_INIT(); -#endif - - nand->cmd_ctrl = plat_cmd_ctrl; - nand->dev_ready = plat_dev_ready; - nand->ecc.mode = NAND_ECC_SOFT; - - return 0; -} diff --git a/drivers/mtd/nand/nand_spl_load.c b/drivers/mtd/nand/nand_spl_load.c deleted file mode 100644 index ecd373e054..0000000000 --- a/drivers/mtd/nand/nand_spl_load.c +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2011 - * Heiko Schocher, DENX Software Engineering, hs@denx.de. - */ - -#include -#include - -/* - * The main entry for NAND booting. It's necessary that SDRAM is already - * configured and available since this code loads the main U-Boot image - * from NAND into SDRAM and starts it from there. - */ -void nand_boot(void) -{ - __attribute__((noreturn)) void (*uboot)(void); - - /* - * Load U-Boot image from NAND into RAM - */ - nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, - CONFIG_SYS_NAND_U_BOOT_SIZE, - (void *)CONFIG_SYS_NAND_U_BOOT_DST); - -#ifdef CONFIG_NAND_ENV_DST - nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, - (void *)CONFIG_NAND_ENV_DST); - -#ifdef CONFIG_ENV_OFFSET_REDUND - nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, - (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); -#endif -#endif - - /* - * Jump to U-Boot image - */ - uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; - (*uboot)(); -} diff --git a/drivers/mtd/nand/nand_spl_loaders.c b/drivers/mtd/nand/nand_spl_loaders.c deleted file mode 100644 index 177c12b581..0000000000 --- a/drivers/mtd/nand/nand_spl_loaders.c +++ /dev/null @@ -1,104 +0,0 @@ -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) -{ - unsigned int block, lastblock; - unsigned int page, page_offset; - - /* offs has to be aligned to a page address! */ - block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; - lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; - page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; - page_offset = offs % CONFIG_SYS_NAND_PAGE_SIZE; - - while (block <= lastblock) { - if (!nand_is_bad_block(block)) { - /* Skip bad blocks */ - while (page < CONFIG_SYS_NAND_PAGE_COUNT) { - nand_read_page(block, page, dst); - /* - * When offs is not aligned to page address the - * extra offset is copied to dst as well. Copy - * the image such that its first byte will be - * at the dst. - */ - if (unlikely(page_offset)) { - memmove(dst, dst + page_offset, - CONFIG_SYS_NAND_PAGE_SIZE); - dst = (void *)((int)dst - page_offset); - page_offset = 0; - } - dst += CONFIG_SYS_NAND_PAGE_SIZE; - page++; - } - - page = 0; - } else { - lastblock++; - } - - block++; - } - - return 0; -} - -#ifdef CONFIG_SPL_UBI -/* - * Temporary storage for non NAND page aligned and non NAND page sized - * reads. Note: This does not support runtime detected FLASH yet, but - * that should be reasonably easy to fix by making the buffer large - * enough :) - */ -static u8 scratch_buf[CONFIG_SYS_NAND_PAGE_SIZE]; - -/** - * nand_spl_read_block - Read data from physical eraseblock into a buffer - * @block: Number of the physical eraseblock - * @offset: Data offset from the start of @peb - * @len: Data size to read - * @dst: Address of the destination buffer - * - * This could be further optimized if we'd have a subpage read - * function in the simple code. On NAND which allows subpage reads - * this would spare quite some time to readout e.g. the VID header of - * UBI. - * - * Notes: - * @offset + @len are not allowed to be larger than a physical - * erase block. No sanity check done for simplicity reasons. - * - * To support runtime detected flash this needs to be extended by - * information about the actual flash geometry, but thats beyond the - * scope of this effort and for most applications where fast boot is - * required it is not an issue anyway. - */ -int nand_spl_read_block(int block, int offset, int len, void *dst) -{ - int page, read; - - /* Calculate the page number */ - page = offset / CONFIG_SYS_NAND_PAGE_SIZE; - - /* Offset to the start of a flash page */ - offset = offset % CONFIG_SYS_NAND_PAGE_SIZE; - - while (len) { - /* - * Non page aligned reads go to the scratch buffer. - * Page aligned reads go directly to the destination. - */ - if (offset || len < CONFIG_SYS_NAND_PAGE_SIZE) { - nand_read_page(block, page, scratch_buf); - read = min(len, CONFIG_SYS_NAND_PAGE_SIZE - offset); - memcpy(dst, scratch_buf + offset, read); - offset = 0; - } else { - nand_read_page(block, page, dst); - read = CONFIG_SYS_NAND_PAGE_SIZE; - } - page++; - len -= read; - dst += read; - } - return 0; -} -#endif diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c deleted file mode 100644 index 09e053541a..0000000000 --- a/drivers/mtd/nand/nand_spl_simple.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2006-2008 - * Stefan Roese, DENX Software Engineering, sr@denx.de. - */ - -#include -#include -#include -#include - -static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; -static struct mtd_info *mtd; -static struct nand_chip nand_chip; - -#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ - CONFIG_SYS_NAND_ECCSIZE) -#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) - - -#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) -/* - * NAND command for small page NAND devices (512) - */ -static int nand_command(int block, int page, uint32_t offs, - u8 cmd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; - - while (!this->dev_ready(mtd)) - ; - - /* Begin command latch cycle */ - this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - /* Set ALE and clear CLE to start address cycle */ - /* Column address */ - this->cmd_ctrl(mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE); - this->cmd_ctrl(mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */ - this->cmd_ctrl(mtd, (page_addr >> 8) & 0xff, - NAND_CTRL_ALE); /* A[24:17] */ -#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE - /* One more address cycle for devices > 32MiB */ - this->cmd_ctrl(mtd, (page_addr >> 16) & 0x0f, - NAND_CTRL_ALE); /* A[28:25] */ -#endif - /* Latch in address */ - this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Wait a while for the data to be ready - */ - while (!this->dev_ready(mtd)) - ; - - return 0; -} -#else -/* - * NAND command for large page NAND devices (2k) - */ -static int nand_command(int block, int page, uint32_t offs, - u8 cmd) -{ - struct nand_chip *this = mtd_to_nand(mtd); - int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; - void (*hwctrl)(struct mtd_info *mtd, int cmd, - unsigned int ctrl) = this->cmd_ctrl; - - while (!this->dev_ready(mtd)) - ; - - /* Emulate NAND_CMD_READOOB */ - if (cmd == NAND_CMD_READOOB) { - offs += CONFIG_SYS_NAND_PAGE_SIZE; - cmd = NAND_CMD_READ0; - } - - /* Shift the offset from byte addressing to word addressing. */ - if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) - offs >>= 1; - - /* Begin command latch cycle */ - hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); - /* Set ALE and clear CLE to start address cycle */ - /* Column address */ - hwctrl(mtd, offs & 0xff, - NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ - hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ - /* Row address */ - hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ - hwctrl(mtd, ((page_addr >> 8) & 0xff), - NAND_CTRL_ALE); /* A[27:20] */ -#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE - /* One more address cycle for devices > 128MiB */ - hwctrl(mtd, (page_addr >> 16) & 0x0f, - NAND_CTRL_ALE); /* A[31:28] */ -#endif - /* Latch in address */ - hwctrl(mtd, NAND_CMD_READSTART, - NAND_CTRL_CLE | NAND_CTRL_CHANGE); - hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* - * Wait a while for the data to be ready - */ - while (!this->dev_ready(mtd)) - ; - - return 0; -} -#endif - -static int nand_is_bad_block(int block) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char bb_data[2]; - - nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, - NAND_CMD_READOOB); - - /* - * Read one byte (or two if it's a 16 bit chip). - */ - if (this->options & NAND_BUSWIDTH_16) { - this->read_buf(mtd, bb_data, 2); - if (bb_data[0] != 0xff || bb_data[1] != 0xff) - return 1; - } else { - this->read_buf(mtd, bb_data, 1); - if (bb_data[0] != 0xff) - return 1; - } - - return 0; -} - -#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST) -static int nand_read_page(int block, int page, uchar *dst) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ecc_calc[ECCTOTAL]; - u_char ecc_code[ECCTOTAL]; - u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; - int i; - int eccsize = CONFIG_SYS_NAND_ECCSIZE; - int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = ECCSTEPS; - uint8_t *p = dst; - - nand_command(block, page, 0, NAND_CMD_READOOB); - this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); - nand_command(block, page, 0, NAND_CMD_READ0); - - /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < ECCTOTAL; i++) - ecc_code[i] = oob_data[nand_ecc_pos[i]]; - - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - this->ecc.hwctl(mtd, NAND_ECC_READ); - this->read_buf(mtd, p, eccsize); - this->ecc.calculate(mtd, p, &ecc_calc[i]); - this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - } - - return 0; -} -#else -static int nand_read_page(int block, int page, void *dst) -{ - struct nand_chip *this = mtd_to_nand(mtd); - u_char ecc_calc[ECCTOTAL]; - u_char ecc_code[ECCTOTAL]; - u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; - int i; - int eccsize = CONFIG_SYS_NAND_ECCSIZE; - int eccbytes = CONFIG_SYS_NAND_ECCBYTES; - int eccsteps = ECCSTEPS; - uint8_t *p = dst; - - nand_command(block, page, 0, NAND_CMD_READ0); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - if (this->ecc.mode != NAND_ECC_SOFT) - this->ecc.hwctl(mtd, NAND_ECC_READ); - this->read_buf(mtd, p, eccsize); - this->ecc.calculate(mtd, p, &ecc_calc[i]); - } - this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); - - /* Pick the ECC bytes out of the oob data */ - for (i = 0; i < ECCTOTAL; i++) - ecc_code[i] = oob_data[nand_ecc_pos[i]]; - - eccsteps = ECCSTEPS; - p = dst; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - /* No chance to do something with the possible error message - * from correct_data(). We just hope that all possible errors - * are corrected by this routine. - */ - this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - } - - return 0; -} -#endif - -/* nand_init() - initialize data to make nand usable by SPL */ -void nand_init(void) -{ - /* - * Init board specific nand support - */ - mtd = nand_to_mtd(&nand_chip); - nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = - (void __iomem *)CONFIG_SYS_NAND_BASE; - board_nand_init(&nand_chip); - -#ifdef CONFIG_SPL_NAND_SOFTECC - if (nand_chip.ecc.mode == NAND_ECC_SOFT) { - nand_chip.ecc.calculate = nand_calculate_ecc; - nand_chip.ecc.correct = nand_correct_data; - } -#endif - - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, 0); -} - -/* Unselect after operation */ -void nand_deselect(void) -{ - if (nand_chip.select_chip) - nand_chip.select_chip(mtd, -1); -} - -#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c deleted file mode 100644 index c0545a4fb1..0000000000 --- a/drivers/mtd/nand/nand_timings.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2014 Free Electrons - * - * Author: Boris BREZILLON - * - * This program 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 -#include -#include - -static const struct nand_data_interface onfi_sdr_timings[] = { - /* Mode 0 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 20000, - .tALS_min = 50000, - .tAR_min = 25000, - .tCEA_max = 100000, - .tCEH_min = 20000, - .tCH_min = 20000, - .tCHZ_max = 100000, - .tCLH_min = 20000, - .tCLR_min = 20000, - .tCLS_min = 50000, - .tCOH_min = 0, - .tCS_min = 70000, - .tDH_min = 20000, - .tDS_min = 40000, - .tFEAT_max = 1000000, - .tIR_min = 10000, - .tITC_max = 1000000, - .tRC_min = 100000, - .tREA_max = 40000, - .tREH_min = 30000, - .tRHOH_min = 0, - .tRHW_min = 200000, - .tRHZ_max = 200000, - .tRLOH_min = 0, - .tRP_min = 50000, - .tRR_min = 40000, - .tRST_max = 250000000000ULL, - .tWB_max = 200000, - .tWC_min = 100000, - .tWH_min = 30000, - .tWHR_min = 120000, - .tWP_min = 50000, - .tWW_min = 100000, - }, - }, - /* Mode 1 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 10000, - .tALS_min = 25000, - .tAR_min = 10000, - .tCEA_max = 45000, - .tCEH_min = 20000, - .tCH_min = 10000, - .tCHZ_max = 50000, - .tCLH_min = 10000, - .tCLR_min = 10000, - .tCLS_min = 25000, - .tCOH_min = 15000, - .tCS_min = 35000, - .tDH_min = 10000, - .tDS_min = 20000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 50000, - .tREA_max = 30000, - .tREH_min = 15000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRP_min = 25000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 45000, - .tWH_min = 15000, - .tWHR_min = 80000, - .tWP_min = 25000, - .tWW_min = 100000, - }, - }, - /* Mode 2 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 10000, - .tALS_min = 15000, - .tAR_min = 10000, - .tCEA_max = 30000, - .tCEH_min = 20000, - .tCH_min = 10000, - .tCHZ_max = 50000, - .tCLH_min = 10000, - .tCLR_min = 10000, - .tCLS_min = 15000, - .tCOH_min = 15000, - .tCS_min = 25000, - .tDH_min = 5000, - .tDS_min = 15000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 35000, - .tREA_max = 25000, - .tREH_min = 15000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tRP_min = 17000, - .tWC_min = 35000, - .tWH_min = 15000, - .tWHR_min = 80000, - .tWP_min = 17000, - .tWW_min = 100000, - }, - }, - /* Mode 3 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 50000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 25000, - .tDH_min = 5000, - .tDS_min = 10000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 30000, - .tREA_max = 20000, - .tREH_min = 10000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 0, - .tRP_min = 15000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 30000, - .tWH_min = 10000, - .tWHR_min = 80000, - .tWP_min = 15000, - .tWW_min = 100000, - }, - }, - /* Mode 4 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 30000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 20000, - .tDH_min = 5000, - .tDS_min = 10000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 25000, - .tREA_max = 20000, - .tREH_min = 10000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 5000, - .tRP_min = 12000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 25000, - .tWH_min = 10000, - .tWHR_min = 80000, - .tWP_min = 12000, - .tWW_min = 100000, - }, - }, - /* Mode 5 */ - { - .type = NAND_SDR_IFACE, - .timings.sdr = { - .tCCS_min = 500000, - .tR_max = 200000000, - .tADL_min = 400000, - .tALH_min = 5000, - .tALS_min = 10000, - .tAR_min = 10000, - .tCEA_max = 25000, - .tCEH_min = 20000, - .tCH_min = 5000, - .tCHZ_max = 30000, - .tCLH_min = 5000, - .tCLR_min = 10000, - .tCLS_min = 10000, - .tCOH_min = 15000, - .tCS_min = 15000, - .tDH_min = 5000, - .tDS_min = 7000, - .tFEAT_max = 1000000, - .tIR_min = 0, - .tITC_max = 1000000, - .tRC_min = 20000, - .tREA_max = 16000, - .tREH_min = 7000, - .tRHOH_min = 15000, - .tRHW_min = 100000, - .tRHZ_max = 100000, - .tRLOH_min = 5000, - .tRP_min = 10000, - .tRR_min = 20000, - .tRST_max = 500000000, - .tWB_max = 100000, - .tWC_min = 20000, - .tWH_min = 7000, - .tWHR_min = 80000, - .tWP_min = 10000, - .tWW_min = 100000, - }, - }, -}; - -/** - * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND - * timings according to the given ONFI timing mode - * @mode: ONFI timing mode - */ -const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) -{ - if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) - return ERR_PTR(-EINVAL); - - return &onfi_sdr_timings[mode].timings.sdr; -} -EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); - -/** - * onfi_init_data_interface - [NAND Interface] Initialize a data interface from - * given ONFI mode - * @iface: The data interface to be initialized - * @mode: The ONFI timing mode - */ -int onfi_init_data_interface(struct nand_chip *chip, - struct nand_data_interface *iface, - enum nand_data_interface_type type, - int timing_mode) -{ - if (type != NAND_SDR_IFACE) - return -EINVAL; - - if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) - return -EINVAL; - - *iface = onfi_sdr_timings[timing_mode]; - - /* - * Initialize timings that cannot be deduced from timing mode: - * tR, tPROG, tCCS, ... - * These information are part of the ONFI parameter page. - */ - if (chip->onfi_version) { - struct nand_onfi_params *params = &chip->onfi_params; - struct nand_sdr_timings *timings = &iface->timings.sdr; - - /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); - timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); - timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); - - /* nanoseconds -> picoseconds */ - timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); - } - - return 0; -} -EXPORT_SYMBOL(onfi_init_data_interface); - -/** - * nand_get_default_data_interface - [NAND Interface] Retrieve NAND - * data interface for mode 0. This is used as default timing after - * reset. - */ -const struct nand_data_interface *nand_get_default_data_interface(void) -{ - return &onfi_sdr_timings[0]; -} -EXPORT_SYMBOL(nand_get_default_data_interface); diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c deleted file mode 100644 index 1ded7aa920..0000000000 --- a/drivers/mtd/nand/nand_util.c +++ /dev/null @@ -1,904 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * drivers/mtd/nand/nand_util.c - * - * Copyright (C) 2006 by Weiss-Electronic GmbH. - * All rights reserved. - * - * @author: Guido Classen - * @descr: NAND Flash support - * @references: borrowed heavily from Linux mtd-utils code: - * flash_eraseall.c by Arcom Control System Ltd - * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) - * and Thomas Gleixner (tglx@linutronix.de) - * - * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by - * Artem Bityutskiy from mtd-utils - * - * Copyright 2010 Freescale Semiconductor - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -typedef struct erase_info erase_info_t; -typedef struct mtd_info mtd_info_t; - -/* support only for native endian JFFS2 */ -#define cpu_to_je16(x) (x) -#define cpu_to_je32(x) (x) - -/** - * nand_erase_opts: - erase NAND flash with support for various options - * (jffs2 formatting) - * - * @param mtd nand mtd instance to erase - * @param opts options, @see struct nand_erase_options - * @return 0 in case of success - * - * This code is ported from flash_eraseall.c from Linux mtd utils by - * Arcom Control System Ltd. - */ -int nand_erase_opts(struct mtd_info *mtd, - const nand_erase_options_t *opts) -{ - struct jffs2_unknown_node cleanmarker; - erase_info_t erase; - unsigned long erase_length, erased_length; /* in blocks */ - int result; - int percent_complete = -1; - const char *mtd_device = mtd->name; - struct mtd_oob_ops oob_opts; - struct nand_chip *chip = mtd_to_nand(mtd); - - if ((opts->offset & (mtd->erasesize - 1)) != 0) { - printf("Attempt to erase non block-aligned data\n"); - return -1; - } - - memset(&erase, 0, sizeof(erase)); - memset(&oob_opts, 0, sizeof(oob_opts)); - - erase.mtd = mtd; - erase.len = mtd->erasesize; - erase.addr = opts->offset; - erase_length = lldiv(opts->length + mtd->erasesize - 1, - mtd->erasesize); - - cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); - cleanmarker.totlen = cpu_to_je32(8); - - /* scrub option allows to erase badblock. To prevent internal - * check from erase() method, set block check method to dummy - * and disable bad block table while erasing. - */ - if (opts->scrub) { - erase.scrub = opts->scrub; - /* - * We don't need the bad block table anymore... - * after scrub, there are no bad blocks left! - */ - if (chip->bbt) { - kfree(chip->bbt); - } - chip->bbt = NULL; - chip->options &= ~NAND_BBT_SCANNED; - } - - for (erased_length = 0; - erased_length < erase_length; - erase.addr += mtd->erasesize) { - - WATCHDOG_RESET(); - - if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) { - puts("Size of erase exceeds limit\n"); - return -EFBIG; - } - if (!opts->scrub) { - int ret = mtd_block_isbad(mtd, erase.addr); - if (ret > 0) { - if (!opts->quiet) - printf("\rSkipping bad block at " - "0x%08llx " - " \n", - erase.addr); - - if (!opts->spread) - erased_length++; - - continue; - - } else if (ret < 0) { - printf("\n%s: MTD get bad block failed: %d\n", - mtd_device, - ret); - return -1; - } - } - - erased_length++; - - result = mtd_erase(mtd, &erase); - if (result != 0) { - printf("\n%s: MTD Erase failure: %d\n", - mtd_device, result); - continue; - } - - /* format for JFFS2 ? */ - if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { - struct mtd_oob_ops ops; - ops.ooblen = 8; - ops.datbuf = NULL; - ops.oobbuf = (uint8_t *)&cleanmarker; - ops.ooboffs = 0; - ops.mode = MTD_OPS_AUTO_OOB; - - result = mtd_write_oob(mtd, erase.addr, &ops); - if (result != 0) { - printf("\n%s: MTD writeoob failure: %d\n", - mtd_device, result); - continue; - } - } - - if (!opts->quiet) { - unsigned long long n = erased_length * 100ULL; - int percent; - - do_div(n, erase_length); - percent = (int)n; - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - percent_complete = percent; - - printf("\rErasing at 0x%llx -- %3d%% complete.", - erase.addr, percent); - - if (opts->jffs2 && result == 0) - printf(" Cleanmarker written at 0x%llx.", - erase.addr); - } - } - } - if (!opts->quiet) - printf("\n"); - - return 0; -} - -#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK - -#define NAND_CMD_LOCK_TIGHT 0x2c -#define NAND_CMD_LOCK_STATUS 0x7a - -/****************************************************************************** - * Support for locking / unlocking operations of some NAND devices - *****************************************************************************/ - -/** - * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT - * state - * - * @param mtd nand mtd instance - * @param tight bring device in lock tight mode - * - * @return 0 on success, -1 in case of error - * - * The lock / lock-tight command only applies to the whole chip. To get some - * parts of the chip lock and others unlocked use the following sequence: - * - * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) - * - Call nand_unlock() once for each consecutive area to be unlocked - * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) - * - * If the device is in lock-tight state software can't change the - * current active lock/unlock state of all pages. nand_lock() / nand_unlock() - * calls will fail. It is only posible to leave lock-tight state by - * an hardware signal (low pulse on _WP pin) or by power down. - */ -int nand_lock(struct mtd_info *mtd, int tight) -{ - int ret = 0; - int status; - struct nand_chip *chip = mtd_to_nand(mtd); - - /* select the NAND device */ - chip->select_chip(mtd, 0); - - /* check the Lock Tight Status */ - chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0); - if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { - printf("nand_lock: Device is locked tight!\n"); - ret = -1; - goto out; - } - - chip->cmdfunc(mtd, - (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), - -1, -1); - - /* call wait ready function */ - status = chip->waitfunc(mtd, chip); - - /* see if device thinks it succeeded */ - if (status & 0x01) { - ret = -1; - } - - out: - /* de-select the NAND device */ - chip->select_chip(mtd, -1); - return ret; -} - -/** - * nand_get_lock_status: - query current lock state from one page of NAND - * flash - * - * @param mtd nand mtd instance - * @param offset page address to query (must be page-aligned!) - * - * @return -1 in case of error - * >0 lock status: - * bitfield with the following combinations: - * NAND_LOCK_STATUS_TIGHT: page in tight state - * NAND_LOCK_STATUS_UNLOCK: page unlocked - * - */ -int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) -{ - int ret = 0; - int chipnr; - int page; - struct nand_chip *chip = mtd_to_nand(mtd); - - /* select the NAND device */ - chipnr = (int)(offset >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - - if ((offset & (mtd->writesize - 1)) != 0) { - printf("nand_get_lock_status: " - "Start address must be beginning of " - "nand page!\n"); - ret = -1; - goto out; - } - - /* check the Lock Status */ - page = (int)(offset >> chip->page_shift); - chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); - - ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT - | NAND_LOCK_STATUS_UNLOCK); - - out: - /* de-select the NAND device */ - chip->select_chip(mtd, -1); - return ret; -} - -/** - * nand_unlock: - Unlock area of NAND pages - * only one consecutive area can be unlocked at one time! - * - * @param mtd nand mtd instance - * @param start start byte address - * @param length number of bytes to unlock (must be a multiple of - * page size mtd->writesize) - * @param allexcept if set, unlock everything not selected - * - * @return 0 on success, -1 in case of error - */ -int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, - int allexcept) -{ - int ret = 0; - int chipnr; - int status; - int page; - struct nand_chip *chip = mtd_to_nand(mtd); - - debug("nand_unlock%s: start: %08llx, length: %zd!\n", - allexcept ? " (allexcept)" : "", start, length); - - /* select the NAND device */ - chipnr = (int)(start >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* check the WP bit */ - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { - printf("nand_unlock: Device is write protected!\n"); - ret = -1; - goto out; - } - - /* check the Lock Tight Status */ - page = (int)(start >> chip->page_shift); - chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); - if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { - printf("nand_unlock: Device is locked tight!\n"); - ret = -1; - goto out; - } - - if ((start & (mtd->erasesize - 1)) != 0) { - printf("nand_unlock: Start address must be beginning of " - "nand block!\n"); - ret = -1; - goto out; - } - - if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { - printf("nand_unlock: Length must be a multiple of nand block " - "size %08x!\n", mtd->erasesize); - ret = -1; - goto out; - } - - /* - * Set length so that the last address is set to the - * starting address of the last block - */ - length -= mtd->erasesize; - - /* submit address of first page to unlock */ - chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); - - /* submit ADDRESS of LAST page to unlock */ - page += (int)(length >> chip->page_shift); - - /* - * Page addresses for unlocking are supposed to be block-aligned. - * At least some NAND chips use the low bit to indicate that the - * page range should be inverted. - */ - if (allexcept) - page |= 1; - - chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); - - /* call wait ready function */ - status = chip->waitfunc(mtd, chip); - /* see if device thinks it succeeded */ - if (status & 0x01) { - /* there was an error */ - ret = -1; - goto out; - } - - out: - /* de-select the NAND device */ - chip->select_chip(mtd, -1); - return ret; -} -#endif - -/** - * check_skip_len - * - * Check if there are any bad blocks, and whether length including bad - * blocks fits into device - * - * @param mtd nand mtd instance - * @param offset offset in flash - * @param length image length - * @param used length of flash needed for the requested length - * @return 0 if the image fits and there are no bad blocks - * 1 if the image fits, but there are bad blocks - * -1 if the image does not fit - */ -static int check_skip_len(struct mtd_info *mtd, loff_t offset, size_t length, - size_t *used) -{ - size_t len_excl_bad = 0; - int ret = 0; - - while (len_excl_bad < length) { - size_t block_len, block_off; - loff_t block_start; - - if (offset >= mtd->size) - return -1; - - block_start = offset & ~(loff_t)(mtd->erasesize - 1); - block_off = offset & (mtd->erasesize - 1); - block_len = mtd->erasesize - block_off; - - if (!nand_block_isbad(mtd, block_start)) - len_excl_bad += block_len; - else - ret = 1; - - offset += block_len; - *used += block_len; - } - - /* If the length is not a multiple of block_len, adjust. */ - if (len_excl_bad > length) - *used -= (len_excl_bad - length); - - return ret; -} - -#ifdef CONFIG_CMD_NAND_TRIMFFS -static size_t drop_ffs(const struct mtd_info *mtd, const u_char *buf, - const size_t *len) -{ - size_t l = *len; - ssize_t i; - - for (i = l - 1; i >= 0; i--) - if (buf[i] != 0xFF) - break; - - /* The resulting length must be aligned to the minimum flash I/O size */ - l = i + 1; - l = (l + mtd->writesize - 1) / mtd->writesize; - l *= mtd->writesize; - - /* - * since the input length may be unaligned, prevent access past the end - * of the buffer - */ - return min(l, *len); -} -#endif - -/** - * nand_verify_page_oob: - * - * Verify a page of NAND flash, including the OOB. - * Reads page of NAND and verifies the contents and OOB against the - * values in ops. - * - * @param mtd nand mtd instance - * @param ops MTD operations, including data to verify - * @param ofs offset in flash - * @return 0 in case of success - */ -int nand_verify_page_oob(struct mtd_info *mtd, struct mtd_oob_ops *ops, - loff_t ofs) -{ - int rval; - struct mtd_oob_ops vops; - size_t verlen = mtd->writesize + mtd->oobsize; - - memcpy(&vops, ops, sizeof(vops)); - - vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen); - - if (!vops.datbuf) - return -ENOMEM; - - vops.oobbuf = vops.datbuf + mtd->writesize; - - rval = mtd_read_oob(mtd, ofs, &vops); - if (!rval) - rval = memcmp(ops->datbuf, vops.datbuf, vops.len); - if (!rval) - rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen); - - free(vops.datbuf); - - return rval ? -EIO : 0; -} - -/** - * nand_verify: - * - * Verify a region of NAND flash. - * Reads NAND in page-sized chunks and verifies the contents against - * the contents of a buffer. The offset into the NAND must be - * page-aligned, and the function doesn't handle skipping bad blocks. - * - * @param mtd nand mtd instance - * @param ofs offset in flash - * @param len buffer length - * @param buf buffer to read from - * @return 0 in case of success - */ -int nand_verify(struct mtd_info *mtd, loff_t ofs, size_t len, u_char *buf) -{ - int rval = 0; - size_t verofs; - size_t verlen = mtd->writesize; - uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen); - - if (!verbuf) - return -ENOMEM; - - /* Read the NAND back in page-size groups to limit malloc size */ - for (verofs = ofs; verofs < ofs + len; - verofs += verlen, buf += verlen) { - verlen = min(mtd->writesize, (uint32_t)(ofs + len - verofs)); - rval = nand_read(mtd, verofs, &verlen, verbuf); - if (!rval || (rval == -EUCLEAN)) - rval = memcmp(buf, verbuf, verlen); - - if (rval) - break; - } - - free(verbuf); - - return rval ? -EIO : 0; -} - - - -/** - * nand_write_skip_bad: - * - * Write image to NAND flash. - * Blocks that are marked bad are skipped and the is written to the next - * block instead as long as the image is short enough to fit even after - * skipping the bad blocks. Due to bad blocks we may not be able to - * perform the requested write. In the case where the write would - * extend beyond the end of the NAND device, both length and actual (if - * not NULL) are set to 0. In the case where the write would extend - * beyond the limit we are passed, length is set to 0 and actual is set - * to the required length. - * - * @param mtd nand mtd instance - * @param offset offset in flash - * @param length buffer length - * @param actual set to size required to write length worth of - * buffer or 0 on error, if not NULL - * @param lim maximum size that actual may be in order to not - * exceed the buffer - * @param buffer buffer to read from - * @param flags flags modifying the behaviour of the write to NAND - * @return 0 in case of success - */ -int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, - size_t *actual, loff_t lim, u_char *buffer, int flags) -{ - int rval = 0, blocksize; - size_t left_to_write = *length; - size_t used_for_write = 0; - u_char *p_buffer = buffer; - int need_skip; - - if (actual) - *actual = 0; - - blocksize = mtd->erasesize; - - /* - * nand_write() handles unaligned, partial page writes. - * - * We allow length to be unaligned, for convenience in - * using the $filesize variable. - * - * However, starting at an unaligned offset makes the - * semantics of bad block skipping ambiguous (really, - * you should only start a block skipping access at a - * partition boundary). So don't try to handle that. - */ - if ((offset & (mtd->writesize - 1)) != 0) { - printf("Attempt to write non page-aligned data\n"); - *length = 0; - return -EINVAL; - } - - need_skip = check_skip_len(mtd, offset, *length, &used_for_write); - - if (actual) - *actual = used_for_write; - - if (need_skip < 0) { - printf("Attempt to write outside the flash area\n"); - *length = 0; - return -EINVAL; - } - - if (used_for_write > lim) { - puts("Size of write exceeds partition or device limit\n"); - *length = 0; - return -EFBIG; - } - - if (!need_skip && !(flags & WITH_DROP_FFS)) { - rval = nand_write(mtd, offset, length, buffer); - - if ((flags & WITH_WR_VERIFY) && !rval) - rval = nand_verify(mtd, offset, *length, buffer); - - if (rval == 0) - return 0; - - *length = 0; - printf("NAND write to offset %llx failed %d\n", - offset, rval); - return rval; - } - - while (left_to_write > 0) { - size_t block_offset = offset & (mtd->erasesize - 1); - size_t write_size, truncated_write_size; - - WATCHDOG_RESET(); - - if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) { - printf("Skip bad block 0x%08llx\n", - offset & ~(mtd->erasesize - 1)); - offset += mtd->erasesize - block_offset; - continue; - } - - if (left_to_write < (blocksize - block_offset)) - write_size = left_to_write; - else - write_size = blocksize - block_offset; - - truncated_write_size = write_size; -#ifdef CONFIG_CMD_NAND_TRIMFFS - if (flags & WITH_DROP_FFS) - truncated_write_size = drop_ffs(mtd, p_buffer, - &write_size); -#endif - - rval = nand_write(mtd, offset, &truncated_write_size, - p_buffer); - - if ((flags & WITH_WR_VERIFY) && !rval) - rval = nand_verify(mtd, offset, - truncated_write_size, p_buffer); - - offset += write_size; - p_buffer += write_size; - - if (rval != 0) { - printf("NAND write to offset %llx failed %d\n", - offset, rval); - *length -= left_to_write; - return rval; - } - - left_to_write -= write_size; - } - - return 0; -} - -/** - * nand_read_skip_bad: - * - * Read image from NAND flash. - * Blocks that are marked bad are skipped and the next block is read - * instead as long as the image is short enough to fit even after - * skipping the bad blocks. Due to bad blocks we may not be able to - * perform the requested read. In the case where the read would extend - * beyond the end of the NAND device, both length and actual (if not - * NULL) are set to 0. In the case where the read would extend beyond - * the limit we are passed, length is set to 0 and actual is set to the - * required length. - * - * @param mtd nand mtd instance - * @param offset offset in flash - * @param length buffer length, on return holds number of read bytes - * @param actual set to size required to read length worth of buffer or 0 - * on error, if not NULL - * @param lim maximum size that actual may be in order to not exceed the - * buffer - * @param buffer buffer to write to - * @return 0 in case of success - */ -int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, - size_t *actual, loff_t lim, u_char *buffer) -{ - int rval; - size_t left_to_read = *length; - size_t used_for_read = 0; - u_char *p_buffer = buffer; - int need_skip; - - if ((offset & (mtd->writesize - 1)) != 0) { - printf("Attempt to read non page-aligned data\n"); - *length = 0; - if (actual) - *actual = 0; - return -EINVAL; - } - - need_skip = check_skip_len(mtd, offset, *length, &used_for_read); - - if (actual) - *actual = used_for_read; - - if (need_skip < 0) { - printf("Attempt to read outside the flash area\n"); - *length = 0; - return -EINVAL; - } - - if (used_for_read > lim) { - puts("Size of read exceeds partition or device limit\n"); - *length = 0; - return -EFBIG; - } - - if (!need_skip) { - rval = nand_read(mtd, offset, length, buffer); - if (!rval || rval == -EUCLEAN) - return 0; - - *length = 0; - printf("NAND read from offset %llx failed %d\n", - offset, rval); - return rval; - } - - while (left_to_read > 0) { - size_t block_offset = offset & (mtd->erasesize - 1); - size_t read_length; - - WATCHDOG_RESET(); - - if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) { - printf("Skipping bad block 0x%08llx\n", - offset & ~(mtd->erasesize - 1)); - offset += mtd->erasesize - block_offset; - continue; - } - - if (left_to_read < (mtd->erasesize - block_offset)) - read_length = left_to_read; - else - read_length = mtd->erasesize - block_offset; - - rval = nand_read(mtd, offset, &read_length, p_buffer); - if (rval && rval != -EUCLEAN) { - printf("NAND read from offset %llx failed %d\n", - offset, rval); - *length -= left_to_read; - return rval; - } - - left_to_read -= read_length; - offset += read_length; - p_buffer += read_length; - } - - return 0; -} - -#ifdef CONFIG_CMD_NAND_TORTURE - -/** - * check_pattern: - * - * Check if buffer contains only a certain byte pattern. - * - * @param buf buffer to check - * @param patt the pattern to check - * @param size buffer size in bytes - * @return 1 if there are only patt bytes in buf - * 0 if something else was found - */ -static int check_pattern(const u_char *buf, u_char patt, int size) -{ - int i; - - for (i = 0; i < size; i++) - if (buf[i] != patt) - return 0; - return 1; -} - -/** - * nand_torture: - * - * Torture a block of NAND flash. - * This is useful to determine if a block that caused a write error is still - * good or should be marked as bad. - * - * @param mtd nand mtd instance - * @param offset offset in flash - * @return 0 if the block is still good - */ -int nand_torture(struct mtd_info *mtd, loff_t offset) -{ - u_char patterns[] = {0xa5, 0x5a, 0x00}; - struct erase_info instr = { - .mtd = mtd, - .addr = offset, - .len = mtd->erasesize, - }; - size_t retlen; - int err, ret = -1, i, patt_count; - u_char *buf; - - if ((offset & (mtd->erasesize - 1)) != 0) { - puts("Attempt to torture a block at a non block-aligned offset\n"); - return -EINVAL; - } - - if (offset + mtd->erasesize > mtd->size) { - puts("Attempt to torture a block outside the flash area\n"); - return -EINVAL; - } - - patt_count = ARRAY_SIZE(patterns); - - buf = malloc_cache_aligned(mtd->erasesize); - if (buf == NULL) { - puts("Out of memory for erase block buffer\n"); - return -ENOMEM; - } - - for (i = 0; i < patt_count; i++) { - err = mtd_erase(mtd, &instr); - if (err) { - printf("%s: erase() failed for block at 0x%llx: %d\n", - mtd->name, instr.addr, err); - goto out; - } - - /* Make sure the block contains only 0xff bytes */ - err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf); - if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) { - printf("%s: read() failed for block at 0x%llx: %d\n", - mtd->name, instr.addr, err); - goto out; - } - - err = check_pattern(buf, 0xff, mtd->erasesize); - if (!err) { - printf("Erased block at 0x%llx, but a non-0xff byte was found\n", - offset); - ret = -EIO; - goto out; - } - - /* Write a pattern and check it */ - memset(buf, patterns[i], mtd->erasesize); - err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf); - if (err || retlen != mtd->erasesize) { - printf("%s: write() failed for block at 0x%llx: %d\n", - mtd->name, instr.addr, err); - goto out; - } - - err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf); - if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) { - printf("%s: read() failed for block at 0x%llx: %d\n", - mtd->name, instr.addr, err); - goto out; - } - - err = check_pattern(buf, patterns[i], mtd->erasesize); - if (!err) { - printf("Pattern 0x%.2x checking failed for block at " - "0x%llx\n", patterns[i], offset); - ret = -EIO; - goto out; - } - } - - ret = 0; - -out: - free(buf); - return ret; -} - -#endif diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c deleted file mode 100644 index 35c6dd1f1b..0000000000 --- a/drivers/mtd/nand/omap_elm.c +++ /dev/null @@ -1,193 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2010-2011 Texas Instruments, - * Mansoor Ahamed - * - * BCH Error Location Module (ELM) support. - * - * NOTE: - * 1. Supports only continuous mode. Dont see need for page mode in uboot - * 2. Supports only syndrome polynomial 0. i.e. poly local variable is - * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial - * sets in uboot - */ - -#include -#include -#include -#include -#include - -#define DRIVER_NAME "omap-elm" -#define ELM_DEFAULT_POLY (0) - -struct elm *elm_cfg; - -/** - * elm_load_syndromes - Load BCH syndromes based on bch_type selection - * @syndrome: BCH syndrome - * @bch_type: BCH4/BCH8/BCH16 - * @poly: Syndrome Polynomial set to use - */ -static void elm_load_syndromes(u8 *syndrome, enum bch_level bch_type, u8 poly) -{ - u32 *ptr; - u32 val; - - /* reg 0 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; - val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | - (syndrome[3] << 24); - writel(val, ptr); - /* reg 1 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; - val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | - (syndrome[7] << 24); - writel(val, ptr); - - if (bch_type == BCH_8_BIT || bch_type == BCH_16_BIT) { - /* reg 2 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; - val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | - (syndrome[11] << 24); - writel(val, ptr); - /* reg 3 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; - val = syndrome[12] | (syndrome[13] << 8) | - (syndrome[14] << 16) | (syndrome[15] << 24); - writel(val, ptr); - } - - if (bch_type == BCH_16_BIT) { - /* reg 4 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; - val = syndrome[16] | (syndrome[17] << 8) | - (syndrome[18] << 16) | (syndrome[19] << 24); - writel(val, ptr); - - /* reg 5 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; - val = syndrome[20] | (syndrome[21] << 8) | - (syndrome[22] << 16) | (syndrome[23] << 24); - writel(val, ptr); - - /* reg 6 */ - ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; - val = syndrome[24] | (syndrome[25] << 8) | - (syndrome[26] << 16) | (syndrome[27] << 24); - writel(val, ptr); - } -} - -/** - * elm_check_errors - Check for BCH errors and return error locations - * @syndrome: BCH syndrome - * @bch_type: BCH4/BCH8/BCH16 - * @error_count: Returns number of errrors in the syndrome - * @error_locations: Returns error locations (in decimal) in this array - * - * Check the provided syndrome for BCH errors and return error count - * and locations in the array passed. Returns -1 if error is not correctable, - * else returns 0 - */ -int elm_check_error(u8 *syndrome, enum bch_level bch_type, u32 *error_count, - u32 *error_locations) -{ - u8 poly = ELM_DEFAULT_POLY; - s8 i; - u32 location_status; - - elm_load_syndromes(syndrome, bch_type, poly); - - /* start processing */ - writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) - | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), - &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); - - /* wait for processing to complete */ - while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) - ; - /* clear status */ - writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), - &elm_cfg->irqstatus); - - /* check if correctable */ - location_status = readl(&elm_cfg->error_location[poly].location_status); - if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) { - printf("%s: uncorrectable ECC errors\n", DRIVER_NAME); - return -EBADMSG; - } - - /* get error count */ - *error_count = readl(&elm_cfg->error_location[poly].location_status) & - ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; - - for (i = 0; i < *error_count; i++) { - error_locations[i] = - readl(&elm_cfg->error_location[poly].error_location_x[i]); - } - - return 0; -} - - -/** - * elm_config - Configure ELM module - * @level: 4 / 8 / 16 bit BCH - * - * Configure ELM module based on BCH level. - * Set mode as continuous mode. - * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. - * Also, the mode is set only for syndrome 0 - */ -int elm_config(enum bch_level level) -{ - u32 val; - u8 poly = ELM_DEFAULT_POLY; - u32 buffer_size = 0x7FF; - - /* config size and level */ - val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; - val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & - ELM_LOCATION_CONFIG_ECC_SIZE_MASK); - writel(val, &elm_cfg->location_config); - - /* config continous mode */ - /* enable interrupt generation for syndrome polynomial set */ - writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), - &elm_cfg->irqenable); - /* set continuous mode for the syndrome polynomial set */ - writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), - &elm_cfg->page_ctrl); - - return 0; -} - -/** - * elm_reset - Do a soft reset of ELM - * - * Perform a soft reset of ELM and return after reset is done. - */ -void elm_reset(void) -{ - /* initiate reset */ - writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), - &elm_cfg->sysconfig); - - /* wait for reset complete and normal operation */ - while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != - ELM_SYSSTATUS_RESETDONE) - ; -} - -/** - * elm_init - Initialize ELM module - * - * Initialize ELM support. Currently it does only base address init - * and ELM reset. - */ -void elm_init(void) -{ - elm_cfg = (struct elm *)ELM_BASE; - elm_reset(); -} diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c deleted file mode 100644 index 6a050501b0..0000000000 --- a/drivers/mtd/nand/omap_gpmc.c +++ /dev/null @@ -1,1037 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2004-2008 Texas Instruments, - * Rohit Choraria - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BADBLOCK_MARKER_LENGTH 2 -#define SECTOR_BYTES 512 -#define ECCCLEAR (0x1 << 8) -#define ECCRESULTREG1 (0x1 << 0) -/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ -#define BCH4_BIT_PAD 4 - -#ifdef CONFIG_BCH -static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, - 0x97, 0x79, 0xe5, 0x24, 0xb5}; -#endif -static uint8_t cs_next; -static __maybe_unused struct nand_ecclayout omap_ecclayout; - -#if defined(CONFIG_NAND_OMAP_GPMC_WSCFG) -static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE] = - { CONFIG_NAND_OMAP_GPMC_WSCFG }; -#else -/* wscfg is preset to zero since its a static variable */ -static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE]; -#endif - -/* - * Driver configurations - */ -struct omap_nand_info { - struct bch_control *control; - enum omap_ecc ecc_scheme; - uint8_t cs; - uint8_t ws; /* wait status pin (0,1) */ -}; - -/* We are wasting a bit of memory but al least we are safe */ -static struct omap_nand_info omap_nand_info[GPMC_MAX_CS]; - -/* - * omap_nand_hwcontrol - Set the address pointers corretly for the - * following address/data/command operation - */ -static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, - uint32_t ctrl) -{ - register struct nand_chip *this = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(this); - int cs = info->cs; - - /* - * Point the IO_ADDR to DATA and ADDRESS registers instead - * of chip address - */ - switch (ctrl) { - case NAND_CTRL_CHANGE | NAND_CTRL_CLE: - this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; - break; - case NAND_CTRL_CHANGE | NAND_CTRL_ALE: - this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr; - break; - case NAND_CTRL_CHANGE | NAND_NCE: - this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; - break; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -/* Check wait pin as dev ready indicator */ -static int omap_dev_ready(struct mtd_info *mtd) -{ - register struct nand_chip *this = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(this); - return gpmc_cfg->status & (1 << (8 + info->ws)); -} - -/* - * gen_true_ecc - This function will generate true ECC value, which - * can be used when correcting data read from NAND flash memory core - * - * @ecc_buf: buffer to store ecc code - * - * @return: re-formatted ECC value - */ -static uint32_t gen_true_ecc(uint8_t *ecc_buf) -{ - return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | - ((ecc_buf[2] & 0x0F) << 8); -} - -/* - * omap_correct_data - Compares the ecc read from nand spare area with ECC - * registers values and corrects one bit error if it has occurred - * Further details can be had from OMAP TRM and the following selected links: - * http://en.wikipedia.org/wiki/Hamming_code - * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf - * - * @mtd: MTD device structure - * @dat: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from ECC registers - * - * @return 0 if data is OK or corrected, else returns -1 - */ -static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - uint32_t orig_ecc, new_ecc, res, hm; - uint16_t parity_bits, byte; - uint8_t bit; - - /* Regenerate the orginal ECC */ - orig_ecc = gen_true_ecc(read_ecc); - new_ecc = gen_true_ecc(calc_ecc); - /* Get the XOR of real ecc */ - res = orig_ecc ^ new_ecc; - if (res) { - /* Get the hamming width */ - hm = hweight32(res); - /* Single bit errors can be corrected! */ - if (hm == 12) { - /* Correctable data! */ - parity_bits = res >> 16; - bit = (parity_bits & 0x7); - byte = (parity_bits >> 3) & 0x1FF; - /* Flip the bit to correct */ - dat[byte] ^= (0x1 << bit); - } else if (hm == 1) { - printf("Error: Ecc is wrong\n"); - /* ECC itself is corrupted */ - return 2; - } else { - /* - * hm distance != parity pairs OR one, could mean 2 bit - * error OR potentially be on a blank page.. - * orig_ecc: contains spare area data from nand flash. - * new_ecc: generated ecc while reading data area. - * Note: if the ecc = 0, all data bits from which it was - * generated are 0xFF. - * The 3 byte(24 bits) ecc is generated per 512byte - * chunk of a page. If orig_ecc(from spare area) - * is 0xFF && new_ecc(computed now from data area)=0x0, - * this means that data area is 0xFF and spare area is - * 0xFF. A sure sign of a erased page! - */ - if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) - return 0; - printf("Error: Bad compare! failed\n"); - /* detected 2 bit error */ - return -EBADMSG; - } - } - return 0; -} - -/* - * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write - * @mtd: MTD device structure - * @mode: Read/Write mode - */ -__maybe_unused -static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(nand); - unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0; - unsigned int ecc_algo = 0; - unsigned int bch_type = 0; - unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00; - u32 ecc_size_config_val = 0; - u32 ecc_config_val = 0; - int cs = info->cs; - - /* configure GPMC for specific ecc-scheme */ - switch (info->ecc_scheme) { - case OMAP_ECC_HAM1_CODE_SW: - return; - case OMAP_ECC_HAM1_CODE_HW: - ecc_algo = 0x0; - bch_type = 0x0; - bch_wrapmode = 0x00; - eccsize0 = 0xFF; - eccsize1 = 0xFF; - break; - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - case OMAP_ECC_BCH8_CODE_HW: - ecc_algo = 0x1; - bch_type = 0x1; - if (mode == NAND_ECC_WRITE) { - bch_wrapmode = 0x01; - eccsize0 = 0; /* extra bits in nibbles per sector */ - eccsize1 = 28; /* OOB bits in nibbles per sector */ - } else { - bch_wrapmode = 0x01; - eccsize0 = 26; /* ECC bits in nibbles per sector */ - eccsize1 = 2; /* non-ECC bits in nibbles per sector */ - } - break; - case OMAP_ECC_BCH16_CODE_HW: - ecc_algo = 0x1; - bch_type = 0x2; - if (mode == NAND_ECC_WRITE) { - bch_wrapmode = 0x01; - eccsize0 = 0; /* extra bits in nibbles per sector */ - eccsize1 = 52; /* OOB bits in nibbles per sector */ - } else { - bch_wrapmode = 0x01; - eccsize0 = 52; /* ECC bits in nibbles per sector */ - eccsize1 = 0; /* non-ECC bits in nibbles per sector */ - } - break; - default: - return; - } - /* Clear ecc and enable bits */ - writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); - /* Configure ecc size for BCH */ - ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12); - writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config); - - /* Configure device details for BCH engine */ - ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */ - (bch_type << 12) | /* BCH4/BCH8/BCH16 */ - (bch_wrapmode << 8) | /* wrap mode */ - (dev_width << 7) | /* bus width */ - (0x0 << 4) | /* number of sectors */ - (cs << 1) | /* ECC CS */ - (0x1)); /* enable ECC */ - writel(ecc_config_val, &gpmc_cfg->ecc_config); -} - -/* - * omap_calculate_ecc - Read ECC result - * @mtd: MTD structure - * @dat: unused - * @ecc_code: ecc_code buffer - * Using noninverted ECC can be considered ugly since writing a blank - * page ie. padding will clear the ECC bytes. This is no problem as - * long nobody is trying to write data on the seemingly unused page. - * Reading an erased page will produce an ECC mismatch between - * generated and read ECC bytes that has to be dealt with separately. - * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC - * is used, the result of read will be 0x0 while the ECC offsets of the - * spare area will be 0xFF which will result in an ECC mismatch. - */ -static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(chip); - const uint32_t *ptr; - uint32_t val = 0; - int8_t i = 0, j; - - switch (info->ecc_scheme) { - case OMAP_ECC_HAM1_CODE_HW: - val = readl(&gpmc_cfg->ecc1_result); - ecc_code[0] = val & 0xFF; - ecc_code[1] = (val >> 16) & 0xFF; - ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0); - break; -#ifdef CONFIG_BCH - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: -#endif - case OMAP_ECC_BCH8_CODE_HW: - ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; - val = readl(ptr); - ecc_code[i++] = (val >> 0) & 0xFF; - ptr--; - for (j = 0; j < 3; j++) { - val = readl(ptr); - ecc_code[i++] = (val >> 24) & 0xFF; - ecc_code[i++] = (val >> 16) & 0xFF; - ecc_code[i++] = (val >> 8) & 0xFF; - ecc_code[i++] = (val >> 0) & 0xFF; - ptr--; - } - break; - case OMAP_ECC_BCH16_CODE_HW: - val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]); - ecc_code[i++] = (val >> 8) & 0xFF; - ecc_code[i++] = (val >> 0) & 0xFF; - val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]); - ecc_code[i++] = (val >> 24) & 0xFF; - ecc_code[i++] = (val >> 16) & 0xFF; - ecc_code[i++] = (val >> 8) & 0xFF; - ecc_code[i++] = (val >> 0) & 0xFF; - val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]); - ecc_code[i++] = (val >> 24) & 0xFF; - ecc_code[i++] = (val >> 16) & 0xFF; - ecc_code[i++] = (val >> 8) & 0xFF; - ecc_code[i++] = (val >> 0) & 0xFF; - for (j = 3; j >= 0; j--) { - val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j] - ); - ecc_code[i++] = (val >> 24) & 0xFF; - ecc_code[i++] = (val >> 16) & 0xFF; - ecc_code[i++] = (val >> 8) & 0xFF; - ecc_code[i++] = (val >> 0) & 0xFF; - } - break; - default: - return -EINVAL; - } - /* ECC scheme specific syndrome customizations */ - switch (info->ecc_scheme) { - case OMAP_ECC_HAM1_CODE_HW: - break; -#ifdef CONFIG_BCH - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: - - for (i = 0; i < chip->ecc.bytes; i++) - *(ecc_code + i) = *(ecc_code + i) ^ - bch8_polynomial[i]; - break; -#endif - case OMAP_ECC_BCH8_CODE_HW: - ecc_code[chip->ecc.bytes - 1] = 0x00; - break; - case OMAP_ECC_BCH16_CODE_HW: - break; - default: - return -EINVAL; - } - return 0; -} - -#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH - -#define PREFETCH_CONFIG1_CS_SHIFT 24 -#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 -#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) -#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) -#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) -#define ENABLE_PREFETCH (1 << 7) - -/** - * omap_prefetch_enable - configures and starts prefetch transfer - * @fifo_th: fifo threshold to be used for read/ write - * @count: number of bytes to be transferred - * @is_write: prefetch read(0) or write post(1) mode - * @cs: chip select to use - */ -static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs) -{ - uint32_t val; - - if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) - return -EINVAL; - - if (readl(&gpmc_cfg->prefetch_control)) - return -EBUSY; - - /* Set the amount of bytes to be prefetched */ - writel(count, &gpmc_cfg->prefetch_config2); - - val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) | - PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH; - writel(val, &gpmc_cfg->prefetch_config1); - - /* Start the prefetch engine */ - writel(1, &gpmc_cfg->prefetch_control); - - return 0; -} - -/** - * omap_prefetch_reset - disables and stops the prefetch engine - */ -static void omap_prefetch_reset(void) -{ - writel(0, &gpmc_cfg->prefetch_control); - writel(0, &gpmc_cfg->prefetch_config1); -} - -static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len) -{ - int ret; - uint32_t cnt; - struct omap_nand_info *info = nand_get_controller_data(chip); - - ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs); - if (ret < 0) - return ret; - - do { - int i; - - cnt = readl(&gpmc_cfg->prefetch_status); - cnt = PREFETCH_STATUS_FIFO_CNT(cnt); - - for (i = 0; i < cnt / 4; i++) { - *buf++ = readl(CONFIG_SYS_NAND_BASE); - len -= 4; - } - } while (len); - - omap_prefetch_reset(); - - return 0; -} - -static inline void omap_nand_read(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - - if (chip->options & NAND_BUSWIDTH_16) - nand_read_buf16(mtd, buf, len); - else - nand_read_buf(mtd, buf, len); -} - -static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int ret; - uint32_t head, tail; - struct nand_chip *chip = mtd_to_nand(mtd); - - /* - * If the destination buffer is unaligned, start with reading - * the overlap byte-wise. - */ - head = ((uint32_t) buf) % 4; - if (head) { - omap_nand_read(mtd, buf, head); - buf += head; - len -= head; - } - - /* - * Only transfer multiples of 4 bytes in a pre-fetched fashion. - * If there's a residue, care for it byte-wise afterwards. - */ - tail = len % 4; - - ret = __read_prefetch_aligned(chip, (uint32_t *)buf, len - tail); - if (ret < 0) { - /* fallback in case the prefetch engine is busy */ - omap_nand_read(mtd, buf, len); - } else if (tail) { - buf += len - tail; - omap_nand_read(mtd, buf, tail); - } -} -#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */ - -#ifdef CONFIG_NAND_OMAP_ELM -/* - * omap_reverse_list - re-orders list elements in reverse order [internal] - * @list: pointer to start of list - * @length: length of list -*/ -static void omap_reverse_list(u8 *list, unsigned int length) -{ - unsigned int i, j; - unsigned int half_length = length / 2; - u8 tmp; - for (i = 0, j = length - 1; i < half_length; i++, j--) { - tmp = list[i]; - list[i] = list[j]; - list[j] = tmp; - } -} - -/* - * omap_correct_data_bch - Compares the ecc read from nand spare area - * with ECC registers values and corrects one bit error if it has occurred - * - * @mtd: MTD device structure - * @dat: page data - * @read_ecc: ecc read from nand flash (ignored) - * @calc_ecc: ecc read from ECC registers - * - * @return 0 if data is OK or corrected, else returns -1 - */ -static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - uint32_t error_count = 0, error_max; - uint32_t error_loc[ELM_MAX_ERROR_COUNT]; - enum bch_level bch_type; - uint32_t i, ecc_flag = 0; - uint8_t count; - uint32_t byte_pos, bit_pos; - int err = 0; - - /* check calculated ecc */ - for (i = 0; i < ecc->bytes && !ecc_flag; i++) { - if (calc_ecc[i] != 0x00) - ecc_flag = 1; - } - if (!ecc_flag) - return 0; - - /* check for whether its a erased-page */ - ecc_flag = 0; - for (i = 0; i < ecc->bytes && !ecc_flag; i++) { - if (read_ecc[i] != 0xff) - ecc_flag = 1; - } - if (!ecc_flag) - return 0; - - /* - * while reading ECC result we read it in big endian. - * Hence while loading to ELM we have rotate to get the right endian. - */ - switch (info->ecc_scheme) { - case OMAP_ECC_BCH8_CODE_HW: - bch_type = BCH_8_BIT; - omap_reverse_list(calc_ecc, ecc->bytes - 1); - break; - case OMAP_ECC_BCH16_CODE_HW: - bch_type = BCH_16_BIT; - omap_reverse_list(calc_ecc, ecc->bytes); - break; - default: - return -EINVAL; - } - /* use elm module to check for errors */ - elm_config(bch_type); - err = elm_check_error(calc_ecc, bch_type, &error_count, error_loc); - if (err) - return err; - - /* correct bch error */ - for (count = 0; count < error_count; count++) { - switch (info->ecc_scheme) { - case OMAP_ECC_BCH8_CODE_HW: - /* 14th byte in ECC is reserved to match ROM layout */ - error_max = SECTOR_BYTES + (ecc->bytes - 1); - break; - case OMAP_ECC_BCH16_CODE_HW: - error_max = SECTOR_BYTES + ecc->bytes; - break; - default: - return -EINVAL; - } - byte_pos = error_max - (error_loc[count] / 8) - 1; - bit_pos = error_loc[count] % 8; - if (byte_pos < SECTOR_BYTES) { - dat[byte_pos] ^= 1 << bit_pos; - debug("nand: bit-flip corrected @data=%d\n", byte_pos); - } else if (byte_pos < error_max) { - read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos; - debug("nand: bit-flip corrected @oob=%d\n", byte_pos - - SECTOR_BYTES); - } else { - err = -EBADMSG; - printf("nand: error: invalid bit-flip location\n"); - } - } - return (err) ? err : error_count; -} - -/** - * omap_read_page_bch - hardware ecc based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: caller expects OOB data read to chip->oob_poi - * @page: page number to read - * - */ -static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - uint8_t *oob = chip->oob_poi; - uint32_t data_pos; - uint32_t oob_pos; - - data_pos = 0; - /* oob area start */ - oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; - oob += chip->ecc.layout->eccpos[0]; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, - oob += eccbytes) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - /* read data */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1); - chip->read_buf(mtd, p, eccsize); - - /* read respective ecc from oob area */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1); - chip->read_buf(mtd, oob, eccbytes); - /* read syndrome */ - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - data_pos += eccsize; - oob_pos += eccbytes; - } - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - return 0; -} -#endif /* CONFIG_NAND_OMAP_ELM */ - -/* - * OMAP3 BCH8 support (with BCH library) - */ -#ifdef CONFIG_BCH -/** - * omap_correct_data_bch_sw - Decode received data and correct errors - * @mtd: MTD device structure - * @data: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - */ -static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data, - u_char *read_ecc, u_char *calc_ecc) -{ - int i, count; - /* cannot correct more than 8 errors */ - unsigned int errloc[8]; - struct nand_chip *chip = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(chip); - - count = decode_bch(info->control, NULL, SECTOR_BYTES, - read_ecc, calc_ecc, NULL, errloc); - if (count > 0) { - /* correct errors */ - for (i = 0; i < count; i++) { - /* correct data only, not ecc bytes */ - if (errloc[i] < SECTOR_BYTES << 3) - data[errloc[i] >> 3] ^= 1 << (errloc[i] & 7); - debug("corrected bitflip %u\n", errloc[i]); -#ifdef DEBUG - puts("read_ecc: "); - /* - * BCH8 have 13 bytes of ECC; BCH4 needs adoption - * here! - */ - for (i = 0; i < 13; i++) - printf("%02x ", read_ecc[i]); - puts("\n"); - puts("calc_ecc: "); - for (i = 0; i < 13; i++) - printf("%02x ", calc_ecc[i]); - puts("\n"); -#endif - } - } else if (count < 0) { - puts("ecc unrecoverable error\n"); - } - return count; -} - -/** - * omap_free_bch - Release BCH ecc resources - * @mtd: MTD device structure - */ -static void __maybe_unused omap_free_bch(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct omap_nand_info *info = nand_get_controller_data(chip); - - if (info->control) { - free_bch(info->control); - info->control = NULL; - } -} -#endif /* CONFIG_BCH */ - -/** - * omap_select_ecc_scheme - configures driver for particular ecc-scheme - * @nand: NAND chip device structure - * @ecc_scheme: ecc scheme to configure - * @pagesize: number of main-area bytes per page of NAND device - * @oobsize: number of OOB/spare bytes per page of NAND device - */ -static int omap_select_ecc_scheme(struct nand_chip *nand, - enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { - struct omap_nand_info *info = nand_get_controller_data(nand); - struct nand_ecclayout *ecclayout = &omap_ecclayout; - int eccsteps = pagesize / SECTOR_BYTES; - int i; - - switch (ecc_scheme) { - case OMAP_ECC_HAM1_CODE_SW: - debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n"); - /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are - * initialized in nand_scan_tail(), so just set ecc.mode */ - info->control = NULL; - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.layout = NULL; - nand->ecc.size = 0; - break; - - case OMAP_ECC_HAM1_CODE_HW: - debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n"); - /* check ecc-scheme requirements before updating ecc info */ - if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { - printf("nand: error: insufficient OOB: require=%d\n", ( - (3 * eccsteps) + BADBLOCK_MARKER_LENGTH)); - return -EINVAL; - } - info->control = NULL; - /* populate ecc specific fields */ - memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.strength = 1; - nand->ecc.size = SECTOR_BYTES; - nand->ecc.bytes = 3; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.calculate = omap_calculate_ecc; - /* define ecc-layout */ - ecclayout->eccbytes = nand->ecc.bytes * eccsteps; - for (i = 0; i < ecclayout->eccbytes; i++) { - if (nand->options & NAND_BUSWIDTH_16) - ecclayout->eccpos[i] = i + 2; - else - ecclayout->eccpos[i] = i + 1; - } - ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - - BADBLOCK_MARKER_LENGTH; - break; - - case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: -#ifdef CONFIG_BCH - debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); - /* check ecc-scheme requirements before updating ecc info */ - if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { - printf("nand: error: insufficient OOB: require=%d\n", ( - (13 * eccsteps) + BADBLOCK_MARKER_LENGTH)); - return -EINVAL; - } - /* check if BCH S/W library can be used for error detection */ - info->control = init_bch(13, 8, 0x201b); - if (!info->control) { - printf("nand: error: could not init_bch()\n"); - return -ENODEV; - } - /* populate ecc specific fields */ - memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.strength = 8; - nand->ecc.size = SECTOR_BYTES; - nand->ecc.bytes = 13; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data_bch_sw; - nand->ecc.calculate = omap_calculate_ecc; - /* define ecc-layout */ - ecclayout->eccbytes = nand->ecc.bytes * eccsteps; - ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; - for (i = 1; i < ecclayout->eccbytes; i++) { - if (i % nand->ecc.bytes) - ecclayout->eccpos[i] = - ecclayout->eccpos[i - 1] + 1; - else - ecclayout->eccpos[i] = - ecclayout->eccpos[i - 1] + 2; - } - ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - - BADBLOCK_MARKER_LENGTH; - break; -#else - printf("nand: error: CONFIG_BCH required for ECC\n"); - return -EINVAL; -#endif - - case OMAP_ECC_BCH8_CODE_HW: -#ifdef CONFIG_NAND_OMAP_ELM - debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n"); - /* check ecc-scheme requirements before updating ecc info */ - if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { - printf("nand: error: insufficient OOB: require=%d\n", ( - (14 * eccsteps) + BADBLOCK_MARKER_LENGTH)); - return -EINVAL; - } - /* intialize ELM for ECC error detection */ - elm_init(); - info->control = NULL; - /* populate ecc specific fields */ - memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.strength = 8; - nand->ecc.size = SECTOR_BYTES; - nand->ecc.bytes = 14; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc; - nand->ecc.read_page = omap_read_page_bch; - /* define ecc-layout */ - ecclayout->eccbytes = nand->ecc.bytes * eccsteps; - for (i = 0; i < ecclayout->eccbytes; i++) - ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - - BADBLOCK_MARKER_LENGTH; - break; -#else - printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); - return -EINVAL; -#endif - - case OMAP_ECC_BCH16_CODE_HW: -#ifdef CONFIG_NAND_OMAP_ELM - debug("nand: using OMAP_ECC_BCH16_CODE_HW\n"); - /* check ecc-scheme requirements before updating ecc info */ - if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { - printf("nand: error: insufficient OOB: require=%d\n", ( - (26 * eccsteps) + BADBLOCK_MARKER_LENGTH)); - return -EINVAL; - } - /* intialize ELM for ECC error detection */ - elm_init(); - /* populate ecc specific fields */ - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = SECTOR_BYTES; - nand->ecc.bytes = 26; - nand->ecc.strength = 16; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.correct = omap_correct_data_bch; - nand->ecc.calculate = omap_calculate_ecc; - nand->ecc.read_page = omap_read_page_bch; - /* define ecc-layout */ - ecclayout->eccbytes = nand->ecc.bytes * eccsteps; - for (i = 0; i < ecclayout->eccbytes; i++) - ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; - ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes - - BADBLOCK_MARKER_LENGTH; - break; -#else - printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); - return -EINVAL; -#endif - default: - debug("nand: error: ecc scheme not enabled or supported\n"); - return -EINVAL; - } - - /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */ - if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW) - nand->ecc.layout = ecclayout; - - info->ecc_scheme = ecc_scheme; - return 0; -} - -#ifndef CONFIG_SPL_BUILD -/* - * omap_nand_switch_ecc - switch the ECC operation between different engines - * (h/w and s/w) and different algorithms (hamming and BCHx) - * - * @hardware - true if one of the HW engines should be used - * @eccstrength - the number of bits that could be corrected - * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) - */ -int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) -{ - struct nand_chip *nand; - struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); - int err = 0; - - if (!mtd) { - printf("nand: error: no NAND devices found\n"); - return -ENODEV; - } - - nand = mtd_to_nand(mtd); - nand->options |= NAND_OWN_BUFFERS; - nand->options &= ~NAND_SUBPAGE_READ; - /* Setup the ecc configurations again */ - if (hardware) { - if (eccstrength == 1) { - err = omap_select_ecc_scheme(nand, - OMAP_ECC_HAM1_CODE_HW, - mtd->writesize, mtd->oobsize); - } else if (eccstrength == 8) { - err = omap_select_ecc_scheme(nand, - OMAP_ECC_BCH8_CODE_HW, - mtd->writesize, mtd->oobsize); - } else if (eccstrength == 16) { - err = omap_select_ecc_scheme(nand, - OMAP_ECC_BCH16_CODE_HW, - mtd->writesize, mtd->oobsize); - } else { - printf("nand: error: unsupported ECC scheme\n"); - return -EINVAL; - } - } else { - if (eccstrength == 1) { - err = omap_select_ecc_scheme(nand, - OMAP_ECC_HAM1_CODE_SW, - mtd->writesize, mtd->oobsize); - } else if (eccstrength == 8) { - err = omap_select_ecc_scheme(nand, - OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, - mtd->writesize, mtd->oobsize); - } else { - printf("nand: error: unsupported ECC scheme\n"); - return -EINVAL; - } - } - - /* Update NAND handling after ECC mode switch */ - if (!err) - err = nand_scan_tail(mtd); - return err; -} -#endif /* CONFIG_SPL_BUILD */ - -/* - * Board-specific NAND initialization. The following members of the - * argument are board-specific: - * - 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 - * - cmd_ctrl: hardwarespecific function for accesing control-lines - * - waitfunc: hardwarespecific function for accesing device ready/busy line - * - ecc.hwctl: function to enable (reset) hardware ecc generator - * - ecc.mode: mode of ecc, see defines - * - chip_delay: chip dependent delay for transfering data from array to - * read regs (tR) - * - options: various chip options. They can partly be set to inform - * nand_scan about special functionality. See the defines for further - * explanation - */ -int board_nand_init(struct nand_chip *nand) -{ - int32_t gpmc_config = 0; - int cs = cs_next++; - int err = 0; - /* - * xloader/Uboot's gpmc configuration would have configured GPMC for - * nand type of memory. The following logic scans and latches on to the - * first CS with NAND type memory. - * TBD: need to make this logic generic to handle multiple CS NAND - * devices. - */ - while (cs < GPMC_MAX_CS) { - /* Check if NAND type is set */ - if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) { - /* Found it!! */ - break; - } - cs++; - } - if (cs >= GPMC_MAX_CS) { - printf("nand: error: Unable to find NAND settings in " - "GPMC Configuration - quitting\n"); - return -ENODEV; - } - - gpmc_config = readl(&gpmc_cfg->config); - /* Disable Write protect */ - gpmc_config |= 0x10; - writel(gpmc_config, &gpmc_cfg->config); - - nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; - nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; - omap_nand_info[cs].control = NULL; - omap_nand_info[cs].cs = cs; - omap_nand_info[cs].ws = wscfg[cs]; - nand_set_controller_data(nand, &omap_nand_info[cs]); - nand->cmd_ctrl = omap_nand_hwcontrol; - nand->options |= NAND_NO_PADDING | NAND_CACHEPRG; - nand->chip_delay = 100; - nand->ecc.layout = &omap_ecclayout; - - /* configure driver and controller based on NAND device bus-width */ - gpmc_config = readl(&gpmc_cfg->cs[cs].config1); -#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) - nand->options |= NAND_BUSWIDTH_16; - writel(gpmc_config | (0x1 << 12), &gpmc_cfg->cs[cs].config1); -#else - nand->options &= ~NAND_BUSWIDTH_16; - writel(gpmc_config & ~(0x1 << 12), &gpmc_cfg->cs[cs].config1); -#endif - /* select ECC scheme */ -#if defined(CONFIG_NAND_OMAP_ECCSCHEME) - err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME, - CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE); -#else - /* pagesize and oobsize are not required to configure sw ecc-scheme */ - err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, - 0, 0); -#endif - if (err) - return err; - -#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH - nand->read_buf = omap_nand_read_prefetch; -#else - if (nand->options & NAND_BUSWIDTH_16) - nand->read_buf = nand_read_buf16; - else - nand->read_buf = nand_read_buf; -#endif - - nand->dev_ready = omap_dev_ready; - - return 0; -} diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c deleted file mode 100644 index 2a02a9d58e..0000000000 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ /dev/null @@ -1,1828 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * drivers/mtd/nand/pxa3xx_nand.c - * - * Copyright © 2005 Intel Corporation - * Copyright © 2006 Marvell International Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pxa3xx_nand.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define TIMEOUT_DRAIN_FIFO 5 /* in ms */ -#define CHIP_DELAY_TIMEOUT 200 -#define NAND_STOP_DELAY 40 - -/* - * Define a buffer size for the initial command that detects the flash device: - * STATUS, READID and PARAM. - * ONFI param page is 256 bytes, and there are three redundant copies - * to be read. JEDEC param page is 512 bytes, and there are also three - * redundant copies to be read. - * Hence this buffer should be at least 512 x 3. Let's pick 2048. - */ -#define INIT_BUFFER_SIZE 2048 - -/* registers and bit definitions */ -#define NDCR (0x00) /* Control register */ -#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ -#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ -#define NDSR (0x14) /* Status Register */ -#define NDPCR (0x18) /* Page Count Register */ -#define NDBDR0 (0x1C) /* Bad Block Register 0 */ -#define NDBDR1 (0x20) /* Bad Block Register 1 */ -#define NDECCCTRL (0x28) /* ECC control */ -#define NDDB (0x40) /* Data Buffer */ -#define NDCB0 (0x48) /* Command Buffer0 */ -#define NDCB1 (0x4C) /* Command Buffer1 */ -#define NDCB2 (0x50) /* Command Buffer2 */ - -#define NDCR_SPARE_EN (0x1 << 31) -#define NDCR_ECC_EN (0x1 << 30) -#define NDCR_DMA_EN (0x1 << 29) -#define NDCR_ND_RUN (0x1 << 28) -#define NDCR_DWIDTH_C (0x1 << 27) -#define NDCR_DWIDTH_M (0x1 << 26) -#define NDCR_PAGE_SZ (0x1 << 24) -#define NDCR_NCSX (0x1 << 23) -#define NDCR_ND_MODE (0x3 << 21) -#define NDCR_NAND_MODE (0x0) -#define NDCR_CLR_PG_CNT (0x1 << 20) -#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) -#define NDCR_RD_ID_CNT_MASK (0x7 << 16) -#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) - -#define NDCR_RA_START (0x1 << 15) -#define NDCR_PG_PER_BLK (0x1 << 14) -#define NDCR_ND_ARB_EN (0x1 << 12) -#define NDCR_INT_MASK (0xFFF) - -#define NDSR_MASK (0xfff) -#define NDSR_ERR_CNT_OFF (16) -#define NDSR_ERR_CNT_MASK (0x1f) -#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) -#define NDSR_RDY (0x1 << 12) -#define NDSR_FLASH_RDY (0x1 << 11) -#define NDSR_CS0_PAGED (0x1 << 10) -#define NDSR_CS1_PAGED (0x1 << 9) -#define NDSR_CS0_CMDD (0x1 << 8) -#define NDSR_CS1_CMDD (0x1 << 7) -#define NDSR_CS0_BBD (0x1 << 6) -#define NDSR_CS1_BBD (0x1 << 5) -#define NDSR_UNCORERR (0x1 << 4) -#define NDSR_CORERR (0x1 << 3) -#define NDSR_WRDREQ (0x1 << 2) -#define NDSR_RDDREQ (0x1 << 1) -#define NDSR_WRCMDREQ (0x1) - -#define NDCB0_LEN_OVRD (0x1 << 28) -#define NDCB0_ST_ROW_EN (0x1 << 26) -#define NDCB0_AUTO_RS (0x1 << 25) -#define NDCB0_CSEL (0x1 << 24) -#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) -#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) -#define NDCB0_CMD_TYPE_MASK (0x7 << 21) -#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) -#define NDCB0_NC (0x1 << 20) -#define NDCB0_DBC (0x1 << 19) -#define NDCB0_ADDR_CYC_MASK (0x7 << 16) -#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) -#define NDCB0_CMD2_MASK (0xff << 8) -#define NDCB0_CMD1_MASK (0xff) -#define NDCB0_ADDR_CYC_SHIFT (16) - -#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ -#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ -#define EXT_CMD_TYPE_READ 4 /* Read */ -#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ -#define EXT_CMD_TYPE_FINAL 3 /* Final command */ -#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ -#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ - -/* - * This should be large enough to read 'ONFI' and 'JEDEC'. - * Let's use 7 bytes, which is the maximum ID count supported - * by the controller (see NDCR_RD_ID_CNT_MASK). - */ -#define READ_ID_BYTES 7 - -/* macros for registers read/write */ -#define nand_writel(info, off, val) \ - writel((val), (info)->mmio_base + (off)) - -#define nand_readl(info, off) \ - readl((info)->mmio_base + (off)) - -/* error code and state */ -enum { - ERR_NONE = 0, - ERR_DMABUSERR = -1, - ERR_SENDCMD = -2, - ERR_UNCORERR = -3, - ERR_BBERR = -4, - ERR_CORERR = -5, -}; - -enum { - STATE_IDLE = 0, - STATE_PREPARED, - STATE_CMD_HANDLE, - STATE_DMA_READING, - STATE_DMA_WRITING, - STATE_DMA_DONE, - STATE_PIO_READING, - STATE_PIO_WRITING, - STATE_CMD_DONE, - STATE_READY, -}; - -enum pxa3xx_nand_variant { - PXA3XX_NAND_VARIANT_PXA, - PXA3XX_NAND_VARIANT_ARMADA370, -}; - -struct pxa3xx_nand_host { - struct nand_chip chip; - void *info_data; - - /* page size of attached chip */ - int use_ecc; - int cs; - - /* calculated from pxa3xx_nand_flash data */ - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; -}; - -struct pxa3xx_nand_info { - struct nand_hw_control controller; - struct pxa3xx_nand_platform_data *pdata; - - struct clk *clk; - void __iomem *mmio_base; - unsigned long mmio_phys; - int cmd_complete, dev_ready; - - unsigned int buf_start; - unsigned int buf_count; - unsigned int buf_size; - unsigned int data_buff_pos; - unsigned int oob_buff_pos; - - unsigned char *data_buff; - unsigned char *oob_buff; - - struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; - unsigned int state; - - /* - * This driver supports NFCv1 (as found in PXA SoC) - * and NFCv2 (as found in Armada 370/XP SoC). - */ - enum pxa3xx_nand_variant variant; - - int cs; - int use_ecc; /* use HW ECC ? */ - int ecc_bch; /* using BCH ECC? */ - int use_spare; /* use spare ? */ - int need_wait; - - /* Amount of real data per full chunk */ - unsigned int chunk_size; - - /* Amount of spare data per full chunk */ - unsigned int spare_size; - - /* Number of full chunks (i.e chunk_size + spare_size) */ - unsigned int nfullchunks; - - /* - * Total number of chunks. If equal to nfullchunks, then there - * are only full chunks. Otherwise, there is one last chunk of - * size (last_chunk_size + last_spare_size) - */ - unsigned int ntotalchunks; - - /* Amount of real data in the last chunk */ - unsigned int last_chunk_size; - - /* Amount of spare data in the last chunk */ - unsigned int last_spare_size; - - unsigned int ecc_size; - unsigned int ecc_err_cnt; - unsigned int max_bitflips; - int retcode; - - /* - * Variables only valid during command - * execution. step_chunk_size and step_spare_size is the - * amount of real data and spare data in the current - * chunk. cur_chunk is the current chunk being - * read/programmed. - */ - unsigned int step_chunk_size; - unsigned int step_spare_size; - unsigned int cur_chunk; - - /* cached register value */ - uint32_t reg_ndcr; - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; - - /* generated NDCBx register values */ - uint32_t ndcb0; - uint32_t ndcb1; - uint32_t ndcb2; - uint32_t ndcb3; -}; - -static struct pxa3xx_nand_timing timing[] = { - /* - * tCH Enable signal hold time - * tCS Enable signal setup time - * tWH ND_nWE high duration - * tWP ND_nWE pulse time - * tRH ND_nRE high duration - * tRP ND_nRE pulse width - * tR ND_nWE high to ND_nRE low for read - * tWHR ND_nWE high to ND_nRE low for status read - * tAR ND_ALE low to ND_nRE low delay - */ - /*ch cs wh wp rh rp r whr ar */ - { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, - { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, - { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, - { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, - { 5, 20, 10, 12, 10, 12, 25000, 60, 10, }, -}; - -static struct pxa3xx_nand_flash builtin_flash_types[] = { - /* - * chip_id - * flash_width Width of Flash memory (DWIDTH_M) - * dfc_width Width of flash controller(DWIDTH_C) - * *timing - * http://www.linux-mtd.infradead.org/nand-data/nanddata.html - */ - { 0x46ec, 16, 16, &timing[1] }, - { 0xdaec, 8, 8, &timing[1] }, - { 0xd7ec, 8, 8, &timing[1] }, - { 0xa12c, 8, 8, &timing[2] }, - { 0xb12c, 16, 16, &timing[2] }, - { 0xdc2c, 8, 8, &timing[2] }, - { 0xcc2c, 16, 16, &timing[2] }, - { 0xba20, 16, 16, &timing[3] }, - { 0xda98, 8, 8, &timing[4] }, -}; - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT -static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; -static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION, - .offs = 8, - .len = 6, - .veroffs = 14, - .maxblocks = 8, /* Last 8 blocks in each chip */ - .pattern = bbt_mirror_pattern -}; -#endif - -static struct nand_ecclayout ecc_layout_2KB_bch4bit = { - .eccbytes = 32, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { {2, 30} } -}; - -static struct nand_ecclayout ecc_layout_2KB_bch8bit = { - .eccbytes = 64, - .eccpos = { - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { {1, 4}, {6, 26} } -}; - -static struct nand_ecclayout ecc_layout_4KB_bch4bit = { - .eccbytes = 64, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - /* Bootrom looks in bytes 0 & 5 for bad blocks */ - .oobfree = { {6, 26}, { 64, 32} } -}; - -static struct nand_ecclayout ecc_layout_8KB_bch4bit = { - .eccbytes = 128, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - - 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, - - 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255}, - - /* Bootrom looks in bytes 0 & 5 for bad blocks */ - .oobfree = { {1, 4}, {6, 26}, { 64, 32}, {128, 32}, {192, 32} } -}; - -static struct nand_ecclayout ecc_layout_4KB_bch8bit = { - .eccbytes = 128, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { } -}; - -static struct nand_ecclayout ecc_layout_8KB_bch8bit = { - .eccbytes = 256, - .eccpos = {}, - /* HW ECC handles all ECC data and all spare area is free for OOB */ - .oobfree = {{0, 160} } -}; - -#define NDTR0_tCH(c) (min((c), 7) << 19) -#define NDTR0_tCS(c) (min((c), 7) << 16) -#define NDTR0_tWH(c) (min((c), 7) << 11) -#define NDTR0_tWP(c) (min((c), 7) << 8) -#define NDTR0_tRH(c) (min((c), 7) << 3) -#define NDTR0_tRP(c) (min((c), 7) << 0) - -#define NDTR1_tR(c) (min((c), 65535) << 16) -#define NDTR1_tWHR(c) (min((c), 15) << 4) -#define NDTR1_tAR(c) (min((c), 15) << 0) - -/* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) - -static enum pxa3xx_nand_variant pxa3xx_nand_get_variant(void) -{ - /* We only support the Armada 370/XP/38x for now */ - return PXA3XX_NAND_VARIANT_ARMADA370; -} - -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, - const struct pxa3xx_nand_timing *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - unsigned long nand_clk = mvebu_get_nand_clock(); - uint32_t ndtr0, ndtr1; - - ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | - NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | - NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | - NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | - NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | - NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | - NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, - const struct nand_sdr_timings *t) -{ - struct pxa3xx_nand_info *info = host->info_data; - struct nand_chip *chip = &host->chip; - unsigned long nand_clk = mvebu_get_nand_clock(); - uint32_t ndtr0, ndtr1; - - u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); - u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); - u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); - u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); - u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); - u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); - u32 tR = chip->chip_delay * 1000; - u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); - u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); - - /* fallback to a default value if tR = 0 */ - if (!tR) - tR = 20000; - - ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | - NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | - NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | - NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | - NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | - NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | - NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); - - info->ndtr0cs0 = ndtr0; - info->ndtr1cs0 = ndtr1; - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); -} - -static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host) -{ - const struct nand_sdr_timings *timings; - struct nand_chip *chip = &host->chip; - struct pxa3xx_nand_info *info = host->info_data; - const struct pxa3xx_nand_flash *f = NULL; - struct mtd_info *mtd = nand_to_mtd(&host->chip); - int mode, id, ntypes, i; - - mode = onfi_get_async_timing_mode(chip); - if (mode == ONFI_TIMING_MODE_UNKNOWN) { - ntypes = ARRAY_SIZE(builtin_flash_types); - - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - id = chip->read_byte(mtd); - id |= chip->read_byte(mtd) << 0x8; - - for (i = 0; i < ntypes; i++) { - f = &builtin_flash_types[i]; - - if (f->chip_id == id) - break; - } - - if (i == ntypes) { - dev_err(&info->pdev->dev, "Error: timings not found\n"); - return -EINVAL; - } - - pxa3xx_nand_set_timing(host, f->timing); - - if (f->flash_width == 16) { - info->reg_ndcr |= NDCR_DWIDTH_M; - chip->options |= NAND_BUSWIDTH_16; - } - - info->reg_ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - } else { - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - } - - return 0; -} - -/** - * NOTE: it is a must to set ND_RUN first, then write - * command buffer, otherwise, it does not work. - * We enable all the interrupt at the same time, and - * let pxa3xx_nand_irq to handle all logic. - */ -static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) -{ - uint32_t ndcr; - - ndcr = info->reg_ndcr; - - if (info->use_ecc) { - ndcr |= NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x1); - } else { - ndcr &= ~NDCR_ECC_EN; - if (info->ecc_bch) - nand_writel(info, NDECCCTRL, 0x0); - } - - ndcr &= ~NDCR_DMA_EN; - - if (info->use_spare) - ndcr |= NDCR_SPARE_EN; - else - ndcr &= ~NDCR_SPARE_EN; - - ndcr |= NDCR_ND_RUN; - - /* clear status bits and run */ - nand_writel(info, NDSR, NDSR_MASK); - nand_writel(info, NDCR, 0); - nand_writel(info, NDCR, ndcr); -} - -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr | int_mask); -} - -static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) -{ - if (info->ecc_bch) { - u32 ts; - - /* - * According to the datasheet, when reading from NDDB - * with BCH enabled, after each 32 bytes reads, we - * have to make sure that the NDSR.RDDREQ bit is set. - * - * Drain the FIFO 8 32 bits reads at a time, and skip - * the polling on the last read. - */ - while (len > 8) { - readsl(info->mmio_base + NDDB, data, 8); - - ts = get_timer(0); - while (!(nand_readl(info, NDSR) & NDSR_RDDREQ)) { - if (get_timer(ts) > TIMEOUT_DRAIN_FIFO) { - dev_err(&info->pdev->dev, - "Timeout on RDDREQ while draining the FIFO\n"); - return; - } - } - - data += 32; - len -= 8; - } - } - - readsl(info->mmio_base + NDDB, data, len); -} - -static void handle_data_pio(struct pxa3xx_nand_info *info) -{ - switch (info->state) { - case STATE_PIO_WRITING: - if (info->step_chunk_size) - writesl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - writesl(info->mmio_base + NDDB, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - case STATE_PIO_READING: - if (info->step_chunk_size) - drain_fifo(info, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(info->step_chunk_size, 4)); - - if (info->step_spare_size) - drain_fifo(info, - info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->step_spare_size, 4)); - break; - default: - dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); - BUG(); - } - - /* Update buffer pointers for multi-page read/write */ - info->data_buff_pos += info->step_chunk_size; - info->oob_buff_pos += info->step_spare_size; -} - -static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info) -{ - handle_data_pio(info); - - info->state = STATE_CMD_DONE; - nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); -} - -static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info) -{ - unsigned int status, is_completed = 0, is_ready = 0; - unsigned int ready, cmd_done; - irqreturn_t ret = IRQ_HANDLED; - - if (info->cs == 0) { - ready = NDSR_FLASH_RDY; - cmd_done = NDSR_CS0_CMDD; - } else { - ready = NDSR_RDY; - cmd_done = NDSR_CS1_CMDD; - } - - /* TODO - find out why we need the delay during write operation. */ - ndelay(1); - - status = nand_readl(info, NDSR); - - if (status & NDSR_UNCORERR) - info->retcode = ERR_UNCORERR; - if (status & NDSR_CORERR) { - info->retcode = ERR_CORERR; - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && - info->ecc_bch) - info->ecc_err_cnt = NDSR_ERR_CNT(status); - else - info->ecc_err_cnt = 1; - - /* - * Each chunk composing a page is corrected independently, - * and we need to store maximum number of corrected bitflips - * to return it to the MTD layer in ecc.read_page(). - */ - info->max_bitflips = max_t(unsigned int, - info->max_bitflips, - info->ecc_err_cnt); - } - if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { - info->state = (status & NDSR_RDDREQ) ? - STATE_PIO_READING : STATE_PIO_WRITING; - /* Call the IRQ thread in U-Boot directly */ - pxa3xx_nand_irq_thread(info); - return 0; - } - if (status & cmd_done) { - info->state = STATE_CMD_DONE; - is_completed = 1; - } - if (status & ready) { - info->state = STATE_READY; - is_ready = 1; - } - - /* - * Clear all status bit before issuing the next command, which - * can and will alter the status bits and will deserve a new - * interrupt on its own. This lets the controller exit the IRQ - */ - nand_writel(info, NDSR, status); - - if (status & NDSR_WRCMDREQ) { - status &= ~NDSR_WRCMDREQ; - info->state = STATE_CMD_HANDLE; - - /* - * Command buffer registers NDCB{0-2} (and optionally NDCB3) - * must be loaded by writing directly either 12 or 16 - * bytes directly to NDCB0, four bytes at a time. - * - * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored - * but each NDCBx register can be read. - */ - nand_writel(info, NDCB0, info->ndcb0); - nand_writel(info, NDCB0, info->ndcb1); - nand_writel(info, NDCB0, info->ndcb2); - - /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) - nand_writel(info, NDCB0, info->ndcb3); - } - - if (is_completed) - info->cmd_complete = 1; - if (is_ready) - info->dev_ready = 1; - - return ret; -} - -static inline int is_buf_blank(uint8_t *buf, size_t len) -{ - for (; len > 0; len--) - if (*buf++ != 0xff) - return 0; - return 1; -} - -static void set_command_address(struct pxa3xx_nand_info *info, - unsigned int page_size, uint16_t column, int page_addr) -{ - /* small page addr setting */ - if (page_size < info->chunk_size) { - info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) - | (column & 0xFF); - - info->ndcb2 = 0; - } else { - info->ndcb1 = ((page_addr & 0xFFFF) << 16) - | (column & 0xFFFF); - - if (page_addr & 0xFF0000) - info->ndcb2 = (page_addr & 0xFF0000) >> 16; - else - info->ndcb2 = 0; - } -} - -static void prepare_start_command(struct pxa3xx_nand_info *info, int command) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = nand_to_mtd(&host->chip); - - /* reset data and oob column point to handle data */ - info->buf_start = 0; - info->buf_count = 0; - info->data_buff_pos = 0; - info->oob_buff_pos = 0; - info->step_chunk_size = 0; - info->step_spare_size = 0; - info->cur_chunk = 0; - info->use_ecc = 0; - info->use_spare = 1; - info->retcode = ERR_NONE; - info->ecc_err_cnt = 0; - info->ndcb3 = 0; - info->need_wait = 0; - - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - case NAND_CMD_PAGEPROG: - info->use_ecc = 1; - break; - case NAND_CMD_PARAM: - info->use_spare = 0; - break; - default: - info->ndcb1 = 0; - info->ndcb2 = 0; - break; - } - - /* - * If we are about to issue a read command, or about to set - * the write address, then clean the data buffer. - */ - if (command == NAND_CMD_READ0 || - command == NAND_CMD_READOOB || - command == NAND_CMD_SEQIN) { - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xFF, info->buf_count); - } -} - -static int prepare_set_command(struct pxa3xx_nand_info *info, int command, - int ext_cmd_type, uint16_t column, int page_addr) -{ - int addr_cycle, exec_cmd; - struct pxa3xx_nand_host *host; - struct mtd_info *mtd; - - host = info->host[info->cs]; - mtd = nand_to_mtd(&host->chip); - addr_cycle = 0; - exec_cmd = 1; - - if (info->cs != 0) - info->ndcb0 = NDCB0_CSEL; - else - info->ndcb0 = 0; - - if (command == NAND_CMD_SEQIN) - exec_cmd = 0; - - addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles - + host->col_addr_cycles); - - switch (command) { - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - info->buf_start = column; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | addr_cycle - | NAND_CMD_READ0; - - if (command == NAND_CMD_READOOB) - info->buf_start += mtd->writesize; - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* - * Multiple page read needs an 'extended command type' field, - * which is either naked-read or last-read according to the - * state. - */ - if (mtd->writesize == info->chunk_size) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); - } else if (mtd->writesize > info->chunk_size) { - info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - } - - set_command_address(info, mtd->writesize, column, page_addr); - break; - - case NAND_CMD_SEQIN: - - info->buf_start = column; - set_command_address(info, mtd->writesize, 0, page_addr); - - /* - * Multiple page programming needs to execute the initial - * SEQIN command that sets the page address. - */ - if (mtd->writesize > info->chunk_size) { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | addr_cycle - | command; - exec_cmd = 1; - } - break; - - case NAND_CMD_PAGEPROG: - if (is_buf_blank(info->data_buff, - (mtd->writesize + mtd->oobsize))) { - exec_cmd = 0; - break; - } - - if (info->cur_chunk < info->nfullchunks) { - info->step_chunk_size = info->chunk_size; - info->step_spare_size = info->spare_size; - } else { - info->step_chunk_size = info->last_chunk_size; - info->step_spare_size = info->last_spare_size; - } - - /* Second command setting for large pages */ - if (mtd->writesize > info->chunk_size) { - /* - * Multiple page write uses the 'extended command' - * field. This can be used to issue a command dispatch - * or a naked-write depending on the current stage. - */ - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_LEN_OVRD - | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->step_chunk_size + - info->step_spare_size; - - /* - * This is the command dispatch that completes a chunked - * page program operation. - */ - if (info->cur_chunk == info->ntotalchunks) { - info->ndcb0 = NDCB0_CMD_TYPE(0x1) - | NDCB0_EXT_CMD_TYPE(ext_cmd_type) - | command; - info->ndcb1 = 0; - info->ndcb2 = 0; - info->ndcb3 = 0; - } - } else { - info->ndcb0 |= NDCB0_CMD_TYPE(0x1) - | NDCB0_AUTO_RS - | NDCB0_ST_ROW_EN - | NDCB0_DBC - | (NAND_CMD_PAGEPROG << 8) - | NAND_CMD_SEQIN - | addr_cycle; - } - break; - - case NAND_CMD_PARAM: - info->buf_count = INIT_BUFFER_SIZE; - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | NDCB0_ADDR_CYC(1) - | NDCB0_LEN_OVRD - | command; - info->ndcb1 = (column & 0xFF); - info->ndcb3 = INIT_BUFFER_SIZE; - info->step_chunk_size = INIT_BUFFER_SIZE; - break; - - case NAND_CMD_READID: - info->buf_count = READ_ID_BYTES; - info->ndcb0 |= NDCB0_CMD_TYPE(3) - | NDCB0_ADDR_CYC(1) - | command; - info->ndcb1 = (column & 0xFF); - - info->step_chunk_size = 8; - break; - case NAND_CMD_STATUS: - info->buf_count = 1; - info->ndcb0 |= NDCB0_CMD_TYPE(4) - | NDCB0_ADDR_CYC(1) - | command; - - info->step_chunk_size = 8; - break; - - case NAND_CMD_ERASE1: - info->ndcb0 |= NDCB0_CMD_TYPE(2) - | NDCB0_AUTO_RS - | NDCB0_ADDR_CYC(3) - | NDCB0_DBC - | (NAND_CMD_ERASE2 << 8) - | NAND_CMD_ERASE1; - info->ndcb1 = page_addr; - info->ndcb2 = 0; - - break; - case NAND_CMD_RESET: - info->ndcb0 |= NDCB0_CMD_TYPE(5) - | command; - - break; - - case NAND_CMD_ERASE2: - exec_cmd = 0; - break; - - default: - exec_cmd = 0; - dev_err(&info->pdev->dev, "non-supported command %x\n", - command); - break; - } - - return exec_cmd; -} - -static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd; - - /* - * if this is a x16 device ,then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - prepare_start_command(info, command); - - info->state = STATE_PREPARED; - exec_cmd = prepare_set_command(info, command, 0, column, page_addr); - - if (exec_cmd) { - u32 ts; - - info->cmd_complete = 0; - info->dev_ready = 0; - info->need_wait = 1; - pxa3xx_nand_start(info); - - ts = get_timer(0); - while (1) { - u32 status; - - status = nand_readl(info, NDSR); - if (status) - pxa3xx_nand_irq(info); - - if (info->cmd_complete) - break; - - if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { - dev_err(&info->pdev->dev, "Wait timeout!!!\n"); - return; - } - } - } - info->state = STATE_IDLE; -} - -static void nand_cmdfunc_extended(struct mtd_info *mtd, - const unsigned command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int exec_cmd, ext_cmd_type; - - /* - * if this is a x16 device then convert the input - * "byte" address into a "word" address appropriate - * for indexing a word-oriented device - */ - if (info->reg_ndcr & NDCR_DWIDTH_M) - column /= 2; - - /* - * There may be different NAND chip hooked to - * different chip select, so check whether - * chip select has been changed, if yes, reset the timing - */ - if (info->cs != host->cs) { - info->cs = host->cs; - nand_writel(info, NDTR0CS0, info->ndtr0cs0); - nand_writel(info, NDTR1CS0, info->ndtr1cs0); - } - - /* Select the extended command for the first command */ - switch (command) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - ext_cmd_type = EXT_CMD_TYPE_MONO; - break; - case NAND_CMD_SEQIN: - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - break; - case NAND_CMD_PAGEPROG: - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - break; - default: - ext_cmd_type = 0; - break; - } - - prepare_start_command(info, command); - - /* - * Prepare the "is ready" completion before starting a command - * transaction sequence. If the command is not executed the - * completion will be completed, see below. - * - * We can do that inside the loop because the command variable - * is invariant and thus so is the exec_cmd. - */ - info->need_wait = 1; - info->dev_ready = 0; - - do { - u32 ts; - - info->state = STATE_PREPARED; - exec_cmd = prepare_set_command(info, command, ext_cmd_type, - column, page_addr); - if (!exec_cmd) { - info->need_wait = 0; - info->dev_ready = 1; - break; - } - - info->cmd_complete = 0; - pxa3xx_nand_start(info); - - ts = get_timer(0); - while (1) { - u32 status; - - status = nand_readl(info, NDSR); - if (status) - pxa3xx_nand_irq(info); - - if (info->cmd_complete) - break; - - if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { - dev_err(&info->pdev->dev, "Wait timeout!!!\n"); - return; - } - } - - /* Only a few commands need several steps */ - if (command != NAND_CMD_PAGEPROG && - command != NAND_CMD_READ0 && - command != NAND_CMD_READOOB) - break; - - info->cur_chunk++; - - /* Check if the sequence is complete */ - if (info->cur_chunk == info->ntotalchunks && - command != NAND_CMD_PAGEPROG) - break; - - /* - * After a splitted program command sequence has issued - * the command dispatch, the command sequence is complete. - */ - if (info->cur_chunk == (info->ntotalchunks + 1) && - command == NAND_CMD_PAGEPROG && - ext_cmd_type == EXT_CMD_TYPE_DISPATCH) - break; - - if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { - /* Last read: issue a 'last naked read' */ - if (info->cur_chunk == info->ntotalchunks - 1) - ext_cmd_type = EXT_CMD_TYPE_LAST_RW; - else - ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; - - /* - * If a splitted program command has no more data to transfer, - * the command dispatch must be issued to complete. - */ - } else if (command == NAND_CMD_PAGEPROG && - info->cur_chunk == info->ntotalchunks) { - ext_cmd_type = EXT_CMD_TYPE_DISPATCH; - } - } while (1); - - info->state = STATE_IDLE; -} - -static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; -} - -static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, - int page) -{ - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - if (info->retcode == ERR_CORERR && info->use_ecc) { - mtd->ecc_stats.corrected += info->ecc_err_cnt; - - } else if (info->retcode == ERR_UNCORERR) { - /* - * for blank page (all 0xff), HW will calculate its ECC as - * 0, which is different from the ECC information within - * OOB, ignore such uncorrectable errors - */ - if (is_buf_blank(buf, mtd->writesize)) - info->retcode = ERR_NONE; - else - mtd->ecc_stats.failed++; - } - - return info->max_bitflips; -} - -static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - char retval = 0xFF; - - if (info->buf_start < info->buf_count) - /* Has just send a new command? */ - retval = info->data_buff[info->buf_start++]; - - return retval; -} - -static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - u16 retval = 0xFFFF; - - if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { - retval = *((u16 *)(info->data_buff+info->buf_start)); - info->buf_start += 2; - } - return retval; -} - -static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(buf, info->data_buff + info->buf_start, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - int real_len = min_t(size_t, len, info->buf_count - info->buf_start); - - memcpy(info->data_buff + info->buf_start, buf, real_len); - info->buf_start += real_len; -} - -static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) -{ - return; -} - -static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - - if (info->need_wait) { - u32 ts; - - info->need_wait = 0; - - ts = get_timer(0); - while (1) { - u32 status; - - status = nand_readl(info, NDSR); - if (status) - pxa3xx_nand_irq(info); - - if (info->dev_ready) - break; - - if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { - dev_err(&info->pdev->dev, "Ready timeout!!!\n"); - return NAND_STATUS_FAIL; - } - } - } - - /* pxa3xx_nand_send_command has waited for command complete */ - if (this->state == FL_WRITING || this->state == FL_ERASING) { - if (info->retcode == ERR_NONE) - return 0; - else - return NAND_STATUS_FAIL; - } - - return NAND_STATUS_READY; -} - -static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_platform_data *pdata = info->pdata; - - /* Configure default flash values */ - info->reg_ndcr = 0x0; /* enable all interrupts */ - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); - info->reg_ndcr |= NDCR_SPARE_EN; - - return 0; -} - -static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = nand_to_mtd(&info->host[info->cs]->chip); - struct nand_chip *chip = mtd_to_nand(mtd); - - info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; - info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; - info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; -} - -static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_platform_data *pdata = info->pdata; - uint32_t ndcr = nand_readl(info, NDCR); - - /* Set an initial chunk size */ - info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - info->reg_ndcr = ndcr & - ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->ndtr0cs0 = nand_readl(info, NDTR0CS0); - info->ndtr1cs0 = nand_readl(info, NDTR1CS0); -} - -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) -{ - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) - return -ENOMEM; - return 0; -} - -static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) -{ - struct pxa3xx_nand_info *info = host->info_data; - struct pxa3xx_nand_platform_data *pdata = info->pdata; - struct mtd_info *mtd; - struct nand_chip *chip; - const struct nand_sdr_timings *timings; - int ret; - - mtd = nand_to_mtd(&info->host[info->cs]->chip); - chip = mtd_to_nand(mtd); - - /* configure default flash values */ - info->reg_ndcr = 0x0; /* enable all interrupts */ - info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); - info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */ - - /* use the common timing to make a try */ - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - pxa3xx_nand_set_sdr_timing(host, timings); - - chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); - ret = chip->waitfunc(mtd, chip); - if (ret & NAND_STATUS_FAIL) - return -ENODEV; - - return 0; -} - -static int pxa_ecc_init(struct pxa3xx_nand_info *info, - struct nand_ecc_ctrl *ecc, - int strength, int ecc_stepsize, int page_size) -{ - if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 40; - info->ecc_size = 24; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 512; - info->spare_size = 8; - info->ecc_size = 8; - ecc->mode = NAND_ECC_HW; - ecc->size = 512; - ecc->strength = 1; - - /* - * Required ECC: 4-bit correction per 512 bytes - * Select: 16-bit correction per 2048 bytes - */ - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { - info->ecc_bch = 1; - info->nfullchunks = 1; - info->ntotalchunks = 1; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_2KB_bch4bit; - ecc->strength = 16; - - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 2; - info->ntotalchunks = 2; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_4KB_bch4bit; - ecc->strength = 16; - - } else if (strength == 4 && ecc_stepsize == 512 && page_size == 8192) { - info->ecc_bch = 1; - info->nfullchunks = 4; - info->ntotalchunks = 4; - info->chunk_size = 2048; - info->spare_size = 32; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_8KB_bch4bit; - ecc->strength = 16; - - /* - * Required ECC: 8-bit correction per 512 bytes - * Select: 16-bit correction per 1024 bytes - */ - } else if (strength == 8 && ecc_stepsize == 512 && page_size == 2048) { - info->ecc_bch = 1; - info->nfullchunks = 1; - info->ntotalchunks = 2; - info->chunk_size = 1024; - info->spare_size = 0; - info->last_chunk_size = 1024; - info->last_spare_size = 64; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_2KB_bch8bit; - ecc->strength = 16; - - } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { - info->ecc_bch = 1; - info->nfullchunks = 4; - info->ntotalchunks = 5; - info->chunk_size = 1024; - info->spare_size = 0; - info->last_chunk_size = 0; - info->last_spare_size = 64; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_4KB_bch8bit; - ecc->strength = 16; - - } else if (strength == 8 && ecc_stepsize == 512 && page_size == 8192) { - info->ecc_bch = 1; - info->nfullchunks = 8; - info->ntotalchunks = 9; - info->chunk_size = 1024; - info->spare_size = 0; - info->last_chunk_size = 0; - info->last_spare_size = 160; - info->ecc_size = 32; - ecc->mode = NAND_ECC_HW; - ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_8KB_bch8bit; - ecc->strength = 16; - - } else { - dev_err(&info->pdev->dev, - "ECC strength %d at page size %d is not supported\n", - strength, page_size); - return -ENODEV; - } - - return 0; -} - -static int pxa3xx_nand_scan(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct pxa3xx_nand_host *host = nand_get_controller_data(chip); - struct pxa3xx_nand_info *info = host->info_data; - struct pxa3xx_nand_platform_data *pdata = info->pdata; - int ret; - uint16_t ecc_strength, ecc_step; - - if (pdata->keep_config) { - pxa3xx_nand_detect_config(info); - } else { - ret = pxa3xx_nand_config_ident(info); - if (ret) - return ret; - ret = pxa3xx_nand_sensing(host); - if (ret) { - dev_info(&info->pdev->dev, - "There is no chip on cs %d!\n", - info->cs); - return ret; - } - } - - /* Device detection must be done with ECC disabled */ - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) - nand_writel(info, NDECCCTRL, 0x0); - - if (nand_scan_ident(mtd, 1, NULL)) - return -ENODEV; - - if (!pdata->keep_config) { - ret = pxa3xx_nand_init_timings(host); - if (ret) { - dev_err(&info->pdev->dev, - "Failed to set timings: %d\n", ret); - return ret; - } - } - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - /* - * We'll use a bad block table stored in-flash and don't - * allow writing the bad block marker to the flash. - */ - chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB_BBM; - chip->bbt_td = &bbt_main_descr; - chip->bbt_md = &bbt_mirror_descr; -#endif - - if (pdata->ecc_strength && pdata->ecc_step_size) { - ecc_strength = pdata->ecc_strength; - ecc_step = pdata->ecc_step_size; - } else { - ecc_strength = chip->ecc_strength_ds; - ecc_step = chip->ecc_step_ds; - } - - /* Set default ECC strength requirements on non-ONFI devices */ - if (ecc_strength < 1 && ecc_step < 1) { - ecc_strength = 1; - ecc_step = 512; - } - - ret = pxa_ecc_init(info, &chip->ecc, ecc_strength, - ecc_step, mtd->writesize); - if (ret) - return ret; - - /* - * If the page size is bigger than the FIFO size, let's check - * we are given the right variant and then switch to the extended - * (aka split) command handling, - */ - if (mtd->writesize > info->chunk_size) { - if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { - chip->cmdfunc = nand_cmdfunc_extended; - } else { - dev_err(&info->pdev->dev, - "unsupported page size on this variant\n"); - return -ENODEV; - } - } - - /* calculate addressing information */ - if (mtd->writesize >= 2048) - host->col_addr_cycles = 2; - else - host->col_addr_cycles = 1; - - /* release the initial buffer */ - kfree(info->data_buff); - - /* allocate the real data + oob buffer */ - info->buf_size = mtd->writesize + mtd->oobsize; - ret = pxa3xx_nand_init_buff(info); - if (ret) - return ret; - info->oob_buff = info->data_buff + mtd->writesize; - - if ((mtd->size >> chip->page_shift) > 65536) - host->row_addr_cycles = 3; - else - host->row_addr_cycles = 2; - - if (!pdata->keep_config) - pxa3xx_nand_config_tail(info); - - return nand_scan_tail(mtd); -} - -static int alloc_nand_resource(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_host *host; - struct nand_chip *chip = NULL; - struct mtd_info *mtd; - int ret, cs; - - pdata = info->pdata; - if (pdata->num_cs <= 0) - return -ENODEV; - - info->variant = pxa3xx_nand_get_variant(); - for (cs = 0; cs < pdata->num_cs; cs++) { - chip = (struct nand_chip *) - ((u8 *)&info[1] + sizeof(*host) * cs); - mtd = nand_to_mtd(chip); - host = (struct pxa3xx_nand_host *)chip; - info->host[cs] = host; - host->cs = cs; - host->info_data = info; - mtd->owner = THIS_MODULE; - - nand_set_controller_data(chip, host); - chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; - chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; - chip->controller = &info->controller; - chip->waitfunc = pxa3xx_nand_waitfunc; - chip->select_chip = pxa3xx_nand_select_chip; - chip->read_word = pxa3xx_nand_read_word; - chip->read_byte = pxa3xx_nand_read_byte; - chip->read_buf = pxa3xx_nand_read_buf; - chip->write_buf = pxa3xx_nand_write_buf; - chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->cmdfunc = nand_cmdfunc; - } - - /* Allocate a buffer to allow flash detection */ - info->buf_size = INIT_BUFFER_SIZE; - info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); - if (info->data_buff == NULL) { - ret = -ENOMEM; - goto fail_disable_clk; - } - - /* initialize all interrupts to be disabled */ - disable_int(info, NDSR_MASK); - - return 0; - - kfree(info->data_buff); -fail_disable_clk: - return ret; -} - -static int pxa3xx_nand_probe_dt(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_platform_data *pdata; - const void *blob = gd->fdt_blob; - int node = -1; - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* Get address decoding nodes from the FDT blob */ - do { - node = fdt_node_offset_by_compatible(blob, node, - "marvell,mvebu-pxa3xx-nand"); - if (node < 0) - break; - - /* Bypass disabeld nodes */ - if (!fdtdec_get_is_enabled(blob, node)) - continue; - - /* Get the first enabled NAND controler base address */ - info->mmio_base = - (void __iomem *)fdtdec_get_addr_size_auto_noparent( - blob, node, "reg", 0, NULL, true); - - pdata->num_cs = fdtdec_get_int(blob, node, "num-cs", 1); - if (pdata->num_cs != 1) { - pr_err("pxa3xx driver supports single CS only\n"); - break; - } - - if (fdtdec_get_bool(blob, node, "nand-enable-arbiter")) - pdata->enable_arbiter = 1; - - if (fdtdec_get_bool(blob, node, "nand-keep-config")) - pdata->keep_config = 1; - - /* - * ECC parameters. - * If these are not set, they will be selected according - * to the detected flash type. - */ - /* ECC strength */ - pdata->ecc_strength = fdtdec_get_int(blob, node, - "nand-ecc-strength", 0); - - /* ECC step size */ - pdata->ecc_step_size = fdtdec_get_int(blob, node, - "nand-ecc-step-size", 0); - - info->pdata = pdata; - - /* Currently support only a single NAND controller */ - return 0; - - } while (node >= 0); - - return -EINVAL; -} - -static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info) -{ - struct pxa3xx_nand_platform_data *pdata; - int ret, cs, probe_success; - - ret = pxa3xx_nand_probe_dt(info); - if (ret) - return ret; - - pdata = info->pdata; - - ret = alloc_nand_resource(info); - if (ret) { - dev_err(&pdev->dev, "alloc nand resource failed\n"); - return ret; - } - - probe_success = 0; - for (cs = 0; cs < pdata->num_cs; cs++) { - struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); - - /* - * The mtd name matches the one used in 'mtdparts' kernel - * parameter. This name cannot be changed or otherwise - * user's mtd partitions configuration would get broken. - */ - mtd->name = "pxa3xx_nand-0"; - info->cs = cs; - ret = pxa3xx_nand_scan(mtd); - if (ret) { - dev_info(&pdev->dev, "failed to scan nand at cs %d\n", - cs); - continue; - } - - if (nand_register(cs, mtd)) - continue; - - probe_success = 1; - } - - if (!probe_success) - return -ENODEV; - - return 0; -} - -/* - * Main initialization routine - */ -void board_nand_init(void) -{ - struct pxa3xx_nand_info *info; - struct pxa3xx_nand_host *host; - int ret; - - info = kzalloc(sizeof(*info) + - sizeof(*host) * CONFIG_SYS_MAX_NAND_DEVICE, - GFP_KERNEL); - if (!info) - return; - - ret = pxa3xx_nand_probe(info); - if (ret) - return; -} diff --git a/drivers/mtd/nand/pxa3xx_nand.h b/drivers/mtd/nand/pxa3xx_nand.h deleted file mode 100644 index 8f24ae6d18..0000000000 --- a/drivers/mtd/nand/pxa3xx_nand.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __ASM_ARCH_PXA3XX_NAND_H -#define __ASM_ARCH_PXA3XX_NAND_H - -#include -#include - -struct pxa3xx_nand_timing { - unsigned int tCH; /* Enable signal hold time */ - unsigned int tCS; /* Enable signal setup time */ - unsigned int tWH; /* ND_nWE high duration */ - unsigned int tWP; /* ND_nWE pulse time */ - unsigned int tRH; /* ND_nRE high duration */ - unsigned int tRP; /* ND_nRE pulse width */ - unsigned int tR; /* ND_nWE high to ND_nRE low for read */ - unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ - unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ -}; - -struct pxa3xx_nand_flash { - uint32_t chip_id; - unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ - unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ -}; - -/* - * Current pxa3xx_nand controller has two chip select which - * both be workable. - * - * Notice should be taken that: - * When you want to use this feature, you should not enable the - * keep configuration feature, for two chip select could be - * attached with different nand chip. The different page size - * and timing requirement make the keep configuration impossible. - */ - -/* The max num of chip select current support */ -#define NUM_CHIP_SELECT (2) -struct pxa3xx_nand_platform_data { - /* the data flash bus is shared between the Static Memory - * Controller and the Data Flash Controller, the arbiter - * controls the ownership of the bus - */ - int enable_arbiter; - - /* allow platform code to keep OBM/bootloader defined NFC config */ - int keep_config; - - /* indicate how many chip selects will be used */ - int num_cs; - - /* use an flash-based bad block table */ - bool flash_bbt; - - /* requested ECC strength and ECC step size */ - int ecc_strength, ecc_step_size; - - const struct mtd_partition *parts[NUM_CHIP_SELECT]; - unsigned int nr_parts[NUM_CHIP_SELECT]; - - const struct pxa3xx_nand_flash *flash; - size_t num_flash; -}; -#endif /* __ASM_ARCH_PXA3XX_NAND_H */ diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig new file mode 100644 index 0000000000..1e4ea7bdd4 --- /dev/null +++ b/drivers/mtd/nand/raw/Kconfig @@ -0,0 +1,297 @@ + +menuconfig NAND + bool "NAND Device Support" +if NAND + +config SYS_NAND_SELF_INIT + bool + help + This option, if enabled, provides more flexible and linux-like + NAND initialization process. + +config NAND_ATMEL + bool "Support Atmel NAND controller" + imply SYS_NAND_USE_FLASH_BBT + help + Enable this driver for NAND flash platforms using an Atmel NAND + controller. + +config NAND_DAVINCI + bool "Support TI Davinci NAND controller" + help + Enable this driver for NAND flash controllers available in TI Davinci + and Keystone2 platforms + +config NAND_DENALI + bool + select SYS_NAND_SELF_INIT + imply CMD_NAND + +config NAND_DENALI_DT + bool "Support Denali NAND controller as a DT device" + select NAND_DENALI + depends on OF_CONTROL && DM + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + +config NAND_DENALI_SPARE_AREA_SKIP_BYTES + int "Number of bytes skipped in OOB area" + depends on NAND_DENALI + range 0 63 + help + This option specifies the number of bytes to skip from the beginning + of OOB area before last ECC sector data starts. This is potentially + used to preserve the bad block marker in the OOB area. + +config NAND_LPC32XX_SLC + bool "Support LPC32XX_SLC controller" + help + Enable the LPC32XX SLC NAND controller. + +config NAND_OMAP_GPMC + bool "Support OMAP GPMC NAND controller" + depends on ARCH_OMAP2PLUS + help + Enables omap_gpmc.c driver for OMAPx and AMxxxx platforms. + GPMC controller is used for parallel NAND flash devices, and can + do ECC calculation (not ECC error detection) for HAM1, BCH4, BCH8 + and BCH16 ECC algorithms. + +config NAND_OMAP_GPMC_PREFETCH + bool "Enable GPMC Prefetch" + depends on NAND_OMAP_GPMC + default y + help + On OMAP platforms that use the GPMC controller + (CONFIG_NAND_OMAP_GPMC_PREFETCH), this options enables the code that + uses the prefetch mode to speed up read operations. + +config NAND_OMAP_ELM + bool "Enable ELM driver for OMAPxx and AMxx platforms." + depends on NAND_OMAP_GPMC && !OMAP34XX + help + ELM controller is used for ECC error detection (not ECC calculation) + of BCH4, BCH8 and BCH16 ECC algorithms. + Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, + thus such SoC platforms need to depend on software library for ECC error + detection. However ECC calculation on such plaforms would still be + done by GPMC controller. + +config NAND_VF610_NFC + bool "Support for Freescale NFC for VF610" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + Enables support for NAND Flash Controller on some Freescale + processors like the VF610, MCF54418 or Kinetis K70. + The driver supports a maximum 2k page size. The driver + currently does not support hardware ECC. + +choice + prompt "Hardware ECC strength" + depends on NAND_VF610_NFC + default SYS_NAND_VF610_NFC_45_ECC_BYTES + help + Select the ECC strength used in the hardware BCH ECC block. + +config SYS_NAND_VF610_NFC_45_ECC_BYTES + bool "24-error correction (45 ECC bytes)" + +config SYS_NAND_VF610_NFC_60_ECC_BYTES + bool "32-error correction (60 ECC bytes)" + +endchoice + +config NAND_PXA3XX + bool "Support for NAND on PXA3xx and Armada 370/XP/38x" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables the driver for the NAND flash device found on + PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2). + +config NAND_SUNXI + bool "Support for NAND on Allwinner SoCs" + default ARCH_SUNXI + depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I + select SYS_NAND_SELF_INIT + select SYS_NAND_U_BOOT_LOCATIONS + select SPL_NAND_SUPPORT + imply CMD_NAND + ---help--- + Enable support for NAND. This option enables the standard and + SPL drivers. + The SPL driver only supports reading from the NAND using DMA + transfers. + +if NAND_SUNXI + +config NAND_SUNXI_SPL_ECC_STRENGTH + int "Allwinner NAND SPL ECC Strength" + default 64 + +config NAND_SUNXI_SPL_ECC_SIZE + int "Allwinner NAND SPL ECC Step Size" + default 1024 + +config NAND_SUNXI_SPL_USABLE_PAGE_SIZE + int "Allwinner NAND SPL Usable Page Size" + default 1024 + +endif + +config NAND_ARASAN + bool "Configure Arasan Nand" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables Nand driver support for Arasan nand flash + controller. This uses the hardware ECC for read and + write operations. + +config NAND_MXC + bool "MXC NAND support" + depends on CPU_ARM926EJS || CPU_ARM1136 || MX5 + imply CMD_NAND + help + This enables the NAND driver for the NAND flash controller on the + i.MX27 / i.MX31 / i.MX5 rocessors. + +config NAND_MXS + bool "MXS NAND support" + depends on MX23 || MX28 || MX6 || MX7 + select SYS_NAND_SELF_INIT + imply CMD_NAND + select APBH_DMA + select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 + select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 + help + This enables NAND driver for the NAND flash controller on the + MXS processors. + +if NAND_MXS + +config NAND_MXS_DT + bool "Support MXS NAND controller as a DT device" + depends on OF_CONTROL && MTD + help + Enable the driver for MXS NAND flash on platforms using + device tree. + +config NAND_MXS_USE_MINIMUM_ECC + bool "Use minimum ECC strength supported by the controller" + default false + +endif + +config NAND_ZYNQ + bool "Support for Zynq Nand controller" + select SYS_NAND_SELF_INIT + imply CMD_NAND + help + This enables Nand driver support for Nand flash controller + found on Zynq SoC. + +config NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS + bool "Enable use of 1st stage bootloader timing for NAND" + depends on NAND_ZYNQ + help + This flag prevent U-boot reconfigure NAND flash controller and reuse + the NAND timing from 1st stage bootloader. + +comment "Generic NAND options" + +config SYS_NAND_BLOCK_SIZE + hex "NAND chip eraseblock size" + depends on ARCH_SUNXI + help + Number of data bytes in one eraseblock for the NAND chip on the + board. This is the multiple of NAND_PAGE_SIZE and the number of + pages. + +config SYS_NAND_PAGE_SIZE + hex "NAND chip page size" + depends on ARCH_SUNXI + help + Number of data bytes in one page for the NAND chip on the + board, not including the OOB area. + +config SYS_NAND_OOBSIZE + hex "NAND chip OOB size" + depends on ARCH_SUNXI + help + Number of bytes in the Out-Of-Band area for the NAND chip on + the board. + +# Enhance depends when converting drivers to Kconfig which use this config +# option (mxc_nand, ndfc, omap_gpmc). +config SYS_NAND_BUSWIDTH_16BIT + bool "Use 16-bit NAND interface" + depends on NAND_VF610_NFC || NAND_OMAP_GPMC || NAND_MXC || ARCH_DAVINCI + help + Indicates that NAND device has 16-bit wide data-bus. In absence of this + config, bus-width of NAND device is assumed to be either 8-bit and later + determined by reading ONFI params. + Above config is useful when NAND device's bus-width information cannot + be determined from on-chip ONFI params, like in following scenarios: + - SPL boot does not support reading of ONFI parameters. This is done to + keep SPL code foot-print small. + - In current U-Boot flow using nand_init(), driver initialization + happens in board_nand_init() which is called before any device probe + (nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are + not available while configuring controller. So a static CONFIG_NAND_xx + is needed to know the device's bus-width in advance. + +if SPL + +config SYS_NAND_U_BOOT_LOCATIONS + bool "Define U-boot binaries locations in NAND" + help + Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. + This option should not be enabled when compiling U-boot for boards + defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/.h + file. + +config SYS_NAND_U_BOOT_OFFS + hex "Location in NAND to read U-Boot from" + default 0x800000 if NAND_SUNXI + depends on SYS_NAND_U_BOOT_LOCATIONS + help + Set the offset from the start of the nand where u-boot should be + loaded from. + +config SYS_NAND_U_BOOT_OFFS_REDUND + hex "Location in NAND to read U-Boot from" + default SYS_NAND_U_BOOT_OFFS + depends on SYS_NAND_U_BOOT_LOCATIONS + help + Set the offset from the start of the nand where the redundant u-boot + should be loaded from. + +config SPL_NAND_AM33XX_BCH + bool "Enables SPL-NAND driver which supports ELM based" + depends on NAND_OMAP_GPMC && !OMAP34XX + default y + help + Hardware ECC correction. This is useful for platforms which have ELM + hardware engine and use NAND boot mode. + Some legacy platforms like OMAP3xx do not have in-built ELM h/w engine, + so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling + SPL-NAND driver with software ECC correction support. + +config SPL_NAND_DENALI + bool "Support Denali NAND controller for SPL" + help + This is a small implementation of the Denali NAND controller + for use on SPL. + +config SPL_NAND_SIMPLE + bool "Use simple SPL NAND driver" + depends on !SPL_NAND_AM33XX_BCH + help + Support for NAND boot using simple NAND drivers that + expose the cmd_ctrl() interface. +endif + +endif # if NAND diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile new file mode 100644 index 0000000000..c61e3f3839 --- /dev/null +++ b/drivers/mtd/nand/raw/Makefile @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +ifdef CONFIG_SPL_BUILD + +ifdef CONFIG_SPL_NAND_DRIVERS +NORMAL_DRIVERS=y +endif + +obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o +obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o +obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o +obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o +obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o +obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o +obj-$(CONFIG_SPL_NAND_INIT) += nand.o +ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) +obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o +endif + +else # not spl + +NORMAL_DRIVERS=y + +obj-y += nand.o +obj-y += nand_bbt.o +obj-y += nand_ids.o +obj-y += nand_util.o +obj-y += nand_ecc.o +obj-y += nand_base.o +obj-y += nand_timings.o + +endif # not spl + +ifdef NORMAL_DRIVERS + +obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o + +obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o +obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o +obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o +obj-$(CONFIG_NAND_DENALI) += denali.o +obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o +obj-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +obj-$(CONFIG_NAND_FSMC) += fsmc_nand.o +obj-$(CONFIG_NAND_KB9202) += kb9202_nand.o +obj-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o +obj-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o +obj-$(CONFIG_NAND_LPC32XX_MLC) += lpc32xx_nand_mlc.o +obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o +obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o +obj-$(CONFIG_NAND_MXC) += mxc_nand.o +obj-$(CONFIG_NAND_MXS) += mxs_nand.o +obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o +obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o +obj-$(CONFIG_NAND_SPEAR) += spr_nand.o +obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o +obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o +obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o +obj-$(CONFIG_NAND_PLAT) += nand_plat.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o +obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o + +else # minimal SPL drivers + +obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o +obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o +obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o +obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o + +endif # drivers diff --git a/drivers/mtd/nand/raw/am335x_spl_bch.c b/drivers/mtd/nand/raw/am335x_spl_bch.c new file mode 100644 index 0000000000..ba2f33a96e --- /dev/null +++ b/drivers/mtd/nand/raw/am335x_spl_bch.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2012 + * Konstantin Kozhevnikov, Cogent Embedded + * + * based on nand_spl_simple code + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + */ + +#include +#include +#include +#include + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static struct mtd_info *mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Begin command latch cycle */ + hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_RESET) { + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Apply this short delay always to ensure that we do wait + * tWB in any case on any machine. + */ + ndelay(150); + + while (!this->dev_ready(mtd)) + ; + return 0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) + offs >>= 1; + + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + if (cmd != NAND_CMD_RNDOUT) { + hwctrl(mtd, (page_addr & 0xff), + NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + } + + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + + /* + * Program and erase have their own busy handlers status, sequential + * in and status need no delay. + */ + switch (cmd) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + return 0; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + hwctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | + NAND_CTRL_CHANGE); + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + return 0; + + case NAND_CMD_READ0: + /* Latch in address */ + hwctrl(mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + } + + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(150); + + while (!this->dev_ready(mtd)) + ; + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + uint32_t data_pos = 0; + uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; + uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(mtd, NAND_ECC_READ); + nand_command(block, page, data_pos, NAND_CMD_RNDOUT); + + this->read_buf(mtd, p, eccsize); + + nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); + + this->read_buf(mtd, oob, eccbytes); + this->ecc.calculate(mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + oob += eccbytes; + } + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd = nand_to_mtd(&nand_chip); + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, 0); + + /* NAND chip may require reset after power-on */ + nand_command(0, 0, 0, NAND_CMD_RESET); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, -1); +} + +#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/raw/arasan_nfc.c b/drivers/mtd/nand/raw/arasan_nfc.c new file mode 100644 index 0000000000..41db9f8bb9 --- /dev/null +++ b/drivers/mtd/nand/raw/arasan_nfc.c @@ -0,0 +1,1270 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Arasan NAND Flash Controller Driver + * + * Copyright (C) 2014 - 2015 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct arasan_nand_info { + void __iomem *nand_base; + u32 page; + bool on_die_ecc_enabled; +}; + +struct nand_regs { + u32 pkt_reg; + u32 memadr_reg1; + u32 memadr_reg2; + u32 cmd_reg; + u32 pgm_reg; + u32 intsts_enr; + u32 intsig_enr; + u32 intsts_reg; + u32 rdy_busy; + u32 cms_sysadr_reg; + u32 flash_sts_reg; + u32 tmg_reg; + u32 buf_dataport; + u32 ecc_reg; + u32 ecc_errcnt_reg; + u32 ecc_sprcmd_reg; + u32 errcnt_1bitreg; + u32 errcnt_2bitreg; + u32 errcnt_3bitreg; + u32 errcnt_4bitreg; + u32 dma_sysadr0_reg; + u32 dma_bufbdry_reg; + u32 cpu_rls_reg; + u32 errcnt_5bitreg; + u32 errcnt_6bitreg; + u32 errcnt_7bitreg; + u32 errcnt_8bitreg; + u32 data_if_reg; +}; + +#define arasan_nand_base ((struct nand_regs __iomem *)ARASAN_NAND_BASEADDR) + +struct arasan_nand_command_format { + u8 cmd1; + u8 cmd2; + u8 addr_cycles; + u32 pgm; +}; + +#define ONDIE_ECC_FEATURE_ADDR 0x90 +#define ENABLE_ONDIE_ECC 0x08 + +#define ARASAN_PROG_RD_MASK 0x00000001 +#define ARASAN_PROG_BLK_ERS_MASK 0x00000004 +#define ARASAN_PROG_RD_ID_MASK 0x00000040 +#define ARASAN_PROG_RD_STS_MASK 0x00000008 +#define ARASAN_PROG_PG_PROG_MASK 0x00000010 +#define ARASAN_PROG_RD_PARAM_PG_MASK 0x00000080 +#define ARASAN_PROG_RST_MASK 0x00000100 +#define ARASAN_PROG_GET_FTRS_MASK 0x00000200 +#define ARASAN_PROG_SET_FTRS_MASK 0x00000400 +#define ARASAN_PROG_CHNG_ROWADR_END_MASK 0x00400000 + +#define ARASAN_NAND_CMD_ECC_ON_MASK 0x80000000 +#define ARASAN_NAND_CMD_CMD12_MASK 0xFFFF +#define ARASAN_NAND_CMD_PG_SIZE_MASK 0x3800000 +#define ARASAN_NAND_CMD_PG_SIZE_SHIFT 23 +#define ARASAN_NAND_CMD_CMD2_SHIFT 8 +#define ARASAN_NAND_CMD_ADDR_CYCL_MASK 0x70000000 +#define ARASAN_NAND_CMD_ADDR_CYCL_SHIFT 28 + +#define ARASAN_NAND_MEM_ADDR1_PAGE_MASK 0xFFFF0000 +#define ARASAN_NAND_MEM_ADDR1_COL_MASK 0xFFFF +#define ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT 16 +#define ARASAN_NAND_MEM_ADDR2_PAGE_MASK 0xFF +#define ARASAN_NAND_MEM_ADDR2_CS_MASK 0xC0000000 +#define ARASAN_NAND_MEM_ADDR2_BCH_MASK 0xE000000 +#define ARASAN_NAND_MEM_ADDR2_BCH_SHIFT 25 + +#define ARASAN_NAND_INT_STS_ERR_EN_MASK 0x10 +#define ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK 0x08 +#define ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK 0x02 +#define ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK 0x01 +#define ARASAN_NAND_INT_STS_XFR_CMPLT_MASK 0x04 + +#define ARASAN_NAND_PKT_REG_PKT_CNT_MASK 0xFFF000 +#define ARASAN_NAND_PKT_REG_PKT_SIZE_MASK 0x7FF +#define ARASAN_NAND_PKT_REG_PKT_CNT_SHFT 12 + +#define ARASAN_NAND_ROW_ADDR_CYCL_MASK 0x0F +#define ARASAN_NAND_COL_ADDR_CYCL_MASK 0xF0 +#define ARASAN_NAND_COL_ADDR_CYCL_SHIFT 4 + +#define ARASAN_NAND_ECC_SIZE_SHIFT 16 +#define ARASAN_NAND_ECC_BCH_SHIFT 27 + +#define ARASAN_NAND_PKTSIZE_1K 1024 +#define ARASAN_NAND_PKTSIZE_512 512 + +#define ARASAN_NAND_POLL_TIMEOUT 1000000 +#define ARASAN_NAND_INVALID_ADDR_CYCL 0xFF + +#define ERR_ADDR_CYCLE -1 +#define READ_BUFF_SIZE 0x4000 + +static struct arasan_nand_command_format *curr_cmd; + +enum addr_cycles { + NAND_ADDR_CYCL_NONE, + NAND_ADDR_CYCL_ONE, + NAND_ADDR_CYCL_ROW, + NAND_ADDR_CYCL_COL, + NAND_ADDR_CYCL_BOTH, +}; + +static struct arasan_nand_command_format arasan_nand_commands[] = { + {NAND_CMD_READ0, NAND_CMD_READSTART, NAND_ADDR_CYCL_BOTH, + ARASAN_PROG_RD_MASK}, + {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, NAND_ADDR_CYCL_COL, + ARASAN_PROG_RD_MASK}, + {NAND_CMD_READID, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, + ARASAN_PROG_RD_ID_MASK}, + {NAND_CMD_STATUS, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, + ARASAN_PROG_RD_STS_MASK}, + {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, NAND_ADDR_CYCL_BOTH, + ARASAN_PROG_PG_PROG_MASK}, + {NAND_CMD_RNDIN, NAND_CMD_NONE, NAND_ADDR_CYCL_COL, + ARASAN_PROG_CHNG_ROWADR_END_MASK}, + {NAND_CMD_ERASE1, NAND_CMD_ERASE2, NAND_ADDR_CYCL_ROW, + ARASAN_PROG_BLK_ERS_MASK}, + {NAND_CMD_RESET, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, + ARASAN_PROG_RST_MASK}, + {NAND_CMD_PARAM, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, + ARASAN_PROG_RD_PARAM_PG_MASK}, + {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, + ARASAN_PROG_GET_FTRS_MASK}, + {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, NAND_ADDR_CYCL_ONE, + ARASAN_PROG_SET_FTRS_MASK}, + {NAND_CMD_NONE, NAND_CMD_NONE, NAND_ADDR_CYCL_NONE, 0}, +}; + +struct arasan_ecc_matrix { + u32 pagesize; + u32 ecc_codeword_size; + u8 eccbits; + u8 bch; + u8 bchval; + u16 eccaddr; + u16 eccsize; +}; + +static const struct arasan_ecc_matrix ecc_matrix[] = { + {512, 512, 1, 0, 0, 0x20D, 0x3}, + {512, 512, 4, 1, 3, 0x209, 0x7}, + {512, 512, 8, 1, 2, 0x203, 0xD}, + /* + * 2K byte page + */ + {2048, 512, 1, 0, 0, 0x834, 0xC}, + {2048, 512, 4, 1, 3, 0x826, 0x1A}, + {2048, 512, 8, 1, 2, 0x80c, 0x34}, + {2048, 512, 12, 1, 1, 0x822, 0x4E}, + {2048, 512, 16, 1, 0, 0x808, 0x68}, + {2048, 1024, 24, 1, 4, 0x81c, 0x54}, + /* + * 4K byte page + */ + {4096, 512, 1, 0, 0, 0x1068, 0x18}, + {4096, 512, 4, 1, 3, 0x104c, 0x34}, + {4096, 512, 8, 1, 2, 0x1018, 0x68}, + {4096, 512, 12, 1, 1, 0x1044, 0x9C}, + {4096, 512, 16, 1, 0, 0x1010, 0xD0}, + {4096, 1024, 24, 1, 4, 0x1038, 0xA8}, + /* + * 8K byte page + */ + {8192, 512, 1, 0, 0, 0x20d0, 0x30}, + {8192, 512, 4, 1, 3, 0x2098, 0x68}, + {8192, 512, 8, 1, 2, 0x2030, 0xD0}, + {8192, 512, 12, 1, 1, 0x2088, 0x138}, + {8192, 512, 16, 1, 0, 0x2020, 0x1A0}, + {8192, 1024, 24, 1, 4, 0x2070, 0x150}, + /* + * 16K byte page + */ + {16384, 512, 1, 0, 0, 0x4460, 0x60}, + {16384, 512, 4, 1, 3, 0x43f0, 0xD0}, + {16384, 512, 8, 1, 2, 0x4320, 0x1A0}, + {16384, 512, 12, 1, 1, 0x4250, 0x270}, + {16384, 512, 16, 1, 0, 0x4180, 0x340}, + {16384, 1024, 24, 1, 4, 0x4220, 0x2A0} +}; + +static struct nand_ecclayout ondie_nand_oob_64 = { + .eccbytes = 32, + + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 24, 25, 26, 27, 28, 29, 30, 31, + 40, 41, 42, 43, 44, 45, 46, 47, + 56, 57, 58, 59, 60, 61, 62, 63 + }, + + .oobfree = { + { .offset = 4, .length = 4 }, + { .offset = 20, .length = 4 }, + { .offset = 36, .length = 4 }, + { .offset = 52, .length = 4 } + } +}; + +/* + * bbt decriptors for chips with on-die ECC and + * chips with 64-byte OOB + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +static u8 buf_data[READ_BUFF_SIZE]; +static u32 buf_index; + +static struct nand_ecclayout nand_oob; + +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; + +static void arasan_nand_select_chip(struct mtd_info *mtd, int chip) +{ +} + +static void arasan_nand_enable_ecc(void) +{ + u32 reg_val; + + reg_val = readl(&arasan_nand_base->cmd_reg); + reg_val |= ARASAN_NAND_CMD_ECC_ON_MASK; + + writel(reg_val, &arasan_nand_base->cmd_reg); +} + +static u8 arasan_nand_get_addrcycle(struct mtd_info *mtd) +{ + u8 addrcycles; + struct nand_chip *chip = mtd_to_nand(mtd); + + switch (curr_cmd->addr_cycles) { + case NAND_ADDR_CYCL_NONE: + addrcycles = 0; + break; + case NAND_ADDR_CYCL_ONE: + addrcycles = 1; + break; + case NAND_ADDR_CYCL_ROW: + addrcycles = chip->onfi_params.addr_cycles & + ARASAN_NAND_ROW_ADDR_CYCL_MASK; + break; + case NAND_ADDR_CYCL_COL: + addrcycles = (chip->onfi_params.addr_cycles & + ARASAN_NAND_COL_ADDR_CYCL_MASK) >> + ARASAN_NAND_COL_ADDR_CYCL_SHIFT; + break; + case NAND_ADDR_CYCL_BOTH: + addrcycles = chip->onfi_params.addr_cycles & + ARASAN_NAND_ROW_ADDR_CYCL_MASK; + addrcycles += (chip->onfi_params.addr_cycles & + ARASAN_NAND_COL_ADDR_CYCL_MASK) >> + ARASAN_NAND_COL_ADDR_CYCL_SHIFT; + break; + default: + addrcycles = ARASAN_NAND_INVALID_ADDR_CYCL; + break; + } + return addrcycles; +} + +static int arasan_nand_read_page(struct mtd_info *mtd, u8 *buf, u32 size) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct arasan_nand_info *nand = nand_get_controller_data(chip); + u32 reg_val, i, pktsize, pktnum; + u32 *bufptr = (u32 *)buf; + u32 timeout; + u32 rdcount = 0; + u8 addr_cycles; + + if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K) + pktsize = ARASAN_NAND_PKTSIZE_1K; + else + pktsize = ARASAN_NAND_PKTSIZE_512; + + if (size % pktsize) + pktnum = size/pktsize + 1; + else + pktnum = size/pktsize; + + reg_val = readl(&arasan_nand_base->intsts_enr); + reg_val |= ARASAN_NAND_INT_STS_ERR_EN_MASK | + ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK; + writel(reg_val, &arasan_nand_base->intsts_enr); + + reg_val = readl(&arasan_nand_base->pkt_reg); + reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | + ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); + reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | + pktsize; + writel(reg_val, &arasan_nand_base->pkt_reg); + + if (!nand->on_die_ecc_enabled) { + arasan_nand_enable_ecc(); + addr_cycles = arasan_nand_get_addrcycle(mtd); + if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) + return ERR_ADDR_CYCLE; + + writel((NAND_CMD_RNDOUTSTART << ARASAN_NAND_CMD_CMD2_SHIFT) | + NAND_CMD_RNDOUT | (addr_cycles << + ARASAN_NAND_CMD_ADDR_CYCL_SHIFT), + &arasan_nand_base->ecc_sprcmd_reg); + } + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (rdcount < pktnum) { + timeout = ARASAN_NAND_POLL_TIMEOUT; + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) { + puts("arasan_read_page: timedout:Buff RDY\n"); + return -ETIMEDOUT; + } + + rdcount++; + + if (pktnum == rdcount) { + reg_val = readl(&arasan_nand_base->intsts_enr); + reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; + writel(reg_val, &arasan_nand_base->intsts_enr); + } else { + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_enr); + } + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_reg); + + for (i = 0; i < pktsize/4; i++) + bufptr[i] = readl(&arasan_nand_base->buf_dataport); + + + bufptr += pktsize/4; + + if (rdcount >= pktnum) + break; + + writel(ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_enr); + } + + timeout = ARASAN_NAND_POLL_TIMEOUT; + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) { + puts("arasan rd_page timedout:Xfer CMPLT\n"); + return -ETIMEDOUT; + } + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); + + if (!nand->on_die_ecc_enabled) { + if (readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_MUL_BIT_ERR_MASK) { + printf("arasan rd_page:sbiterror\n"); + return -1; + } + + if (readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_ERR_EN_MASK) { + mtd->ecc_stats.failed++; + printf("arasan rd_page:multibiterror\n"); + return -1; + } + } + + return 0; +} + +static int arasan_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + int status; + + status = arasan_nand_read_page(mtd, buf, (mtd->writesize)); + + if (oob_required) + chip->ecc.read_oob(mtd, chip, page); + + return status; +} + +static void arasan_nand_fill_tx(const u8 *buf, int len) +{ + u32 __iomem *nand = &arasan_nand_base->buf_dataport; + + if (((unsigned long)buf & 0x3) != 0) { + if (((unsigned long)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((unsigned long)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + while (len >= 4) { + writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + +static int arasan_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, + int page) +{ + u32 reg_val, i, pktsize, pktnum; + const u32 *bufptr = (const u32 *)buf; + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + u32 size = mtd->writesize; + u32 rdcount = 0; + u8 column_addr_cycles; + struct arasan_nand_info *nand = nand_get_controller_data(chip); + + if (chip->ecc_step_ds >= ARASAN_NAND_PKTSIZE_1K) + pktsize = ARASAN_NAND_PKTSIZE_1K; + else + pktsize = ARASAN_NAND_PKTSIZE_512; + + if (size % pktsize) + pktnum = size/pktsize + 1; + else + pktnum = size/pktsize; + + reg_val = readl(&arasan_nand_base->pkt_reg); + reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | + ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); + reg_val |= (pktnum << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | pktsize; + writel(reg_val, &arasan_nand_base->pkt_reg); + + if (!nand->on_die_ecc_enabled) { + arasan_nand_enable_ecc(); + column_addr_cycles = (chip->onfi_params.addr_cycles & + ARASAN_NAND_COL_ADDR_CYCL_MASK) >> + ARASAN_NAND_COL_ADDR_CYCL_SHIFT; + writel((NAND_CMD_RNDIN | (column_addr_cycles << 28)), + &arasan_nand_base->ecc_sprcmd_reg); + } + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (rdcount < pktnum) { + timeout = ARASAN_NAND_POLL_TIMEOUT; + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) { + udelay(1); + timeout--; + } + + if (!timeout) { + puts("arasan_write_page: timedout:Buff RDY\n"); + return -ETIMEDOUT; + } + + rdcount++; + + if (pktnum == rdcount) { + reg_val = readl(&arasan_nand_base->intsts_enr); + reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; + writel(reg_val, &arasan_nand_base->intsts_enr); + } else { + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_enr); + } + + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_reg); + + for (i = 0; i < pktsize/4; i++) + writel(bufptr[i], &arasan_nand_base->buf_dataport); + + bufptr += pktsize/4; + + if (rdcount >= pktnum) + break; + + writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_enr); + } + + timeout = ARASAN_NAND_POLL_TIMEOUT; + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) { + puts("arasan write_page timedout:Xfer CMPLT\n"); + return -ETIMEDOUT; + } + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); + + if (oob_required) + chip->ecc.write_oob(mtd, chip, nand->page); + + return 0; +} + +static int arasan_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, (mtd->oobsize)); + + return 0; +} + +static int arasan_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const u8 *buf = chip->oob_poi; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, mtd->oobsize); + + return status; +} + +static int arasan_nand_reset(struct arasan_nand_command_format *curr_cmd) +{ + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + u32 cmd_reg = 0; + + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + cmd_reg = readl(&arasan_nand_base->cmd_reg); + cmd_reg &= ~ARASAN_NAND_CMD_CMD12_MASK; + + cmd_reg |= curr_cmd->cmd1 | + (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); + writel(cmd_reg, &arasan_nand_base->cmd_reg); + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) { + printf("ERROR:%s timedout\n", __func__); + return -ETIMEDOUT; + } + + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); + + return 0; +} + +static u8 arasan_nand_page(struct mtd_info *mtd) +{ + u8 page_val = 0; + + switch (mtd->writesize) { + case 512: + page_val = 0; + break; + case 2048: + page_val = 1; + break; + case 4096: + page_val = 2; + break; + case 8192: + page_val = 3; + break; + case 16384: + page_val = 4; + break; + case 1024: + page_val = 5; + break; + default: + printf("%s:Pagesize>16K\n", __func__); + break; + } + + return page_val; +} + +static int arasan_nand_send_wrcmd(struct arasan_nand_command_format *curr_cmd, + int column, int page_addr, struct mtd_info *mtd) +{ + u32 reg_val, page; + u8 page_val, addr_cycles; + + writel(ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->cmd_reg); + reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; + reg_val |= curr_cmd->cmd1 | + (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); + if (curr_cmd->cmd1 == NAND_CMD_SEQIN) { + reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK; + page_val = arasan_nand_page(mtd); + reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT); + } + + reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; + addr_cycles = arasan_nand_get_addrcycle(mtd); + + if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) + return ERR_ADDR_CYCLE; + + reg_val |= (addr_cycles << + ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); + writel(reg_val, &arasan_nand_base->cmd_reg); + + if (page_addr == -1) + page_addr = 0; + + page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & + ARASAN_NAND_MEM_ADDR1_PAGE_MASK; + column &= ARASAN_NAND_MEM_ADDR1_COL_MASK; + writel(page|column, &arasan_nand_base->memadr_reg1); + + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; + reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); + writel(reg_val, &arasan_nand_base->memadr_reg2); + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; + writel(reg_val, &arasan_nand_base->memadr_reg2); + + return 0; +} + +static void arasan_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + u32 reg_val; + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + + reg_val = readl(&arasan_nand_base->pkt_reg); + reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | + ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); + + reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | len; + writel(reg_val, &arasan_nand_base->pkt_reg); + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK) && timeout) { + udelay(1); + timeout--; + } + + if (!timeout) + puts("ERROR:arasan_nand_write_buf timedout:Buff RDY\n"); + + reg_val = readl(&arasan_nand_base->intsts_enr); + reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; + writel(reg_val, &arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_WR_RDY_MASK, + &arasan_nand_base->intsts_reg); + + arasan_nand_fill_tx(buf, len); + + timeout = ARASAN_NAND_POLL_TIMEOUT; + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) + puts("ERROR:arasan_nand_write_buf timedout:Xfer CMPLT\n"); + + writel(readl(&arasan_nand_base->intsts_enr) | + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + writel(readl(&arasan_nand_base->intsts_reg) | + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); +} + +static int arasan_nand_erase(struct arasan_nand_command_format *curr_cmd, + int column, int page_addr, struct mtd_info *mtd) +{ + u32 reg_val, page; + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + u8 row_addr_cycles; + + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->cmd_reg); + reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; + reg_val |= curr_cmd->cmd1 | + (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); + row_addr_cycles = arasan_nand_get_addrcycle(mtd); + + if (row_addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) + return ERR_ADDR_CYCLE; + + reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; + reg_val |= (row_addr_cycles << + ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); + + writel(reg_val, &arasan_nand_base->cmd_reg); + + page = (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & + ARASAN_NAND_MEM_ADDR1_COL_MASK; + column = page_addr & ARASAN_NAND_MEM_ADDR1_COL_MASK; + writel(column | (page << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT), + &arasan_nand_base->memadr_reg1); + + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; + reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); + writel(reg_val, &arasan_nand_base->memadr_reg2); + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; + writel(reg_val, &arasan_nand_base->memadr_reg2); + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + if (!timeout) { + printf("ERROR:%s timedout:Xfer CMPLT\n", __func__); + return -ETIMEDOUT; + } + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); + + return 0; +} + +static int arasan_nand_read_status(struct arasan_nand_command_format *curr_cmd, + int column, int page_addr, struct mtd_info *mtd) +{ + u32 reg_val; + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + u8 addr_cycles; + + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->cmd_reg); + reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; + reg_val |= curr_cmd->cmd1 | + (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); + addr_cycles = arasan_nand_get_addrcycle(mtd); + + if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) + return ERR_ADDR_CYCLE; + + reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; + reg_val |= (addr_cycles << + ARASAN_NAND_CMD_ADDR_CYCL_SHIFT); + + writel(reg_val, &arasan_nand_base->cmd_reg); + + reg_val = readl(&arasan_nand_base->pkt_reg); + reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | + ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); + reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | 1; + writel(reg_val, &arasan_nand_base->pkt_reg); + + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; + writel(reg_val, &arasan_nand_base->memadr_reg2); + + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + + if (!timeout) { + printf("ERROR:%s: timedout:Xfer CMPLT\n", __func__); + return -ETIMEDOUT; + } + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); + + return 0; +} + +static int arasan_nand_send_rdcmd(struct arasan_nand_command_format *curr_cmd, + int column, int page_addr, struct mtd_info *mtd) +{ + u32 reg_val, addr_cycles, page; + u8 page_val; + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_enr); + + reg_val = readl(&arasan_nand_base->cmd_reg); + reg_val &= ~ARASAN_NAND_CMD_CMD12_MASK; + reg_val |= curr_cmd->cmd1 | + (curr_cmd->cmd2 << ARASAN_NAND_CMD_CMD2_SHIFT); + + if (curr_cmd->cmd1 == NAND_CMD_RNDOUT || + curr_cmd->cmd1 == NAND_CMD_READ0) { + reg_val &= ~ARASAN_NAND_CMD_PG_SIZE_MASK; + page_val = arasan_nand_page(mtd); + reg_val |= (page_val << ARASAN_NAND_CMD_PG_SIZE_SHIFT); + } + + reg_val &= ~ARASAN_NAND_CMD_ECC_ON_MASK; + + reg_val &= ~ARASAN_NAND_CMD_ADDR_CYCL_MASK; + + addr_cycles = arasan_nand_get_addrcycle(mtd); + + if (addr_cycles == ARASAN_NAND_INVALID_ADDR_CYCL) + return ERR_ADDR_CYCLE; + + reg_val |= (addr_cycles << 28); + writel(reg_val, &arasan_nand_base->cmd_reg); + + if (page_addr == -1) + page_addr = 0; + + page = (page_addr << ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT) & + ARASAN_NAND_MEM_ADDR1_PAGE_MASK; + column &= ARASAN_NAND_MEM_ADDR1_COL_MASK; + writel(page | column, &arasan_nand_base->memadr_reg1); + + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_PAGE_MASK; + reg_val |= (page_addr >> ARASAN_NAND_MEM_ADDR1_PAGE_SHIFT); + writel(reg_val, &arasan_nand_base->memadr_reg2); + + reg_val = readl(&arasan_nand_base->memadr_reg2); + reg_val &= ~ARASAN_NAND_MEM_ADDR2_CS_MASK; + writel(reg_val, &arasan_nand_base->memadr_reg2); + buf_index = 0; + + return 0; +} + +static void arasan_nand_read_buf(struct mtd_info *mtd, u8 *buf, int size) +{ + u32 reg_val, i; + u32 *bufptr = (u32 *)buf; + u32 timeout = ARASAN_NAND_POLL_TIMEOUT; + + reg_val = readl(&arasan_nand_base->pkt_reg); + reg_val &= ~(ARASAN_NAND_PKT_REG_PKT_CNT_MASK | + ARASAN_NAND_PKT_REG_PKT_SIZE_MASK); + reg_val |= (1 << ARASAN_NAND_PKT_REG_PKT_CNT_SHFT) | size; + writel(reg_val, &arasan_nand_base->pkt_reg); + + writel(curr_cmd->pgm, &arasan_nand_base->pgm_reg); + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK) && timeout) { + udelay(1); + timeout--; + } + + if (!timeout) + puts("ERROR:arasan_nand_read_buf timedout:Buff RDY\n"); + + reg_val = readl(&arasan_nand_base->intsts_enr); + reg_val |= ARASAN_NAND_INT_STS_XFR_CMPLT_MASK; + writel(reg_val, &arasan_nand_base->intsts_enr); + + writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_BUF_RD_RDY_MASK, + &arasan_nand_base->intsts_reg); + + buf_index = 0; + for (i = 0; i < size / 4; i++) + bufptr[i] = readl(&arasan_nand_base->buf_dataport); + + if (size & 0x03) + bufptr[i] = readl(&arasan_nand_base->buf_dataport); + + timeout = ARASAN_NAND_POLL_TIMEOUT; + + while (!(readl(&arasan_nand_base->intsts_reg) & + ARASAN_NAND_INT_STS_XFR_CMPLT_MASK) && timeout) { + udelay(1); + timeout--; + } + + if (!timeout) + puts("ERROR:arasan_nand_read_buf timedout:Xfer CMPLT\n"); + + reg_val = readl(&arasan_nand_base->intsts_enr); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + reg_val = readl(&arasan_nand_base->intsts_reg); + writel(reg_val | ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_reg); +} + +static u8 arasan_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u32 size; + u8 val; + struct nand_onfi_params *p; + + if (buf_index == 0) { + p = &chip->onfi_params; + if (curr_cmd->cmd1 == NAND_CMD_READID) + size = 4; + else if (curr_cmd->cmd1 == NAND_CMD_PARAM) + size = sizeof(struct nand_onfi_params); + else if (curr_cmd->cmd1 == NAND_CMD_RNDOUT) + size = le16_to_cpu(p->ext_param_page_length) * 16; + else if (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES) + size = 4; + else if (curr_cmd->cmd1 == NAND_CMD_STATUS) + return readb(&arasan_nand_base->flash_sts_reg); + else + size = 8; + chip->read_buf(mtd, &buf_data[0], size); + } + + val = *(&buf_data[0] + buf_index); + buf_index++; + + return val; +} + +static void arasan_nand_cmd_function(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + u32 i, ret = 0; + struct nand_chip *chip = mtd_to_nand(mtd); + struct arasan_nand_info *nand = nand_get_controller_data(chip); + + curr_cmd = NULL; + writel(ARASAN_NAND_INT_STS_XFR_CMPLT_MASK, + &arasan_nand_base->intsts_enr); + + if ((command == NAND_CMD_READOOB) && + (mtd->writesize > 512)) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Get the command format */ + for (i = 0; (arasan_nand_commands[i].cmd1 != NAND_CMD_NONE || + arasan_nand_commands[i].cmd2 != NAND_CMD_NONE); i++) { + if (command == arasan_nand_commands[i].cmd1) { + curr_cmd = &arasan_nand_commands[i]; + break; + } + } + + if (curr_cmd == NULL) { + printf("Unsupported Command; 0x%x\n", command); + return; + } + + if (curr_cmd->cmd1 == NAND_CMD_RESET) + ret = arasan_nand_reset(curr_cmd); + + if ((curr_cmd->cmd1 == NAND_CMD_READID) || + (curr_cmd->cmd1 == NAND_CMD_PARAM) || + (curr_cmd->cmd1 == NAND_CMD_RNDOUT) || + (curr_cmd->cmd1 == NAND_CMD_GET_FEATURES) || + (curr_cmd->cmd1 == NAND_CMD_READ0)) + ret = arasan_nand_send_rdcmd(curr_cmd, column, page_addr, mtd); + + if ((curr_cmd->cmd1 == NAND_CMD_SET_FEATURES) || + (curr_cmd->cmd1 == NAND_CMD_SEQIN)) { + nand->page = page_addr; + ret = arasan_nand_send_wrcmd(curr_cmd, column, page_addr, mtd); + } + + if (curr_cmd->cmd1 == NAND_CMD_ERASE1) + ret = arasan_nand_erase(curr_cmd, column, page_addr, mtd); + + if (curr_cmd->cmd1 == NAND_CMD_STATUS) + ret = arasan_nand_read_status(curr_cmd, column, page_addr, mtd); + + if (ret != 0) + printf("ERROR:%s:command:0x%x\n", __func__, curr_cmd->cmd1); +} + +static void arasan_check_ondie(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct arasan_nand_info *nand = nand_get_controller_data(nand_chip); + u8 maf_id, dev_id; + u8 get_feature[4]; + u8 set_feature[4] = {ENABLE_ONDIE_ECC, 0x00, 0x00, 0x00}; + u32 i; + + /* Send the command for reading device ID */ + nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0, -1); + + /* Read manufacturer and device IDs */ + maf_id = nand_chip->read_byte(mtd); + dev_id = nand_chip->read_byte(mtd); + + if ((maf_id == NAND_MFR_MICRON) && + ((dev_id == 0xf1) || (dev_id == 0xa1) || (dev_id == 0xb1) || + (dev_id == 0xaa) || (dev_id == 0xba) || (dev_id == 0xda) || + (dev_id == 0xca) || (dev_id == 0xac) || (dev_id == 0xbc) || + (dev_id == 0xdc) || (dev_id == 0xcc) || (dev_id == 0xa3) || + (dev_id == 0xb3) || (dev_id == 0xd3) || (dev_id == 0xc3))) { + nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + + nand_chip->write_buf(mtd, &set_feature[0], 4); + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + + for (i = 0; i < 4; i++) + get_feature[i] = nand_chip->read_byte(mtd); + + if (get_feature[0] & ENABLE_ONDIE_ECC) + nand->on_die_ecc_enabled = true; + else + printf("%s: Unable to enable OnDie ECC\n", __func__); + + /* Use the BBT pattern descriptors */ + nand_chip->bbt_td = &bbt_main_descr; + nand_chip->bbt_md = &bbt_mirror_descr; + } +} + +static int arasan_nand_ecc_init(struct mtd_info *mtd) +{ + int found = -1; + u32 regval, eccpos_start, i, eccaddr; + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) { + if ((ecc_matrix[i].pagesize == mtd->writesize) && + (ecc_matrix[i].ecc_codeword_size >= + nand_chip->ecc_step_ds)) { + if (ecc_matrix[i].eccbits >= + nand_chip->ecc_strength_ds) { + found = i; + break; + } + found = i; + } + } + + if (found < 0) + return 1; + + eccaddr = mtd->writesize + mtd->oobsize - + ecc_matrix[found].eccsize; + + regval = eccaddr | + (ecc_matrix[found].eccsize << ARASAN_NAND_ECC_SIZE_SHIFT) | + (ecc_matrix[found].bch << ARASAN_NAND_ECC_BCH_SHIFT); + writel(regval, &arasan_nand_base->ecc_reg); + + if (ecc_matrix[found].bch) { + regval = readl(&arasan_nand_base->memadr_reg2); + regval &= ~ARASAN_NAND_MEM_ADDR2_BCH_MASK; + regval |= (ecc_matrix[found].bchval << + ARASAN_NAND_MEM_ADDR2_BCH_SHIFT); + writel(regval, &arasan_nand_base->memadr_reg2); + } + + nand_oob.eccbytes = ecc_matrix[found].eccsize; + eccpos_start = mtd->oobsize - nand_oob.eccbytes; + + for (i = 0; i < nand_oob.eccbytes; i++) + nand_oob.eccpos[i] = eccpos_start + i; + + nand_oob.oobfree[0].offset = 2; + nand_oob.oobfree[0].length = eccpos_start - 2; + + nand_chip->ecc.size = ecc_matrix[found].ecc_codeword_size; + nand_chip->ecc.strength = ecc_matrix[found].eccbits; + nand_chip->ecc.bytes = ecc_matrix[found].eccsize; + nand_chip->ecc.layout = &nand_oob; + + return 0; +} + +static int arasan_nand_init(struct nand_chip *nand_chip, int devnum) +{ + struct arasan_nand_info *nand; + struct mtd_info *mtd; + int err = -1; + + nand = calloc(1, sizeof(struct arasan_nand_info)); + if (!nand) { + printf("%s: failed to allocate\n", __func__); + return err; + } + + nand->nand_base = arasan_nand_base; + mtd = nand_to_mtd(nand_chip); + nand_set_controller_data(nand_chip, nand); + + /* Set the driver entry points for MTD */ + nand_chip->cmdfunc = arasan_nand_cmd_function; + nand_chip->select_chip = arasan_nand_select_chip; + nand_chip->read_byte = arasan_nand_read_byte; + + /* Buffer read/write routines */ + nand_chip->read_buf = arasan_nand_read_buf; + nand_chip->write_buf = arasan_nand_write_buf; + nand_chip->bbt_options = NAND_BBT_USE_FLASH; + + writel(0x0, &arasan_nand_base->cmd_reg); + writel(0x0, &arasan_nand_base->pgm_reg); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + printf("%s: nand_scan_ident failed\n", __func__); + goto fail; + } + + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = arasan_nand_read_page_hwecc; + nand_chip->ecc.write_page = arasan_nand_write_page_hwecc; + nand_chip->ecc.read_oob = arasan_nand_read_oob; + nand_chip->ecc.write_oob = arasan_nand_write_oob; + + arasan_check_ondie(mtd); + + /* + * If on die supported, then give priority to on-die ecc and use + * it instead of controller ecc. + */ + if (nand->on_die_ecc_enabled) { + nand_chip->ecc.strength = 1; + nand_chip->ecc.size = mtd->writesize; + nand_chip->ecc.bytes = 0; + nand_chip->ecc.layout = &ondie_nand_oob_64; + } else { + if (arasan_nand_ecc_init(mtd)) { + printf("%s: nand_ecc_init failed\n", __func__); + goto fail; + } + } + + if (nand_scan_tail(mtd)) { + printf("%s: nand_scan_tail failed\n", __func__); + goto fail; + } + + if (nand_register(devnum, mtd)) { + printf("Nand Register Fail\n"); + goto fail; + } + + return 0; +fail: + free(nand); + return err; +} + +void board_nand_init(void) +{ + struct nand_chip *nand = &nand_chip[0]; + + if (arasan_nand_init(nand, 0)) + puts("NAND init failed\n"); +} diff --git a/drivers/mtd/nand/raw/atmel_nand.c b/drivers/mtd/nand/raw/atmel_nand.c new file mode 100644 index 0000000000..a5b76e1aa0 --- /dev/null +++ b/drivers/mtd/nand/raw/atmel_nand.c @@ -0,0 +1,1511 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2007-2008 + * Stelian Pop + * Lead Tech Design + * + * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * (C) Copyright 2012 ATMEL, Hong Xu + */ + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_ATMEL_NAND_HWECC + +/* Register access macros */ +#define ecc_readl(add, reg) \ + readl(add + ATMEL_ECC_##reg) +#define ecc_writel(add, reg, value) \ + writel((value), add + ATMEL_ECC_##reg) + +#include "atmel_nand_ecc.h" /* Hardware ECC registers */ + +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + +#ifdef CONFIG_SPL_BUILD +#undef CONFIG_SYS_NAND_ONFI_DETECTION +#endif + +struct atmel_nand_host { + struct pmecc_regs __iomem *pmecc; + struct pmecc_errloc_regs __iomem *pmerrloc; + void __iomem *pmecc_rom_base; + + u8 pmecc_corr_cap; + u16 pmecc_sector_size; + u32 pmecc_index_table_offset; + u32 pmecc_version; + + int pmecc_bytes_per_sector; + int pmecc_sector_number; + int pmecc_degree; /* Degree of remainders */ + int pmecc_cw_len; /* Length of codeword */ + + /* lookup table for alpha_to and index_of */ + void __iomem *pmecc_alpha_to; + void __iomem *pmecc_index_of; + + /* data for pmecc computation */ + int16_t *pmecc_smu; + int16_t *pmecc_partial_syn; + int16_t *pmecc_si; + int16_t *pmecc_lmu; /* polynomal order */ + int *pmecc_mu; + int *pmecc_dmu; + int *pmecc_delta; +}; + +static struct atmel_nand_host pmecc_host; +static struct nand_ecclayout atmel_pmecc_oobinfo; + +/* + * Return number of ecc bytes per sector according to sector size and + * correction capability + * + * Following table shows what at91 PMECC supported: + * Correction Capability Sector_512_bytes Sector_1024_bytes + * ===================== ================ ================= + * 2-bits 4-bytes 4-bytes + * 4-bits 7-bytes 7-bytes + * 8-bits 13-bytes 14-bytes + * 12-bits 20-bytes 21-bytes + * 24-bits 39-bytes 42-bytes + * 32-bits 52-bytes 56-bytes + */ +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ + int m = 12 + sector_size / 512; + return (m * cap + 7) / 8; +} + +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, + int oobsize, int ecc_len) +{ + int i; + + layout->eccbytes = ecc_len; + + /* ECC will occupy the last ecc_len bytes continuously */ + for (i = 0; i < ecc_len; i++) + layout->eccpos[i] = oobsize - ecc_len + i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = + oobsize - ecc_len - layout->oobfree[0].offset; +} + +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +{ + int table_size; + + table_size = host->pmecc_sector_size == 512 ? + PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024; + + /* the ALPHA lookup table is right behind the INDEX lookup table. */ + return host->pmecc_rom_base + host->pmecc_index_table_offset + + table_size * sizeof(int16_t); +} + +static void pmecc_data_free(struct atmel_nand_host *host) +{ + free(host->pmecc_partial_syn); + free(host->pmecc_si); + free(host->pmecc_lmu); + free(host->pmecc_smu); + free(host->pmecc_mu); + free(host->pmecc_dmu); + free(host->pmecc_delta); +} + +static int pmecc_data_alloc(struct atmel_nand_host *host) +{ + const int cap = host->pmecc_corr_cap; + int size; + + size = (2 * cap + 1) * sizeof(int16_t); + host->pmecc_partial_syn = malloc(size); + host->pmecc_si = malloc(size); + host->pmecc_lmu = malloc((cap + 1) * sizeof(int16_t)); + host->pmecc_smu = malloc((cap + 2) * size); + + size = (cap + 1) * sizeof(int); + host->pmecc_mu = malloc(size); + host->pmecc_dmu = malloc(size); + host->pmecc_delta = malloc(size); + + if (host->pmecc_partial_syn && + host->pmecc_si && + host->pmecc_lmu && + host->pmecc_smu && + host->pmecc_mu && + host->pmecc_dmu && + host->pmecc_delta) + return 0; + + /* error happened */ + pmecc_data_free(host); + return -ENOMEM; + +} + +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + int i; + uint32_t value; + + /* Fill odd syndromes */ + for (i = 0; i < host->pmecc_corr_cap; i++) { + value = pmecc_readl(host->pmecc, rem_port[sector].rem[i / 2]); + if (i & 1) + value >>= 16; + value &= 0xffff; + host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; + } +} + +static void pmecc_substitute(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t *partial_syn = host->pmecc_partial_syn; + const int cap = host->pmecc_corr_cap; + int16_t *si; + int i, j; + + /* si[] is a table that holds the current syndrome value, + * an element of that table belongs to the field + */ + si = host->pmecc_si; + + memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); + + /* Computation 2t syndromes based on S(x) */ + /* Odd syndromes */ + for (i = 1; i < 2 * cap; i += 2) { + for (j = 0; j < host->pmecc_degree; j++) { + if (partial_syn[i] & (0x1 << j)) + si[i] = readw(alpha_to + i * j) ^ si[i]; + } + } + /* Even syndrome = (Odd syndrome) ** 2 */ + for (i = 2, j = 1; j <= cap; i = ++j << 1) { + if (si[j] == 0) { + si[i] = 0; + } else { + int16_t tmp; + + tmp = readw(index_of + si[j]); + tmp = (tmp * 2) % host->pmecc_cw_len; + si[i] = readw(alpha_to + tmp); + } + } +} + +/* + * This function defines a Berlekamp iterative procedure for + * finding the value of the error location polynomial. + * The input is si[], initialize by pmecc_substitute(). + * The output is smu[][]. + * + * This function is written according to chip datasheet Chapter: + * Find the Error Location Polynomial Sigma(x) of Section: + * Programmable Multibit ECC Control (PMECC). + */ +static void pmecc_get_sigma(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + + int16_t *lmu = host->pmecc_lmu; + int16_t *si = host->pmecc_si; + int *mu = host->pmecc_mu; + int *dmu = host->pmecc_dmu; /* Discrepancy */ + int *delta = host->pmecc_delta; /* Delta order */ + int cw_len = host->pmecc_cw_len; + const int16_t cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int16_t __iomem *index_of = host->pmecc_index_of; + int16_t __iomem *alpha_to = host->pmecc_alpha_to; + int i, j, k; + uint32_t dmu_0_count, tmp; + int16_t *smu = host->pmecc_smu; + + /* index of largest delta */ + int ro; + int largest; + int diff; + + /* Init the Sigma(x) */ + memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu)); + + dmu_0_count = 0; + + /* First Row */ + + /* Mu */ + mu[0] = -1; + + smu[0] = 1; + + /* discrepancy set to 1 */ + dmu[0] = 1; + /* polynom order set to 0 */ + lmu[0] = 0; + /* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */ + delta[0] = -1; + + /* Second Row */ + + /* Mu */ + mu[1] = 0; + /* Sigma(x) set to 1 */ + smu[num] = 1; + + /* discrepancy set to S1 */ + dmu[1] = si[1]; + + /* polynom order set to 0 */ + lmu[1] = 0; + + /* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */ + delta[1] = 0; + + for (i = 1; i <= cap; i++) { + mu[i + 1] = i << 1; + /* Begin Computing Sigma (Mu+1) and L(mu) */ + /* check if discrepancy is set to 0 */ + if (dmu[i] == 0) { + dmu_0_count++; + + tmp = ((cap - (lmu[i] >> 1) - 1) / 2); + if ((cap - (lmu[i] >> 1) - 1) & 0x1) + tmp += 2; + else + tmp += 1; + + if (dmu_0_count == tmp) { + for (j = 0; j <= (lmu[i] >> 1) + 1; j++) + smu[(cap + 1) * num + j] = + smu[i * num + j]; + + lmu[cap + 1] = lmu[i]; + return; + } + + /* copy polynom */ + for (j = 0; j <= lmu[i] >> 1; j++) + smu[(i + 1) * num + j] = smu[i * num + j]; + + /* copy previous polynom order to the next */ + lmu[i + 1] = lmu[i]; + } else { + ro = 0; + largest = -1; + /* find largest delta with dmu != 0 */ + for (j = 0; j < i; j++) { + if ((dmu[j]) && (delta[j] > largest)) { + largest = delta[j]; + ro = j; + } + } + + /* compute difference */ + diff = (mu[i] - mu[ro]); + + /* Compute degree of the new smu polynomial */ + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) + lmu[i + 1] = lmu[i]; + else + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + + /* Init smu[i+1] with 0 */ + for (k = 0; k < num; k++) + smu[(i + 1) * num + k] = 0; + + /* Compute smu[i+1] */ + for (k = 0; k <= lmu[ro] >> 1; k++) { + int16_t a, b, c; + + if (!(smu[ro * num + k] && dmu[i])) + continue; + a = readw(index_of + dmu[i]); + b = readw(index_of + dmu[ro]); + c = readw(index_of + smu[ro * num + k]); + tmp = a + (cw_len - b) + c; + a = readw(alpha_to + tmp % cw_len); + smu[(i + 1) * num + (k + diff)] = a; + } + + for (k = 0; k <= lmu[i] >> 1; k++) + smu[(i + 1) * num + k] ^= smu[i * num + k]; + } + + /* End Computing Sigma (Mu+1) and L(mu) */ + /* In either case compute delta */ + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + + /* Do not compute discrepancy for the last iteration */ + if (i >= cap) + continue; + + for (k = 0; k <= (lmu[i + 1] >> 1); k++) { + tmp = 2 * (i - 1); + if (k == 0) { + dmu[i + 1] = si[tmp + 3]; + } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { + int16_t a, b, c; + a = readw(index_of + + smu[(i + 1) * num + k]); + b = si[2 * (i - 1) + 3 - k]; + c = readw(index_of + b); + tmp = a + c; + tmp %= cw_len; + dmu[i + 1] = readw(alpha_to + tmp) ^ + dmu[i + 1]; + } + } + } +} + +static int pmecc_err_location(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + const int cap = host->pmecc_corr_cap; + const int num = 2 * cap + 1; + int sector_size = host->pmecc_sector_size; + int err_nbr = 0; /* number of error */ + int roots_nbr; /* number of roots */ + int i; + uint32_t val; + int16_t *smu = host->pmecc_smu; + int timeout = PMECC_MAX_TIMEOUT_US; + + pmecc_writel(host->pmerrloc, eldis, PMERRLOC_DISABLE); + + for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { + pmecc_writel(host->pmerrloc, sigma[i], + smu[(cap + 1) * num + i]); + err_nbr++; + } + + val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1); + if (sector_size == 1024) + val |= PMERRLOC_ELCFG_SECTOR_1024; + + pmecc_writel(host->pmerrloc, elcfg, val); + pmecc_writel(host->pmerrloc, elen, + sector_size * 8 + host->pmecc_degree * cap); + + while (--timeout) { + if (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_CALC_DONE) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to calculate PMECC error location\n"); + return -1; + } + + roots_nbr = (pmecc_readl(host->pmerrloc, elisr) & PMERRLOC_ERR_NUM_MASK) + >> 8; + /* Number of roots == degree of smu hence <= cap */ + if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) + return err_nbr - 1; + + /* Number of roots does not match the degree of smu + * unable to correct error */ + return -1; +} + +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, + int sector_num, int extra_bytes, int err_nbr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + int i = 0; + int byte_pos, bit_pos, sector_size, pos; + uint32_t tmp; + uint8_t err_byte; + + sector_size = host->pmecc_sector_size; + + while (err_nbr) { + tmp = pmecc_readl(host->pmerrloc, el[i]) - 1; + byte_pos = tmp / 8; + bit_pos = tmp % 8; + + if (byte_pos >= (sector_size + extra_bytes)) + BUG(); /* should never happen */ + + if (byte_pos < sector_size) { + err_byte = *(buf + byte_pos); + *(buf + byte_pos) ^= (1 << bit_pos); + + pos = sector_num * host->pmecc_sector_size + byte_pos; + dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, *(buf + byte_pos)); + } else { + /* Bit flip in OOB area */ + tmp = sector_num * host->pmecc_bytes_per_sector + + (byte_pos - sector_size); + err_byte = ecc[tmp]; + ecc[tmp] ^= (1 << bit_pos); + + pos = tmp + nand_chip->ecc.layout->eccpos[0]; + dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + pos, bit_pos, err_byte, ecc[tmp]); + } + + i++; + err_nbr--; + } + + return; +} + +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, + u8 *ecc) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + int i, err_nbr, eccbytes; + uint8_t *buf_pos; + + /* SAMA5D4 PMECC IP can correct errors for all 0xff page */ + if (host->pmecc_version >= PMECC_VERSION_SAMA5D4) + goto normal_check; + + eccbytes = nand_chip->ecc.bytes; + for (i = 0; i < eccbytes; i++) + if (ecc[i] != 0xff) + goto normal_check; + /* Erased page, return OK */ + return 0; + +normal_check: + for (i = 0; i < host->pmecc_sector_number; i++) { + err_nbr = 0; + if (pmecc_stat & 0x1) { + buf_pos = buf + i * host->pmecc_sector_size; + + pmecc_gen_syndrome(mtd, i); + pmecc_substitute(mtd); + pmecc_get_sigma(mtd); + + err_nbr = pmecc_err_location(mtd); + if (err_nbr == -1) { + dev_err(host->dev, "PMECC: Too many errors\n"); + mtd->ecc_stats.failed++; + return -EBADMSG; + } else { + pmecc_correct_data(mtd, buf_pos, ecc, i, + host->pmecc_bytes_per_sector, err_nbr); + mtd->ecc_stats.corrected += err_nbr; + } + } + pmecc_stat >>= 1; + } + + return 0; +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct atmel_nand_host *host = nand_get_controller_data(chip); + int eccsize = chip->ecc.size; + uint8_t *oob = chip->oob_poi; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t stat; + int timeout = PMECC_MAX_TIMEOUT_US; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg)) + & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + + chip->read_buf(mtd, buf, eccsize); + chip->read_buf(mtd, oob, mtd->oobsize); + + while (--timeout) { + if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to read PMECC page\n"); + return -1; + } + + stat = pmecc_readl(host->pmecc, isr); + if (stat != 0) + if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) + return -EBADMSG; + + return 0; +} + +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + struct atmel_nand_host *host = nand_get_controller_data(chip); + uint32_t *eccpos = chip->ecc.layout->eccpos; + int i, j; + int timeout = PMECC_MAX_TIMEOUT_US; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + + pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) | + PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + + chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + + while (--timeout) { + if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) + break; + WATCHDOG_RESET(); + udelay(1); + } + + if (!timeout) { + dev_err(host->dev, "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); + goto out; + } + + for (i = 0; i < host->pmecc_sector_number; i++) { + for (j = 0; j < host->pmecc_bytes_per_sector; j++) { + int pos; + + pos = i * host->pmecc_bytes_per_sector + j; + chip->oob_poi[eccpos[pos]] = + pmecc_readb(host->pmecc, ecc_port[i].ecc[j]); + } + } + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +out: + return 0; +} + +static void atmel_pmecc_core_init(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + uint32_t val = 0; + struct nand_ecclayout *ecc_layout; + + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + + switch (host->pmecc_corr_cap) { + case 2: + val = PMECC_CFG_BCH_ERR2; + break; + case 4: + val = PMECC_CFG_BCH_ERR4; + break; + case 8: + val = PMECC_CFG_BCH_ERR8; + break; + case 12: + val = PMECC_CFG_BCH_ERR12; + break; + case 24: + val = PMECC_CFG_BCH_ERR24; + break; + case 32: + val = PMECC_CFG_BCH_ERR32; + break; + } + + if (host->pmecc_sector_size == 512) + val |= PMECC_CFG_SECTOR512; + else if (host->pmecc_sector_size == 1024) + val |= PMECC_CFG_SECTOR1024; + + switch (host->pmecc_sector_number) { + case 1: + val |= PMECC_CFG_PAGE_1SECTOR; + break; + case 2: + val |= PMECC_CFG_PAGE_2SECTORS; + break; + case 4: + val |= PMECC_CFG_PAGE_4SECTORS; + break; + case 8: + val |= PMECC_CFG_PAGE_8SECTORS; + break; + } + + val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE + | PMECC_CFG_AUTO_DISABLE); + pmecc_writel(host->pmecc, cfg, val); + + ecc_layout = nand_chip->ecc.layout; + pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1); + pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]); + pmecc_writel(host->pmecc, eaddr, + ecc_layout->eccpos[ecc_layout->eccbytes - 1]); + /* See datasheet about PMECC Clock Control Register */ + pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ); + pmecc_writel(host->pmecc, idr, 0xff); + pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +} + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* + * pmecc_choose_ecc - Get ecc requirement from ONFI parameters. If + * pmecc_corr_cap or pmecc_sector_size is 0, then set it as + * ONFI ECC parameters. + * @host: point to an atmel_nand_host structure. + * if host->pmecc_corr_cap is 0 then set it as the ONFI ecc_bits. + * if host->pmecc_sector_size is 0 then set it as the ONFI sector_size. + * @chip: point to an nand_chip structure. + * @cap: store the ONFI ECC correct bits capbility + * @sector_size: in how many bytes that ONFI require to correct @ecc_bits + * + * Return 0 if success. otherwise return the error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, + struct nand_chip *chip, + int *cap, int *sector_size) +{ + /* Get ECC requirement from ONFI parameters */ + *cap = *sector_size = 0; + if (chip->onfi_version) { + *cap = chip->ecc_strength_ds; + *sector_size = chip->ecc_step_ds; + pr_debug("ONFI params, minimum required ECC: %d bits in %d bytes\n", + *cap, *sector_size); + } + + if (*cap == 0 && *sector_size == 0) { + /* Non-ONFI compliant */ + dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes\n"); + *cap = 2; + *sector_size = 512; + } + + /* If head file doesn't specify then use the one in ONFI parameters */ + if (host->pmecc_corr_cap == 0) { + /* use the most fitable ecc bits (the near bigger one ) */ + if (*cap <= 2) + host->pmecc_corr_cap = 2; + else if (*cap <= 4) + host->pmecc_corr_cap = 4; + else if (*cap <= 8) + host->pmecc_corr_cap = 8; + else if (*cap <= 12) + host->pmecc_corr_cap = 12; + else if (*cap <= 24) + host->pmecc_corr_cap = 24; + else +#ifdef CONFIG_SAMA5D2 + host->pmecc_corr_cap = 32; +#else + host->pmecc_corr_cap = 24; +#endif + } + if (host->pmecc_sector_size == 0) { + /* use the most fitable sector size (the near smaller one ) */ + if (*sector_size >= 1024) + host->pmecc_sector_size = 1024; + else if (*sector_size >= 512) + host->pmecc_sector_size = 512; + else + return -EINVAL; + } + return 0; +} +#endif + +#if defined(NO_GALOIS_TABLE_IN_ROM) +static uint16_t *pmecc_galois_table; +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly) - 1; +} + +static int build_gf_tables(int mm, unsigned int poly, + int16_t *index_of, int16_t *alpha_to) +{ + unsigned int i, x = 1; + const unsigned int k = 1 << deg(poly); + unsigned int nn = (1 << mm) - 1; + + /* primitive polynomial must be of degree m */ + if (k != (1u << mm)) + return -EINVAL; + + for (i = 0; i < nn; i++) { + alpha_to[i] = x; + index_of[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0ecc.mode = NAND_ECC_HW; + nand->ecc.calculate = NULL; + nand->ecc.correct = NULL; + nand->ecc.hwctl = NULL; + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + host->pmecc_corr_cap = host->pmecc_sector_size = 0; + +#ifdef CONFIG_PMECC_CAP + host->pmecc_corr_cap = CONFIG_PMECC_CAP; +#endif +#ifdef CONFIG_PMECC_SECTOR_SIZE + host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif + /* Get ECC requirement of ONFI parameters. And if CONFIG_PMECC_CAP or + * CONFIG_PMECC_SECTOR_SIZE not defined, then use ecc_bits, sector_size + * from ONFI. + */ + if (pmecc_choose_ecc(host, nand, &cap, §or_size)) { + dev_err(host->dev, "Required ECC %d bits in %d bytes not supported!\n", + cap, sector_size); + return -EINVAL; + } + + if (cap > host->pmecc_corr_cap) + dev_info(host->dev, "WARNING: Using different ecc correct bits(%d bit) from Nand ONFI ECC reqirement (%d bit).\n", + host->pmecc_corr_cap, cap); + if (sector_size < host->pmecc_sector_size) + dev_info(host->dev, "WARNING: Using different ecc correct sector size (%d bytes) from Nand ONFI ECC reqirement (%d bytes).\n", + host->pmecc_sector_size, sector_size); +#else /* CONFIG_SYS_NAND_ONFI_DETECTION */ + host->pmecc_corr_cap = CONFIG_PMECC_CAP; + host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +#endif + + cap = host->pmecc_corr_cap; + sector_size = host->pmecc_sector_size; + + /* TODO: need check whether cap & sector_size is validate */ +#if defined(NO_GALOIS_TABLE_IN_ROM) + /* + * As pmecc_rom_base is the begin of the gallois field table, So the + * index offset just set as 0. + */ + host->pmecc_index_table_offset = 0; +#else + if (host->pmecc_sector_size == 512) + host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_512; + else + host->pmecc_index_table_offset = ATMEL_PMECC_INDEX_OFFSET_1024; +#endif + + pr_debug("Initialize PMECC params, cap: %d, sector: %d\n", + cap, sector_size); + + host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; + host->pmerrloc = (struct pmecc_errloc_regs __iomem *) + ATMEL_BASE_PMERRLOC; +#if defined(NO_GALOIS_TABLE_IN_ROM) + pmecc_galois_table = create_lookup_table(host->pmecc_sector_size); + if (!pmecc_galois_table) { + dev_err(host->dev, "out of memory\n"); + return -ENOMEM; + } + + host->pmecc_rom_base = (void __iomem *)pmecc_galois_table; +#else + host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; +#endif + + /* ECC is calculated for the whole page (1 step) */ + nand->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 2048: + case 4096: + case 8192: + host->pmecc_degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; + host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; + host->pmecc_sector_number = mtd->writesize / sector_size; + host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( + cap, sector_size); + host->pmecc_alpha_to = pmecc_get_alpha_to(host); + host->pmecc_index_of = host->pmecc_rom_base + + host->pmecc_index_table_offset; + + nand->ecc.steps = 1; + nand->ecc.bytes = host->pmecc_bytes_per_sector * + host->pmecc_sector_number; + + if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) { + dev_err(host->dev, "too large eccpos entries. max support ecc.bytes is %d\n", + MTD_MAX_ECCPOS_ENTRIES_LARGE); + return -EINVAL; + } + + if (nand->ecc.bytes > mtd->oobsize - PMECC_OOB_RESERVED_BYTES) { + dev_err(host->dev, "No room for ECC bytes\n"); + return -EINVAL; + } + pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, + mtd->oobsize, + nand->ecc.bytes); + nand->ecc.layout = &atmel_pmecc_oobinfo; + break; + case 512: + case 1024: + /* TODO */ + dev_err(host->dev, "Unsupported page size for PMECC, use Software ECC\n"); + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.read_page = NULL; + nand->ecc.postpad = 0; + nand->ecc.prepad = 0; + nand->ecc.bytes = 0; + return 0; + } + + /* Allocate data for PMECC computation */ + if (pmecc_data_alloc(host)) { + dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); + return -ENOMEM; + } + + nand->options |= NAND_NO_SUBPAGE_WRITE; + nand->ecc.read_page = atmel_nand_pmecc_read_page; + nand->ecc.write_page = atmel_nand_pmecc_write_page; + nand->ecc.strength = cap; + + /* Check the PMECC ip version */ + host->pmecc_version = pmecc_readl(host->pmerrloc, version); + dev_dbg(host->dev, "PMECC IP version is: %x\n", host->pmecc_version); + + atmel_pmecc_core_init(mtd); + + return 0; +} + +#else + +/* oob layout for large page size + * bad block info is on bytes 0 and 1 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_large = { + .eccbytes = 4, + .eccpos = {60, 61, 62, 63}, + .oobfree = { + {2, 58} + }, +}; + +/* oob layout for small page size + * bad block info is on bytes 4 and 5 + * the bytes have to be consecutives to avoid + * several NAND_CMD_RNDOUT during read + */ +static struct nand_ecclayout atmel_oobinfo_small = { + .eccbytes = 4, + .eccpos = {0, 1, 2, 3}, + .oobfree = { + {6, 10} + }, +}; + +/* + * Calculate HW ECC + * + * function called after a write + * + * mtd: MTD block structure + * dat: raw data (unused) + * ecc_code: buffer for ECC + */ +static int atmel_nand_calculate(struct mtd_info *mtd, + const u_char *dat, unsigned char *ecc_code) +{ + unsigned int ecc_value; + + /* get the first 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR); + + ecc_code[0] = ecc_value & 0xFF; + ecc_code[1] = (ecc_value >> 8) & 0xFF; + + /* get the last 2 ECC bytes */ + ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY; + + ecc_code[2] = ecc_value & 0xFF; + ecc_code[3] = (ecc_value >> 8) & 0xFF; + + return 0; +} + +/* + * HW ECC read page function + * + * mtd: mtd info structure + * chip: nand chip info structure + * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi + */ +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + uint8_t *ecc_pos; + int stat; + + /* read the page */ + chip->read_buf(mtd, p, eccsize); + + /* move to ECC position if needed */ + if (eccpos[0] != 0) { + /* This only works on large pages + * because the ECC controller waits for + * NAND_CMD_RNDOUTSTART after the + * NAND_CMD_RNDOUT. + * anyway, for small pages, the eccpos[0] == 0 + */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + eccpos[0], -1); + } + + /* the ECC controller needs to read the ECC just after the data */ + ecc_pos = oob + eccpos[0]; + chip->read_buf(mtd, ecc_pos, eccbytes); + + /* check if there's an error */ + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + /* get back to oob start (end of page) */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + + /* read the oob */ + chip->read_buf(mtd, oob, mtd->oobsize); + + return 0; +} + +/* + * HW ECC Correction + * + * function called after a read + * + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip (unused) + * isnull: unused + * + * Detect and correct a 1 bit error for a page + */ +static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *isnull) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + unsigned int ecc_status; + unsigned int ecc_word, ecc_bit; + + /* get the status from the Status Register */ + ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR); + + /* if there's no error */ + if (likely(!(ecc_status & ATMEL_ECC_RECERR))) + return 0; + + /* get error bit offset (4 bits) */ + ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR; + /* get word address (12 bits) */ + ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR; + ecc_word >>= 4; + + /* if there are multiple errors */ + if (ecc_status & ATMEL_ECC_MULERR) { + /* check if it is a freshly erased block + * (filled with 0xff) */ + if ((ecc_bit == ATMEL_ECC_BITADDR) + && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { + /* the block has just been erased, return OK */ + return 0; + } + /* it doesn't seems to be a freshly + * erased block. + * We can't correct so many errors */ + dev_warn(host->dev, "atmel_nand : multiple errors detected." + " Unable to correct.\n"); + return -EBADMSG; + } + + /* if there's a single bit error : we can correct it */ + if (ecc_status & ATMEL_ECC_ECCERR) { + /* there's nothing much to do here. + * the bit error is on the ECC itself. + */ + dev_warn(host->dev, "atmel_nand : one bit error on ECC code." + " Nothing to correct\n"); + return 0; + } + + dev_warn(host->dev, "atmel_nand : one bit error on data." + " (word offset in the page :" + " 0x%x bit offset : 0x%x)\n", + ecc_word, ecc_bit); + /* correct the error */ + if (nand_chip->options & NAND_BUSWIDTH_16) { + /* 16 bits words */ + ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit); + } else { + /* 8 bits words */ + dat[ecc_word] ^= (1 << ecc_bit); + } + dev_warn(host->dev, "atmel_nand : error corrected\n"); + return 1; +} + +/* + * Enable HW ECC : unused on most chips + */ +static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{ +} + +int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd) +{ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.calculate = atmel_nand_calculate; + nand->ecc.correct = atmel_nand_correct; + nand->ecc.hwctl = atmel_nand_hwctl; + nand->ecc.read_page = atmel_nand_read_page; + nand->ecc.bytes = 4; + nand->ecc.strength = 4; + + if (nand->ecc.mode == NAND_ECC_HW) { + /* ECC is calculated for the whole page (1 step) */ + nand->ecc.size = mtd->writesize; + + /* set ECC page size and oob layout */ + switch (mtd->writesize) { + case 512: + nand->ecc.layout = &atmel_oobinfo_small; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_528); + break; + case 1024: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_1056); + break; + case 2048: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_2112); + break; + case 4096: + nand->ecc.layout = &atmel_oobinfo_large; + ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, + ATMEL_ECC_PAGESIZE_4224); + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.calculate = NULL; + nand->ecc.correct = NULL; + nand->ecc.hwctl = NULL; + nand->ecc.read_page = NULL; + nand->ecc.postpad = 0; + nand->ecc.prepad = 0; + nand->ecc.bytes = 0; + break; + } + } + + return 0; +} + +#endif /* CONFIG_ATMEL_NAND_HW_PMECC */ + +#endif /* CONFIG_ATMEL_NAND_HWECC */ + +static void at91_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(CONFIG_SYS_NAND_MASK_ALE + | CONFIG_SYS_NAND_MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_MASK_ALE; + +#ifdef CONFIG_SYS_NAND_ENABLE_PIN + at91_set_gpio_value(CONFIG_SYS_NAND_ENABLE_PIN, + !(ctrl & NAND_NCE)); +#endif + this->IO_ADDR_W = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_READY_PIN +static int at91_nand_ready(struct mtd_info *mtd) +{ + return at91_get_gpio_value(CONFIG_SYS_NAND_READY_PIN); +} +#endif + +#ifdef CONFIG_SPL_BUILD +/* The following code is for SPL */ +static struct mtd_info *mtd; +static struct nand_chip nand_chip; + +static int nand_command(int block, int page, uint32_t offs, u8 cmd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(mtd)) + ; + + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) + offs >>= 1; + + hwctrl(mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); + hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); + hwctrl(mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE); +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + hwctrl(mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE); +#endif + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + hwctrl(mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + while (!this->dev_ready(mtd)) + ; + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB); + + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +#ifdef CONFIG_SPL_NAND_ECC +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + int i; + uint8_t *p = dst; + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (this->ecc.mode != NAND_ECC_SOFT) + this->ecc.hwctl(mtd, NAND_ECC_READ); + this->read_buf(mtd, p, eccsize); + this->ecc.calculate(mtd, p, &ecc_calc[i]); + } + this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + + return 0; +} + +int spl_nand_erase_one(int block, int page) +{ + struct nand_chip *this = mtd_to_nand(mtd); + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + int page_addr; + + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, 0); + + page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + hwctrl(mtd, NAND_CMD_ERASE1, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Row address */ + hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE | NAND_CTRL_CHANGE); + hwctrl(mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE | NAND_CTRL_CHANGE); +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); +#endif + hwctrl(mtd, NAND_CMD_ERASE2, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + while (!this->dev_ready(mtd)) + ; + + nand_deselect(); + + return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + nand_command(block, page, 0, NAND_CMD_READ0); + atmel_nand_pmecc_read_page(mtd, this, dst, 0, page); + + return 0; +} +#endif /* CONFIG_SPL_NAND_ECC */ + +int at91_nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + udelay(this->chip_delay); + + return 1; +} + +int board_nand_init(struct nand_chip *nand) +{ + int ret = 0; + + nand->ecc.mode = NAND_ECC_SOFT; +#ifdef CONFIG_SYS_NAND_DBW_16 + nand->options = NAND_BUSWIDTH_16; + nand->read_buf = nand_read_buf16; +#else + nand->read_buf = nand_read_buf; +#endif + nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN + nand->dev_ready = at91_nand_ready; +#else + nand->dev_ready = at91_nand_wait_ready; +#endif + nand->chip_delay = 20; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH; +#endif + +#ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + ret = atmel_pmecc_nand_init_params(nand, mtd); +#endif +#endif + + return ret; +} + +void nand_init(void) +{ + mtd = nand_to_mtd(&nand_chip); + mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE; + mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE; + nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; + nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_ECC + if (nand_chip.ecc.mode == NAND_ECC_SOFT) { + nand_chip.ecc.calculate = nand_calculate_ecc; + nand_chip.ecc.correct = nand_correct_data; + } +#endif + + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, 0); +} + +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, -1); +} + +#include "nand_spl_loaders.c" + +#else + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +static ulong base_addr[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; + +int atmel_nand_chip_init(int devnum, ulong base_addr) +{ + int ret; + struct nand_chip *nand = &nand_chip[devnum]; + struct mtd_info *mtd = nand_to_mtd(nand); + + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + +#ifdef CONFIG_NAND_ECC_BCH + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif +#ifdef CONFIG_SYS_NAND_DBW_16 + nand->options = NAND_BUSWIDTH_16; +#endif + nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN + nand->dev_ready = at91_nand_ready; +#endif + nand->chip_delay = 75; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH; +#endif + + ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); + if (ret) + return ret; + +#ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + ret = atmel_pmecc_nand_init_params(nand, mtd); +#else + ret = atmel_hwecc_nand_init_param(nand, mtd); +#endif + if (ret) + return ret; +#endif + + ret = nand_scan_tail(mtd); + if (!ret) + nand_register(devnum, mtd); + + return ret; +} + +void board_nand_init(void) +{ + int i; + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + if (atmel_nand_chip_init(i, base_addr[i])) + dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip", + i); +} +#endif /* CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/raw/atmel_nand_ecc.h b/drivers/mtd/nand/raw/atmel_nand_ecc.h new file mode 100644 index 0000000000..05eeedb3f8 --- /dev/null +++ b/drivers/mtd/nand/raw/atmel_nand_ecc.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Error Corrected Code Controller (ECC) - System peripherals regsters. + * Based on AT91SAM9260 datasheet revision B. + */ + +#ifndef ATMEL_NAND_ECC_H +#define ATMEL_NAND_ECC_H + +#define ATMEL_ECC_CR 0x00 /* Control register */ +#define ATMEL_ECC_RST (1 << 0) /* Reset parity */ + +#define ATMEL_ECC_MR 0x04 /* Mode register */ +#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */ +#define ATMEL_ECC_PAGESIZE_528 (0) +#define ATMEL_ECC_PAGESIZE_1056 (1) +#define ATMEL_ECC_PAGESIZE_2112 (2) +#define ATMEL_ECC_PAGESIZE_4224 (3) + +#define ATMEL_ECC_SR 0x08 /* Status register */ +#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */ +#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */ +#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */ + +#define ATMEL_ECC_PR 0x0c /* Parity register */ +#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */ +#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */ + +#define ATMEL_ECC_NPR 0x10 /* NParity register */ +#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ + +/* Register access macros for PMECC */ +#define pmecc_readl(addr, reg) \ + readl(&addr->reg) + +#define pmecc_readb(addr, reg) \ + readb(&addr->reg) + +#define pmecc_writel(addr, reg, value) \ + writel((value), &addr->reg) + +/* PMECC Register Definitions */ +#define PMECC_MAX_SECTOR_NUM 8 +struct pmecc_regs { + u32 cfg; /* 0x00 PMECC Configuration Register */ + u32 sarea; /* 0x04 PMECC Spare Area Size Register */ + u32 saddr; /* 0x08 PMECC Start Address Register */ + u32 eaddr; /* 0x0C PMECC End Address Register */ + u32 clk; /* 0x10 PMECC Clock Control Register */ + u32 ctrl; /* 0x14 PMECC Control Register */ + u32 sr; /* 0x18 PMECC Status Register */ + u32 ier; /* 0x1C PMECC Interrupt Enable Register */ + u32 idr; /* 0x20 PMECC Interrupt Disable Register */ + u32 imr; /* 0x24 PMECC Interrupt Mask Register */ + u32 isr; /* 0x28 PMECC Interrupt Status Register */ + u32 reserved0[5]; /* 0x2C-0x3C Reserved */ + + /* 0x40 + sector_num * (0x40), Redundancy Registers */ + struct { +#ifdef CONFIG_SAMA5D2 + u8 ecc[56]; /* PMECC Generated Redundancy Byte Per Sector */ + u32 reserved1[2]; +#else + u8 ecc[44]; /* PMECC Generated Redundancy Byte Per Sector */ + u32 reserved1[5]; +#endif + } ecc_port[PMECC_MAX_SECTOR_NUM]; + + /* 0x240 + sector_num * (0x40) Remainder Registers */ + struct { +#ifdef CONFIG_SAMA5D2 + u32 rem[16]; +#else + u32 rem[12]; + u32 reserved2[4]; +#endif + } rem_port[PMECC_MAX_SECTOR_NUM]; + u32 reserved3[16]; /* 0x440-0x47C Reserved */ +}; + +/* For PMECC Configuration Register */ +#define PMECC_CFG_BCH_ERR2 (0 << 0) +#define PMECC_CFG_BCH_ERR4 (1 << 0) +#define PMECC_CFG_BCH_ERR8 (2 << 0) +#define PMECC_CFG_BCH_ERR12 (3 << 0) +#define PMECC_CFG_BCH_ERR24 (4 << 0) +#define PMECC_CFG_BCH_ERR32 (5 << 0) + +#define PMECC_CFG_SECTOR512 (0 << 4) +#define PMECC_CFG_SECTOR1024 (1 << 4) + +#define PMECC_CFG_PAGE_1SECTOR (0 << 8) +#define PMECC_CFG_PAGE_2SECTORS (1 << 8) +#define PMECC_CFG_PAGE_4SECTORS (2 << 8) +#define PMECC_CFG_PAGE_8SECTORS (3 << 8) + +#define PMECC_CFG_READ_OP (0 << 12) +#define PMECC_CFG_WRITE_OP (1 << 12) + +#define PMECC_CFG_SPARE_ENABLE (1 << 16) +#define PMECC_CFG_SPARE_DISABLE (0 << 16) + +#define PMECC_CFG_AUTO_ENABLE (1 << 20) +#define PMECC_CFG_AUTO_DISABLE (0 << 20) + +/* For PMECC Clock Control Register */ +#define PMECC_CLK_133MHZ (2 << 0) + +/* For PMECC Control Register */ +#define PMECC_CTRL_RST (1 << 0) +#define PMECC_CTRL_DATA (1 << 1) +#define PMECC_CTRL_USER (1 << 2) +#define PMECC_CTRL_ENABLE (1 << 4) +#define PMECC_CTRL_DISABLE (1 << 5) + +/* For PMECC Status Register */ +#define PMECC_SR_BUSY (1 << 0) +#define PMECC_SR_ENABLE (1 << 4) + +/* PMERRLOC Register Definitions */ +struct pmecc_errloc_regs { + u32 elcfg; /* 0x00 Error Location Configuration Register */ + u32 elprim; /* 0x04 Error Location Primitive Register */ + u32 elen; /* 0x08 Error Location Enable Register */ + u32 eldis; /* 0x0C Error Location Disable Register */ + u32 elsr; /* 0x10 Error Location Status Register */ + u32 elier; /* 0x14 Error Location Interrupt Enable Register */ + u32 elidr; /* 0x08 Error Location Interrupt Disable Register */ + u32 elimr; /* 0x0C Error Location Interrupt Mask Register */ + u32 elisr; /* 0x20 Error Location Interrupt Status Register */ + u32 reserved0; /* 0x24 Reserved */ +#ifdef CONFIG_SAMA5D2 + u32 sigma[33]; /* 0x28-0xA8 Error Location Sigma Registers */ + u32 el[32]; /* 0xAC-0x128 Error Location Registers */ + + /* + * 0x12C-0x1FC: + * Reserved for SAMA5D2. + */ + u32 reserved1[53]; +#else + u32 sigma[25]; /* 0x28-0x88 Error Location Sigma Registers */ + u32 el[24]; /* 0x8C-0xE8 Error Location Registers */ + u32 reserved1[5]; /* 0xEC-0xFC Reserved */ +#endif + + /* + * SAMA5 chip HSMC registers start here. But for 9X5 chip it is just + * reserved. + * + * Offset 0x00-0xF8: + */ + u32 reserved2[63]; + + /* + * Offset 0xFC: + * PMECC version for AT91SAM9X5, AT91SAM9N12. + * HSMC version for SAMA5D3, SAMA5D4. Can refer as PMECC version. + */ + u32 version; +}; + +/* For Error Location Configuration Register */ +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) + +/* For Error Location Disable Register */ +#define PMERRLOC_DISABLE (1 << 0) + +/* For Error Location Interrupt Status Register */ +#ifdef CONFIG_SAMA5D2 +#define PMERRLOC_ERR_NUM_MASK (0x3f << 8) +#else +#define PMERRLOC_ERR_NUM_MASK (0x1f << 8) +#endif + +#define PMERRLOC_CALC_DONE (1 << 0) + +/* PMECC IP version */ +#define PMECC_VERSION_SAMA5D2 0x210 +#define PMECC_VERSION_SAMA5D4 0x113 +#define PMECC_VERSION_SAMA5D3 0x112 +#define PMECC_VERSION_AT91SAM9N12 0x102 +#define PMECC_VERSION_AT91SAM9X5 0x101 + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13 13 +#define PMECC_GF_DIMENSION_14 14 + +/* Primitive Polynomial used by PMECC */ +#define PMECC_GF_13_PRIMITIVE_POLY 0x201b +#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 + +#define PMECC_INDEX_TABLE_SIZE_512 0x2000 +#define PMECC_INDEX_TABLE_SIZE_1024 0x4000 + +#define PMECC_MAX_TIMEOUT_US (100 * 1000) + +/* Reserved bytes in oob area */ +#define PMECC_OOB_RESERVED_BYTES 2 + +#endif diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c new file mode 100644 index 0000000000..e6a84a52b4 --- /dev/null +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -0,0 +1,833 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NAND driver for TI DaVinci based boards. + * + * Copyright (C) 2007 Sergey Kubushyn + * + * Based on Linux DaVinci NAND driver by TI. Original copyright follows: + */ + +/* + * + * linux/drivers/mtd/nand/raw/nand_davinci.c + * + * NAND Flash Driver + * + * Copyright (C) 2006 Texas Instruments. + * + * ---------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------- + * + * Overview: + * This is a device driver for the NAND flash device found on the + * DaVinci board which utilizes the Samsung k9k2g08 part. + * + Modifications: + ver. 1.0: Feb 2005, Vinod/Sudhakar + - + */ + +#include +#include +#include +#include + +/* Definitions for 4-bit hardware ECC */ +#define NAND_TIMEOUT 10240 +#define NAND_ECC_BUSY 0xC +#define NAND_4BITECC_MASK 0x03FF03FF +#define EMIF_NANDFSR_ECC_STATE_MASK 0x00000F00 +#define ECC_STATE_NO_ERR 0x0 +#define ECC_STATE_TOO_MANY_ERRS 0x1 +#define ECC_STATE_ERR_CORR_COMP_P 0x2 +#define ECC_STATE_ERR_CORR_COMP_N 0x3 + +/* + * Exploit the little endianness of the ARM to do multi-byte transfers + * per device read. This can perform over twice as quickly as individual + * byte transfers when buffer alignment is conducive. + * + * NOTE: This only works if the NAND is not connected to the 2 LSBs of + * the address bus. On Davinci EVM platforms this has always been true. + */ +static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const u32 *nand = chip->IO_ADDR_R; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + *buf = readb(nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + *(u32 *)buf = __raw_readl(nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + *(u16 *)buf = readw(nand); + buf += 2; + len -= 2; + } + + if (len) + *buf = readb(nand); + } +} + +static void nand_davinci_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + const u32 *nand = chip->IO_ADDR_W; + + /* Make sure that buf is 32 bit aligned */ + if (((int)buf & 0x3) != 0) { + if (((int)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((int)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + __raw_writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + +static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, IO_ADDR_W); +} + +#ifdef CONFIG_SYS_NAND_HW_ECC + +static u_int32_t nand_davinci_readecc(struct mtd_info *mtd) +{ + u_int32_t ecc = 0; + + ecc = __raw_readl(&(davinci_emif_regs->nandfecc[ + CONFIG_SYS_NAND_CS - 2])); + + return ecc; +} + +static void nand_davinci_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u_int32_t val; + + /* reading the ECC result register resets the ECC calculation */ + nand_davinci_readecc(mtd); + + val = __raw_readl(&davinci_emif_regs->nandfcr); + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_1BIT_ECC_START(CONFIG_SYS_NAND_CS); + __raw_writel(val, &davinci_emif_regs->nandfcr); +} + +static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + u_int32_t tmp; + + tmp = nand_davinci_readecc(mtd); + + /* Squeeze 4 bytes ECC into 3 bytes by removing RESERVED bits + * and shifting. RESERVED bits are 31 to 28 and 15 to 12. */ + tmp = (tmp & 0x00000fff) | ((tmp & 0x0fff0000) >> 4); + + /* Invert so that erased block ECC is correct */ + tmp = ~tmp; + + *ecc_code++ = tmp; + *ecc_code++ = tmp >> 8; + *ecc_code++ = tmp >> 16; + + /* NOTE: the above code matches mainline Linux: + * .PQR.stu ==> ~PQRstu + * + * MontaVista/TI kernels encode those bytes differently, use + * complicated (and allegedly sometimes-wrong) correction code, + * and usually shipped with U-Boot that uses software ECC: + * .PQR.stu ==> PsQRtu + * + * If you need MV/TI compatible NAND I/O in U-Boot, it should + * be possible to (a) change the mangling above, (b) reverse + * that mangling in nand_davinci_correct_data() below. + */ + + return 0; +} + +static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_int32_t ecc_nand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + u_int32_t ecc_calc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + u_int32_t diff = ecc_calc ^ ecc_nand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < this->ecc.size) { + uint8_t find_bit = 1 << ((diff >> 12) & 7); + uint32_t find_byte = diff >> (12 + 3); + + dat[find_byte] ^= find_bit; + pr_debug("Correcting single " + "bit ECC error at offset: %d, bit: " + "%d\n", find_byte, find_bit); + return 1; + } else { + return -EBADMSG; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + nothing to fix */ + pr_debug("Single bit ECC error in " "ECC.\n"); + return 1; + } else { + /* Uncorrectable error */ + pr_debug("ECC UNCORRECTED_ERROR 1\n"); + return -EBADMSG; + } + } + return 0; +} +#endif /* CONFIG_SYS_NAND_HW_ECC */ + +#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST +static struct nand_ecclayout nand_davinci_4bit_layout_oobfirst = { +#if defined(CONFIG_SYS_NAND_PAGE_2K) + .eccbytes = 40, +#ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + {2, 4}, {16, 6}, {32, 6}, {48, 6}, + }, +#else + .eccpos = { + 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, + }, + .oobfree = { + {.offset = 2, .length = 22, }, + }, +#endif /* #ifdef CONFIG_NAND_6BYTES_OOB_FREE_10BYTES_ECC */ +#elif defined(CONFIG_SYS_NAND_PAGE_4K) + .eccbytes = 80, + .eccpos = { + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 46, }, + }, +#endif +}; + +#if defined CONFIG_KEYSTONE_RBL_NAND +static struct nand_ecclayout nand_keystone_rbl_4bit_layout_oobfirst = { +#if defined(CONFIG_SYS_NAND_PAGE_2K) + .eccbytes = 40, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + {.offset = 2, .length = 4, }, + {.offset = 16, .length = 6, }, + {.offset = 32, .length = 6, }, + {.offset = 48, .length = 6, }, + }, +#elif defined(CONFIG_SYS_NAND_PAGE_4K) + .eccbytes = 80, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 4, }, + {.offset = 16, .length = 6, }, + {.offset = 32, .length = 6, }, + {.offset = 48, .length = 6, }, + {.offset = 64, .length = 6, }, + {.offset = 80, .length = 6, }, + {.offset = 96, .length = 6, }, + {.offset = 112, .length = 6, }, + }, +#endif +}; + +#ifdef CONFIG_SYS_NAND_PAGE_2K +#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 11 +#elif defined(CONFIG_SYS_NAND_PAGE_4K) +#define CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE CONFIG_KEYSTONE_NAND_MAX_RBL_SIZE >> 12 +#endif + +/** + * nand_davinci_write_page - write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @raw: use _raw version of write_page + */ +static int nand_davinci_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, + const uint8_t *buf, int oob_required, + int page, int raw) +{ + int status; + int ret = 0; + struct nand_ecclayout *saved_ecc_layout; + + /* save current ECC layout and assign Keystone RBL ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + saved_ecc_layout = chip->ecc.layout; + chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; + mtd->oobavail = chip->ecc.layout->oobavail; + } + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) { + status = chip->ecc.write_page_raw(mtd, chip, buf, + oob_required, page); + } else { + status = chip->ecc.write_page(mtd, chip, buf, + oob_required, page); + } + + if (status < 0) { + ret = status; + goto err; + } + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) { + ret = -EIO; + goto err; + } + +err: + /* restore ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = saved_ecc_layout; + mtd->oobavail = saved_ecc_layout->oobavail; + } + + return ret; +} + +/** + * nand_davinci_read_page_hwecc - hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers which need a special oob layout. + */ +static int nand_davinci_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint32_t *eccpos; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->buffers->ecccalc; + struct nand_ecclayout *saved_ecc_layout = chip->ecc.layout; + + /* save current ECC layout and assign Keystone RBL ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = &nand_keystone_rbl_4bit_layout_oobfirst; + mtd->oobavail = chip->ecc.layout->oobavail; + } + + eccpos = chip->ecc.layout->eccpos; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + /* restore ECC layout */ + if (page < CONFIG_KEYSTONE_NAND_MAX_RBL_PAGE) { + chip->ecc.layout = saved_ecc_layout; + mtd->oobavail = saved_ecc_layout->oobavail; + } + + return 0; +} +#endif /* CONFIG_KEYSTONE_RBL_NAND */ + +static void nand_davinci_4bit_enable_hwecc(struct mtd_info *mtd, int mode) +{ + u32 val; + + switch (mode) { + case NAND_ECC_WRITE: + case NAND_ECC_READ: + /* + * Start a new ECC calculation for reading or writing 512 bytes + * of data. + */ + val = __raw_readl(&davinci_emif_regs->nandfcr); + val &= ~DAVINCI_NANDFCR_4BIT_ECC_SEL_MASK; + val |= DAVINCI_NANDFCR_NAND_ENABLE(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_SEL(CONFIG_SYS_NAND_CS); + val |= DAVINCI_NANDFCR_4BIT_ECC_START; + __raw_writel(val, &davinci_emif_regs->nandfcr); + break; + case NAND_ECC_READSYN: + val = __raw_readl(&davinci_emif_regs->nand4bitecc[0]); + break; + default: + break; + } +} + +static u32 nand_davinci_4bit_readecc(struct mtd_info *mtd, unsigned int ecc[4]) +{ + int i; + + for (i = 0; i < 4; i++) { + ecc[i] = __raw_readl(&davinci_emif_regs->nand4bitecc[i]) & + NAND_4BITECC_MASK; + } + + return 0; +} + +static int nand_davinci_4bit_calculate_ecc(struct mtd_info *mtd, + const uint8_t *dat, + uint8_t *ecc_code) +{ + unsigned int hw_4ecc[4]; + unsigned int i; + + nand_davinci_4bit_readecc(mtd, hw_4ecc); + + /*Convert 10 bit ecc value to 8 bit */ + for (i = 0; i < 2; i++) { + unsigned int hw_ecc_low = hw_4ecc[i * 2]; + unsigned int hw_ecc_hi = hw_4ecc[(i * 2) + 1]; + + /* Take first 8 bits from val1 (count1=0) or val5 (count1=1) */ + *ecc_code++ = hw_ecc_low & 0xFF; + + /* + * Take 2 bits as LSB bits from val1 (count1=0) or val5 + * (count1=1) and 6 bits from val2 (count1=0) or + * val5 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_low >> 8) & 0x3) | ((hw_ecc_low >> 14) & 0xFC); + + /* + * Take 4 bits from val2 (count1=0) or val5 (count1=1) and + * 4 bits from val3 (count1=0) or val6 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_low >> 22) & 0xF) | ((hw_ecc_hi << 4) & 0xF0); + + /* + * Take 6 bits from val3(count1=0) or val6 (count1=1) and + * 2 bits from val4 (count1=0) or val7 (count1=1) + */ + *ecc_code++ = + ((hw_ecc_hi >> 4) & 0x3F) | ((hw_ecc_hi >> 10) & 0xC0); + + /* Take 8 bits from val4 (count1=0) or val7 (count1=1) */ + *ecc_code++ = (hw_ecc_hi >> 18) & 0xFF; + } + + return 0; +} + +static int nand_davinci_4bit_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + int i; + unsigned int hw_4ecc[4]; + unsigned int iserror; + unsigned short *ecc16; + unsigned int numerrors, erroraddress, errorvalue; + u32 val; + + /* + * Check for an ECC where all bytes are 0xFF. If this is the case, we + * will assume we are looking at an erased page and we should ignore + * the ECC. + */ + for (i = 0; i < 10; i++) { + if (read_ecc[i] != 0xFF) + break; + } + if (i == 10) + return 0; + + /* Convert 8 bit in to 10 bit */ + ecc16 = (unsigned short *)&read_ecc[0]; + + /* + * Write the parity values in the NAND Flash 4-bit ECC Load register. + * Write each parity value one at a time starting from 4bit_ecc_val8 + * to 4bit_ecc_val1. + */ + + /*Take 2 bits from 8th byte and 8 bits from 9th byte */ + __raw_writel(((ecc16[4]) >> 6) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 4 bits from 7th byte and 6 bits from 8th byte */ + __raw_writel((((ecc16[3]) >> 12) & 0xF) | ((((ecc16[4])) << 4) & 0x3F0), + &davinci_emif_regs->nand4biteccload); + + /* Take 6 bits from 6th byte and 4 bits from 7th byte */ + __raw_writel((ecc16[3] >> 2) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 8 bits from 5th byte and 2 bits from 6th byte */ + __raw_writel(((ecc16[2]) >> 8) | ((((ecc16[3])) << 8) & 0x300), + &davinci_emif_regs->nand4biteccload); + + /*Take 2 bits from 3rd byte and 8 bits from 4th byte */ + __raw_writel((((ecc16[1]) >> 14) & 0x3) | ((((ecc16[2])) << 2) & 0x3FC), + &davinci_emif_regs->nand4biteccload); + + /* Take 4 bits form 2nd bytes and 6 bits from 3rd bytes */ + __raw_writel(((ecc16[1]) >> 4) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* Take 6 bits from 1st byte and 4 bits from 2nd byte */ + __raw_writel((((ecc16[0]) >> 10) & 0x3F) | (((ecc16[1]) << 6) & 0x3C0), + &davinci_emif_regs->nand4biteccload); + + /* Take 10 bits from 0th and 1st bytes */ + __raw_writel((ecc16[0]) & 0x3FF, + &davinci_emif_regs->nand4biteccload); + + /* + * Perform a dummy read to the EMIF Revision Code and Status register. + * This is required to ensure time for syndrome calculation after + * writing the ECC values in previous step. + */ + + val = __raw_readl(&davinci_emif_regs->nandfsr); + + /* + * Read the syndrome from the NAND Flash 4-Bit ECC 1-4 registers. + * A syndrome value of 0 means no bit errors. If the syndrome is + * non-zero then go further otherwise return. + */ + nand_davinci_4bit_readecc(mtd, hw_4ecc); + + if (!(hw_4ecc[0] | hw_4ecc[1] | hw_4ecc[2] | hw_4ecc[3])) + return 0; + + /* + * Clear any previous address calculation by doing a dummy read of an + * error address register. + */ + val = __raw_readl(&davinci_emif_regs->nanderradd1); + + /* + * Set the addr_calc_st bit(bit no 13) in the NAND Flash Control + * register to 1. + */ + __raw_writel(DAVINCI_NANDFCR_4BIT_CALC_START, + &davinci_emif_regs->nandfcr); + + /* + * Wait for the corr_state field (bits 8 to 11) in the + * NAND Flash Status register to be not equal to 0x0, 0x1, 0x2, or 0x3. + * Otherwise ECC calculation has not even begun and the next loop might + * fail because of a false positive! + */ + i = NAND_TIMEOUT; + do { + val = __raw_readl(&davinci_emif_regs->nandfsr); + val &= 0xc00; + i--; + } while ((i > 0) && !val); + + /* + * Wait for the corr_state field (bits 8 to 11) in the + * NAND Flash Status register to be equal to 0x0, 0x1, 0x2, or 0x3. + */ + i = NAND_TIMEOUT; + do { + val = __raw_readl(&davinci_emif_regs->nandfsr); + val &= 0xc00; + i--; + } while ((i > 0) && val); + + iserror = __raw_readl(&davinci_emif_regs->nandfsr); + iserror &= EMIF_NANDFSR_ECC_STATE_MASK; + iserror = iserror >> 8; + + /* + * ECC_STATE_TOO_MANY_ERRS (0x1) means errors cannot be + * corrected (five or more errors). The number of errors + * calculated (err_num field) differs from the number of errors + * searched. ECC_STATE_ERR_CORR_COMP_P (0x2) means error + * correction complete (errors on bit 8 or 9). + * ECC_STATE_ERR_CORR_COMP_N (0x3) means error correction + * complete (error exists). + */ + + if (iserror == ECC_STATE_NO_ERR) { + val = __raw_readl(&davinci_emif_regs->nanderrval1); + return 0; + } else if (iserror == ECC_STATE_TOO_MANY_ERRS) { + val = __raw_readl(&davinci_emif_regs->nanderrval1); + return -EBADMSG; + } + + numerrors = ((__raw_readl(&davinci_emif_regs->nandfsr) >> 16) + & 0x3) + 1; + + /* Read the error address, error value and correct */ + for (i = 0; i < numerrors; i++) { + if (i > 1) { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd2) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval2) >> + (16 * (i & 1))) & 0xFF); + } else { + erroraddress = + ((__raw_readl(&davinci_emif_regs->nanderradd1) >> + (16 * (i & 1))) & 0x3FF); + erroraddress = ((512 + 7) - erroraddress); + errorvalue = + ((__raw_readl(&davinci_emif_regs->nanderrval1) >> + (16 * (i & 1))) & 0xFF); + } + /* xor the corrupt data with error value */ + if (erroraddress < 512) + dat[erroraddress] ^= errorvalue; + } + + return numerrors; +} +#endif /* CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST */ + +static int nand_davinci_dev_ready(struct mtd_info *mtd) +{ + return __raw_readl(&davinci_emif_regs->nandfsr) & 0x1; +} + +static void nand_flash_init(void) +{ + /* This is for DM6446 EVM and *very* similar. DO NOT GROW THIS! + * Instead, have your board_init() set EMIF timings, based on its + * knowledge of the clocks and what devices are hooked up ... and + * don't even do that unless no UBL handled it. + */ +#ifdef CONFIG_SOC_DM644X + u_int32_t acfg1 = 0x3ffffffc; + + /*------------------------------------------------------------------* + * NAND FLASH CHIP TIMEOUT @ 459 MHz * + * * + * AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz * + * AEMIF.CLK period = 1/76.5 MHz = 13.1 ns * + * * + *------------------------------------------------------------------*/ + acfg1 = 0 + | (0 << 31) /* selectStrobe */ + | (0 << 30) /* extWait */ + | (1 << 26) /* writeSetup 10 ns */ + | (3 << 20) /* writeStrobe 40 ns */ + | (1 << 17) /* writeHold 10 ns */ + | (1 << 13) /* readSetup 10 ns */ + | (5 << 7) /* readStrobe 60 ns */ + | (1 << 4) /* readHold 10 ns */ + | (3 << 2) /* turnAround ?? ns */ + | (0 << 0) /* asyncSize 8-bit bus */ + ; + + __raw_writel(acfg1, &davinci_emif_regs->ab1cr); /* CS2 */ + + /* NAND flash on CS2 */ + __raw_writel(0x00000101, &davinci_emif_regs->nandfcr); +#endif +} + +void davinci_nand_init(struct nand_chip *nand) +{ +#if defined CONFIG_KEYSTONE_RBL_NAND + int i; + struct nand_ecclayout *layout; + + layout = &nand_keystone_rbl_4bit_layout_oobfirst; + layout->oobavail = 0; + for (i = 0; layout->oobfree[i].length && + i < ARRAY_SIZE(layout->oobfree); i++) + layout->oobavail += layout->oobfree[i].length; + + nand->write_page = nand_davinci_write_page; + nand->ecc.read_page = nand_davinci_read_page_hwecc; +#endif + nand->chip_delay = 0; +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH; +#endif +#ifdef CONFIG_SYS_NAND_NO_SUBPAGE_WRITE + nand->options |= NAND_NO_SUBPAGE_WRITE; +#endif +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT + nand->options |= NAND_BUSWIDTH_16; +#endif +#ifdef CONFIG_SYS_NAND_HW_ECC + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->ecc.calculate = nand_davinci_calculate_ecc; + nand->ecc.correct = nand_davinci_correct_data; + nand->ecc.hwctl = nand_davinci_enable_hwecc; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif /* CONFIG_SYS_NAND_HW_ECC */ +#ifdef CONFIG_SYS_NAND_4BIT_HW_ECC_OOBFIRST + nand->ecc.mode = NAND_ECC_HW_OOB_FIRST; + nand->ecc.size = 512; + nand->ecc.bytes = 10; + nand->ecc.strength = 4; + nand->ecc.calculate = nand_davinci_4bit_calculate_ecc; + nand->ecc.correct = nand_davinci_4bit_correct_data; + nand->ecc.hwctl = nand_davinci_4bit_enable_hwecc; + nand->ecc.layout = &nand_davinci_4bit_layout_oobfirst; +#endif + /* Set address of hardware control function */ + nand->cmd_ctrl = nand_davinci_hwcontrol; + + nand->read_buf = nand_davinci_read_buf; + nand->write_buf = nand_davinci_write_buf; + + nand->dev_ready = nand_davinci_dev_ready; + + nand_flash_init(); +} + +int board_nand_init(struct nand_chip *chip) __attribute__((weak)); + +int board_nand_init(struct nand_chip *chip) +{ + davinci_nand_init(chip); + return 0; +} diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c new file mode 100644 index 0000000000..d1cac063f4 --- /dev/null +++ b/drivers/mtd/nand/raw/denali.c @@ -0,0 +1,1371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Panasonic Corporation + * Copyright (C) 2013-2014, Altera Corporation + * Copyright (C) 2009-2010, Intel Corporation and its suppliers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "denali.h" + +static dma_addr_t dma_map_single(void *dev, void *ptr, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr = (unsigned long)ptr; + + size = ALIGN(size, ARCH_DMA_MINALIGN); + + if (dir == DMA_FROM_DEVICE) + invalidate_dcache_range(addr, addr + size); + else + flush_dcache_range(addr, addr + size); + + return addr; +} + +static void dma_unmap_single(void *dev, dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + size = ALIGN(size, ARCH_DMA_MINALIGN); + + if (dir != DMA_TO_DEVICE) + invalidate_dcache_range(addr, addr + size); +} + +static int dma_mapping_error(void *dev, dma_addr_t addr) +{ + return 0; +} + +#define DENALI_NAND_NAME "denali-nand" + +/* for Indexed Addressing */ +#define DENALI_INDEXED_CTRL 0x00 +#define DENALI_INDEXED_DATA 0x10 + +#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ +#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ +#define DENALI_MAP10 (2 << 26) /* high-level control plane */ +#define DENALI_MAP11 (3 << 26) /* direct controller access */ + +/* MAP11 access cycle type */ +#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ +#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ +#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ + +/* MAP10 commands */ +#define DENALI_ERASE 0x01 + +#define DENALI_BANK(denali) ((denali)->active_bank << 24) + +#define DENALI_INVALID_BANK -1 +#define DENALI_NR_BANKS 4 + +/* + * The bus interface clock, clk_x, is phase aligned with the core clock. The + * clk_x is an integral multiple N of the core clk. The value N is configured + * at IP delivery time, and its available value is 4, 5, or 6. We need to align + * to the largest value to make it work with any possible configuration. + */ +#define DENALI_CLK_X_MULT 6 + +static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand); +} + +/* + * Direct Addressing - the slave address forms the control information (command + * type, bank, block, and page address). The slave data is the actual data to + * be transferred. This mode requires 28 bits of address region allocated. + */ +static u32 denali_direct_read(struct denali_nand_info *denali, u32 addr) +{ + return ioread32(denali->host + addr); +} + +static void denali_direct_write(struct denali_nand_info *denali, u32 addr, + u32 data) +{ + iowrite32(data, denali->host + addr); +} + +/* + * Indexed Addressing - address translation module intervenes in passing the + * control information. This mode reduces the required address range. The + * control information and transferred data are latched by the registers in + * the translation module. + */ +static u32 denali_indexed_read(struct denali_nand_info *denali, u32 addr) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + return ioread32(denali->host + DENALI_INDEXED_DATA); +} + +static void denali_indexed_write(struct denali_nand_info *denali, u32 addr, + u32 data) +{ + iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); + iowrite32(data, denali->host + DENALI_INDEXED_DATA); +} + +/* + * Use the configuration feature register to determine the maximum number of + * banks that the hardware supports. + */ +static void denali_detect_max_banks(struct denali_nand_info *denali) +{ + uint32_t features = ioread32(denali->reg + FEATURES); + + denali->max_banks = 1 << FIELD_GET(FEATURES__N_BANKS, features); + + /* the encoding changed from rev 5.0 to 5.1 */ + if (denali->revision < 0x0501) + denali->max_banks <<= 1; +} + +static void __maybe_unused denali_enable_irq(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + iowrite32(U32_MAX, denali->reg + INTR_EN(i)); + iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); +} + +static void __maybe_unused denali_disable_irq(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + iowrite32(0, denali->reg + INTR_EN(i)); + iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); +} + +static void denali_clear_irq(struct denali_nand_info *denali, + int bank, uint32_t irq_status) +{ + /* write one to clear bits */ + iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); +} + +static void denali_clear_irq_all(struct denali_nand_info *denali) +{ + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) + denali_clear_irq(denali, i, U32_MAX); +} + +static void __denali_check_irq(struct denali_nand_info *denali) +{ + uint32_t irq_status; + int i; + + for (i = 0; i < DENALI_NR_BANKS; i++) { + irq_status = ioread32(denali->reg + INTR_STATUS(i)); + denali_clear_irq(denali, i, irq_status); + + if (i != denali->active_bank) + continue; + + denali->irq_status |= irq_status; + } +} + +static void denali_reset_irq(struct denali_nand_info *denali) +{ + denali->irq_status = 0; + denali->irq_mask = 0; +} + +static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, + uint32_t irq_mask) +{ + unsigned long time_left = 1000000; + + while (time_left) { + __denali_check_irq(denali); + + if (irq_mask & denali->irq_status) + return denali->irq_status; + udelay(1); + time_left--; + } + + if (!time_left) { + dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", + irq_mask); + return 0; + } + + return denali->irq_status; +} + +static uint32_t denali_check_irq(struct denali_nand_info *denali) +{ + __denali_check_irq(denali); + + return denali->irq_status; +} + +static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + int i; + + for (i = 0; i < len; i++) + buf[i] = denali->host_read(denali, addr); +} + +static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + int i; + + for (i = 0; i < len; i++) + denali->host_write(denali, addr, buf[i]); +} + +static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + uint16_t *buf16 = (uint16_t *)buf; + int i; + + for (i = 0; i < len / 2; i++) + buf16[i] = denali->host_read(denali, addr); +} + +static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + u32 addr = DENALI_MAP11_DATA | DENALI_BANK(denali); + const uint16_t *buf16 = (const uint16_t *)buf; + int i; + + for (i = 0; i < len / 2; i++) + denali->host_write(denali, addr, buf16[i]); +} + +static uint8_t denali_read_byte(struct mtd_info *mtd) +{ + uint8_t byte; + + denali_read_buf(mtd, &byte, 1); + + return byte; +} + +static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + denali_write_buf(mtd, &byte, 1); +} + +static uint16_t denali_read_word(struct mtd_info *mtd) +{ + uint16_t word; + + denali_read_buf16(mtd, (uint8_t *)&word, 2); + + return word; +} + +static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t type; + + if (ctrl & NAND_CLE) + type = DENALI_MAP11_CMD; + else if (ctrl & NAND_ALE) + type = DENALI_MAP11_ADDR; + else + return; + + /* + * Some commands are followed by chip->dev_ready or chip->waitfunc. + * irq_status must be cleared here to catch the R/B# interrupt later. + */ + if (ctrl & NAND_CTRL_CHANGE) + denali_reset_irq(denali); + + denali->host_write(denali, DENALI_BANK(denali) | type, dat); +} + +static int denali_dev_ready(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + return !!(denali_check_irq(denali) & INTR__INT_ACT); +} + +static int denali_check_erased_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + unsigned long uncor_ecc_flags, + unsigned int max_bitflips) +{ + uint8_t *ecc_code = chip->buffers->ecccode; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int i, ret, stat; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + for (i = 0; i < ecc_steps; i++) { + if (!(uncor_ecc_flags & BIT(i))) + continue; + + stat = nand_check_erased_ecc_chunk(buf, ecc_size, + ecc_code, ecc_bytes, + NULL, 0, + chip->ecc.strength); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + + buf += ecc_size; + ecc_code += ecc_bytes; + } + + return max_bitflips; +} + +static int denali_hw_ecc_fixup(struct mtd_info *mtd, + struct denali_nand_info *denali, + unsigned long *uncor_ecc_flags) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int bank = denali->active_bank; + uint32_t ecc_cor; + unsigned int max_bitflips; + + ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); + ecc_cor >>= ECC_COR_INFO__SHIFT(bank); + + if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { + /* + * This flag is set when uncorrectable error occurs at least in + * one ECC sector. We can not know "how many sectors", or + * "which sector(s)". We need erase-page check for all sectors. + */ + *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); + return 0; + } + + max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); + + /* + * The register holds the maximum of per-sector corrected bitflips. + * This is suitable for the return value of the ->read_page() callback. + * Unfortunately, we can not know the total number of corrected bits in + * the page. Increase the stats by max_bitflips. (compromised solution) + */ + mtd->ecc_stats.corrected += max_bitflips; + + return max_bitflips; +} + +static int denali_sw_ecc_fixup(struct mtd_info *mtd, + struct denali_nand_info *denali, + unsigned long *uncor_ecc_flags, uint8_t *buf) +{ + unsigned int ecc_size = denali->nand.ecc.size; + unsigned int bitflips = 0; + unsigned int max_bitflips = 0; + uint32_t err_addr, err_cor_info; + unsigned int err_byte, err_sector, err_device; + uint8_t err_cor_value; + unsigned int prev_sector = 0; + uint32_t irq_status; + + denali_reset_irq(denali); + + do { + err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); + err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); + err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); + + err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); + err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, + err_cor_info); + err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, + err_cor_info); + + /* reset the bitflip counter when crossing ECC sector */ + if (err_sector != prev_sector) + bitflips = 0; + + if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { + /* + * Check later if this is a real ECC error, or + * an erased sector. + */ + *uncor_ecc_flags |= BIT(err_sector); + } else if (err_byte < ecc_size) { + /* + * If err_byte is larger than ecc_size, means error + * happened in OOB, so we ignore it. It's no need for + * us to correct it err_device is represented the NAND + * error bits are happened in if there are more than + * one NAND connected. + */ + int offset; + unsigned int flips_in_byte; + + offset = (err_sector * ecc_size + err_byte) * + denali->devs_per_cs + err_device; + + /* correct the ECC error */ + flips_in_byte = hweight8(buf[offset] ^ err_cor_value); + buf[offset] ^= err_cor_value; + mtd->ecc_stats.corrected += flips_in_byte; + bitflips += flips_in_byte; + + max_bitflips = max(max_bitflips, bitflips); + } + + prev_sector = err_sector; + } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); + + /* + * Once handle all ECC errors, controller will trigger an + * ECC_TRANSACTION_DONE interrupt. + */ + irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); + if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) + return -EIO; + + return max_bitflips; +} + +static void denali_setup_dma64(struct denali_nand_info *denali, + dma_addr_t dma_addr, int page, int write) +{ + uint32_t mode; + const int page_count = 1; + + mode = DENALI_MAP10 | DENALI_BANK(denali) | page; + + /* DMA is a three step process */ + + /* + * 1. setup transfer type, interrupt when complete, + * burst len = 64 bytes, the number of pages + */ + denali->host_write(denali, mode, + 0x01002000 | (64 << 16) | (write << 8) | page_count); + + /* 2. set memory low address */ + denali->host_write(denali, mode, lower_32_bits(dma_addr)); + + /* 3. set memory high address */ + denali->host_write(denali, mode, upper_32_bits(dma_addr)); +} + +static void denali_setup_dma32(struct denali_nand_info *denali, + dma_addr_t dma_addr, int page, int write) +{ + uint32_t mode; + const int page_count = 1; + + mode = DENALI_MAP10 | DENALI_BANK(denali); + + /* DMA is a four step process */ + + /* 1. setup transfer type and # of pages */ + denali->host_write(denali, mode | page, + 0x2000 | (write << 8) | page_count); + + /* 2. set memory high address bits 23:8 */ + denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); + + /* 3. set memory low address bits 23:8 */ + denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); + + /* 4. interrupt when complete, burst len = 64 bytes */ + denali->host_write(denali, mode | 0x14000, 0x2400); +} + +static int denali_pio_read(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw) +{ + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; + uint32_t *buf32 = (uint32_t *)buf; + uint32_t irq_status, ecc_err_mask; + int i; + + if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) + ecc_err_mask = INTR__ECC_UNCOR_ERR; + else + ecc_err_mask = INTR__ECC_ERR; + + denali_reset_irq(denali); + + for (i = 0; i < size / 4; i++) + *buf32++ = denali->host_read(denali, addr); + + irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); + if (!(irq_status & INTR__PAGE_XFER_INC)) + return -EIO; + + if (irq_status & INTR__ERASED_PAGE) + memset(buf, 0xff, size); + + return irq_status & ecc_err_mask ? -EBADMSG : 0; +} + +static int denali_pio_write(struct denali_nand_info *denali, + const void *buf, size_t size, int page, int raw) +{ + u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; + const uint32_t *buf32 = (uint32_t *)buf; + uint32_t irq_status; + int i; + + denali_reset_irq(denali); + + for (i = 0; i < size / 4; i++) + denali->host_write(denali, addr, *buf32++); + + irq_status = denali_wait_for_irq(denali, + INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL); + if (!(irq_status & INTR__PROGRAM_COMP)) + return -EIO; + + return 0; +} + +static int denali_pio_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + if (write) + return denali_pio_write(denali, buf, size, page, raw); + else + return denali_pio_read(denali, buf, size, page, raw); +} + +static int denali_dma_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + dma_addr_t dma_addr; + uint32_t irq_mask, irq_status, ecc_err_mask; + enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + int ret = 0; + + dma_addr = dma_map_single(denali->dev, buf, size, dir); + if (dma_mapping_error(denali->dev, dma_addr)) { + dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); + return denali_pio_xfer(denali, buf, size, page, raw, write); + } + + if (write) { + /* + * INTR__PROGRAM_COMP is never asserted for the DMA transfer. + * We can use INTR__DMA_CMD_COMP instead. This flag is asserted + * when the page program is completed. + */ + irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; + ecc_err_mask = 0; + } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { + irq_mask = INTR__DMA_CMD_COMP; + ecc_err_mask = INTR__ECC_UNCOR_ERR; + } else { + irq_mask = INTR__DMA_CMD_COMP; + ecc_err_mask = INTR__ECC_ERR; + } + + iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); + + denali_reset_irq(denali); + denali->setup_dma(denali, dma_addr, page, write); + + irq_status = denali_wait_for_irq(denali, irq_mask); + if (!(irq_status & INTR__DMA_CMD_COMP)) + ret = -EIO; + else if (irq_status & ecc_err_mask) + ret = -EBADMSG; + + iowrite32(0, denali->reg + DMA_ENABLE); + + dma_unmap_single(denali->dev, dma_addr, size, dir); + + if (irq_status & INTR__ERASED_PAGE) + memset(buf, 0xff, size); + + return ret; +} + +static int denali_data_xfer(struct denali_nand_info *denali, void *buf, + size_t size, int page, int raw, int write) +{ + iowrite32(raw ? 0 : ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); + iowrite32(raw ? TRANSFER_SPARE_REG__FLAG : 0, + denali->reg + TRANSFER_SPARE_REG); + + if (denali->dma_avail) + return denali_dma_xfer(denali, buf, size, page, raw, write); + else + return denali_pio_xfer(denali, buf, size, page, raw, write); +} + +static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, + int page, int write) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0; + unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT; + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + uint8_t *bufpoi = chip->oob_poi; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int i, pos, len; + + /* BBM at the beginning of the OOB area */ + chip->cmdfunc(mtd, start_cmd, writesize, page); + if (write) + chip->write_buf(mtd, bufpoi, oob_skip); + else + chip->read_buf(mtd, bufpoi, oob_skip); + bufpoi += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + chip->cmdfunc(mtd, rnd_cmd, pos, -1); + if (write) + chip->write_buf(mtd, bufpoi, len); + else + chip->read_buf(mtd, bufpoi, len); + bufpoi += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1); + if (write) + chip->write_buf(mtd, bufpoi, len); + else + chip->read_buf(mtd, bufpoi, len); + bufpoi += len; + } + } + + /* OOB free */ + len = oobsize - (bufpoi - chip->oob_poi); + chip->cmdfunc(mtd, rnd_cmd, size - len, -1); + if (write) + chip->write_buf(mtd, bufpoi, len); + else + chip->read_buf(mtd, bufpoi, len); +} + +static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = denali->buf; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int ret, i, pos, len; + + ret = denali_data_xfer(denali, tmp_buf, size, page, 1, 0); + if (ret) + return ret; + + /* Arrange the buffer for syndrome payload/ecc layout */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(buf, tmp_buf + pos, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(buf, tmp_buf + writesize + oob_skip, + len); + buf += len; + } + } + } + + if (oob_required) { + uint8_t *oob = chip->oob_poi; + + /* BBM at the beginning of the OOB area */ + memcpy(oob, tmp_buf + writesize, oob_skip); + oob += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(oob, tmp_buf + pos, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(oob, tmp_buf + writesize + oob_skip, + len); + oob += len; + } + } + + /* OOB free */ + len = oobsize - (oob - chip->oob_poi); + memcpy(oob, tmp_buf + size - len, len); + } + + return 0; +} + +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + denali_oob_xfer(mtd, chip, page, 0); + + return 0; +} + +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int status; + + denali_reset_irq(denali); + + denali_oob_xfer(mtd, chip, page, 1); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + unsigned long uncor_ecc_flags = 0; + int stat = 0; + int ret; + + ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0); + if (ret && ret != -EBADMSG) + return ret; + + if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) + stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags); + else if (ret == -EBADMSG) + stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf); + + if (stat < 0) + return stat; + + if (uncor_ecc_flags) { + ret = denali_read_oob(mtd, chip, page); + if (ret) + return ret; + + stat = denali_check_erased_page(mtd, chip, buf, + uncor_ecc_flags, stat); + } + + return stat; +} + +static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = denali->buf; + int oob_skip = denali->oob_skip_bytes; + size_t size = writesize + oobsize; + int i, pos, len; + + /* + * Fill the buffer with 0xff first except the full page transfer. + * This simplifies the logic. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, size); + + /* Arrange the buffer for syndrome payload/ecc layout */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, buf, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(tmp_buf + writesize + oob_skip, buf, + len); + buf += len; + } + } + } + + if (oob_required) { + const uint8_t *oob = chip->oob_poi; + + /* BBM at the beginning of the OOB area */ + memcpy(tmp_buf + writesize, oob, oob_skip); + oob += oob_skip; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, oob, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(tmp_buf + writesize + oob_skip, oob, + len); + oob += len; + } + } + + /* OOB free */ + len = oobsize - (oob - chip->oob_poi); + memcpy(tmp_buf + size - len, oob, len); + } + + return denali_data_xfer(denali, tmp_buf, size, page, 1, 1); +} + +static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + return denali_data_xfer(denali, (void *)buf, mtd->writesize, + page, 0, 1); +} + +static void denali_select_chip(struct mtd_info *mtd, int chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + denali->active_bank = chip; +} + +static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status; + + /* R/B# pin transitioned from low to high? */ + irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); + + return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; +} + +static int denali_erase(struct mtd_info *mtd, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status; + + denali_reset_irq(denali); + + denali->host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page, + DENALI_ERASE); + + /* wait for erase to complete or failure to occur */ + irq_status = denali_wait_for_irq(denali, + INTR__ERASE_COMP | INTR__ERASE_FAIL); + + return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL; +} + +static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + const struct nand_sdr_timings *timings; + unsigned long t_clk; + int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; + int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; + int addr_2_data_mask; + uint32_t tmp; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + /* clk_x period in picoseconds */ + t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); + if (!t_clk) + return -EINVAL; + + if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + /* tREA -> ACC_CLKS */ + acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk); + acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); + + tmp = ioread32(denali->reg + ACC_CLKS); + tmp &= ~ACC_CLKS__VALUE; + tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); + iowrite32(tmp, denali->reg + ACC_CLKS); + + /* tRWH -> RE_2_WE */ + re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk); + re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); + + tmp = ioread32(denali->reg + RE_2_WE); + tmp &= ~RE_2_WE__VALUE; + tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); + iowrite32(tmp, denali->reg + RE_2_WE); + + /* tRHZ -> RE_2_RE */ + re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk); + re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); + + tmp = ioread32(denali->reg + RE_2_RE); + tmp &= ~RE_2_RE__VALUE; + tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); + iowrite32(tmp, denali->reg + RE_2_RE); + + /* + * tCCS, tWHR -> WE_2_RE + * + * With WE_2_RE properly set, the Denali controller automatically takes + * care of the delay; the driver need not set NAND_WAIT_TCCS. + */ + we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), + t_clk); + we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); + + tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); + tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; + tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); + iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE); + + /* tADL -> ADDR_2_DATA */ + + /* for older versions, ADDR_2_DATA is only 6 bit wide */ + addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; + if (denali->revision < 0x0501) + addr_2_data_mask >>= 1; + + addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk); + addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); + + tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); + tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; + tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); + iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA); + + /* tREH, tWH -> RDWR_EN_HI_CNT */ + rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), + t_clk); + rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); + + tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); + tmp &= ~RDWR_EN_HI_CNT__VALUE; + tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); + iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT); + + /* tRP, tWP -> RDWR_EN_LO_CNT */ + rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), + t_clk); + rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), + t_clk); + rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT); + rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); + rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); + + tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); + tmp &= ~RDWR_EN_LO_CNT__VALUE; + tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); + iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT); + + /* tCS, tCEA -> CS_SETUP_CNT */ + cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo, + (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks, + 0); + cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); + + tmp = ioread32(denali->reg + CS_SETUP_CNT); + tmp &= ~CS_SETUP_CNT__VALUE; + tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); + iowrite32(tmp, denali->reg + CS_SETUP_CNT); + + return 0; +} + +static void denali_reset_banks(struct denali_nand_info *denali) +{ + u32 irq_status; + int i; + + for (i = 0; i < denali->max_banks; i++) { + denali->active_bank = i; + + denali_reset_irq(denali); + + iowrite32(DEVICE_RESET__BANK(i), + denali->reg + DEVICE_RESET); + + irq_status = denali_wait_for_irq(denali, + INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT); + if (!(irq_status & INTR__INT_ACT)) + break; + } + + dev_dbg(denali->dev, "%d chips connected\n", i); + denali->max_banks = i; +} + +static void denali_hw_init(struct denali_nand_info *denali) +{ + /* + * The REVISION register may not be reliable. Platforms are allowed to + * override it. + */ + if (!denali->revision) + denali->revision = swab16(ioread32(denali->reg + REVISION)); + + /* + * tell driver how many bit controller will skip before writing + * ECC code in OOB. This is normally used for bad block marker + */ + denali->oob_skip_bytes = CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES; + iowrite32(denali->oob_skip_bytes, denali->reg + SPARE_AREA_SKIP_BYTES); + denali_detect_max_banks(denali); + iowrite32(0x0F, denali->reg + RB_PIN_ENABLED); + iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); + + iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); +} + +int denali_calc_ecc_bytes(int step_size, int strength) +{ + /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ + return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; +} +EXPORT_SYMBOL(denali_calc_ecc_bytes); + +static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, + struct denali_nand_info *denali) +{ + int oobavail = mtd->oobsize - denali->oob_skip_bytes; + int ret; + + /* + * If .size and .strength are already set (usually by DT), + * check if they are supported by this controller. + */ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail); + + /* + * We want .size and .strength closest to the chip's requirement + * unless NAND_ECC_MAXIMIZE is requested. + */ + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { + ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail); + if (!ret) + return 0; + } + + /* Max ECC strength is the last thing we can do */ + return nand_maximize_ecc(chip, denali->ecc_caps, oobavail); +} + +static struct nand_ecclayout nand_oob; + +static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = denali->oob_skip_bytes; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int denali_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops denali_ooblayout_ops = { + .ecc = denali_ooblayout_ecc, + .free = denali_ooblayout_free, +}; + +static int denali_multidev_fixup(struct denali_nand_info *denali) +{ + struct nand_chip *chip = &denali->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + + /* + * Support for multi device: + * When the IP configuration is x16 capable and two x8 chips are + * connected in parallel, DEVICES_CONNECTED should be set to 2. + * In this case, the core framework knows nothing about this fact, + * so we should tell it the _logical_ pagesize and anything necessary. + */ + denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); + + /* + * On some SoCs, DEVICES_CONNECTED is not auto-detected. + * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. + */ + if (denali->devs_per_cs == 0) { + denali->devs_per_cs = 1; + iowrite32(1, denali->reg + DEVICES_CONNECTED); + } + + if (denali->devs_per_cs == 1) + return 0; + + if (denali->devs_per_cs != 2) { + dev_err(denali->dev, "unsupported number of devices %d\n", + denali->devs_per_cs); + return -EINVAL; + } + + /* 2 chips in parallel */ + mtd->size <<= 1; + mtd->erasesize <<= 1; + mtd->writesize <<= 1; + mtd->oobsize <<= 1; + chip->chipsize <<= 1; + chip->page_shift += 1; + chip->phys_erase_shift += 1; + chip->bbt_erase_shift += 1; + chip->chip_shift += 1; + chip->pagemask <<= 1; + chip->ecc.size <<= 1; + chip->ecc.bytes <<= 1; + chip->ecc.strength <<= 1; + denali->oob_skip_bytes <<= 1; + + return 0; +} + +int denali_init(struct denali_nand_info *denali) +{ + struct nand_chip *chip = &denali->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + u32 features = ioread32(denali->reg + FEATURES); + int ret; + + denali_hw_init(denali); + + denali_clear_irq_all(denali); + + denali_reset_banks(denali); + + denali->active_bank = DENALI_INVALID_BANK; + + chip->flash_node = dev_of_offset(denali->dev); + /* Fallback to the default name if DT did not give "label" property */ + if (!mtd->name) + mtd->name = "denali-nand"; + + chip->select_chip = denali_select_chip; + chip->read_byte = denali_read_byte; + chip->write_byte = denali_write_byte; + chip->read_word = denali_read_word; + chip->cmd_ctrl = denali_cmd_ctrl; + chip->dev_ready = denali_dev_ready; + chip->waitfunc = denali_waitfunc; + + if (features & FEATURES__INDEX_ADDR) { + denali->host_read = denali_indexed_read; + denali->host_write = denali_indexed_write; + } else { + denali->host_read = denali_direct_read; + denali->host_write = denali_direct_write; + } + + /* clk rate info is needed for setup_data_interface */ + if (denali->clk_x_rate) + chip->setup_data_interface = denali_setup_data_interface; + + ret = nand_scan_ident(mtd, denali->max_banks, NULL); + if (ret) + return ret; + + if (ioread32(denali->reg + FEATURES) & FEATURES__DMA) + denali->dma_avail = 1; + + if (denali->dma_avail) { + chip->buf_align = ARCH_DMA_MINALIGN; + if (denali->caps & DENALI_CAP_DMA_64BIT) + denali->setup_dma = denali_setup_dma64; + else + denali->setup_dma = denali_setup_dma32; + } else { + chip->buf_align = 4; + } + + chip->options |= NAND_USE_BOUNCE_BUFFER; + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->bbt_options |= NAND_BBT_NO_OOB; + denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + + /* no subpage writes on denali */ + chip->options |= NAND_NO_SUBPAGE_WRITE; + + ret = denali_ecc_setup(mtd, chip, denali); + if (ret) { + dev_err(denali->dev, "Failed to setup ECC settings.\n"); + return ret; + } + + dev_dbg(denali->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | + FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), + denali->reg + ECC_CORRECTION); + iowrite32(mtd->erasesize / mtd->writesize, + denali->reg + PAGES_PER_BLOCK); + iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, + denali->reg + DEVICE_WIDTH); + iowrite32(chip->options & NAND_ROW_ADDR_3 ? 0 : TWO_ROW_ADDR_CYCLES__FLAG, + denali->reg + TWO_ROW_ADDR_CYCLES); + iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); + iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); + + iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); + iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); + /* chip->ecc.steps is set by nand_scan_tail(); not available here */ + iowrite32(mtd->writesize / chip->ecc.size, + denali->reg + CFG_NUM_DATA_BLOCKS); + + mtd_set_ooblayout(mtd, &denali_ooblayout_ops); + + nand_oob.eccbytes = denali->nand.ecc.bytes; + denali->nand.ecc.layout = &nand_oob; + + if (chip->options & NAND_BUSWIDTH_16) { + chip->read_buf = denali_read_buf16; + chip->write_buf = denali_write_buf16; + } else { + chip->read_buf = denali_read_buf; + chip->write_buf = denali_write_buf; + } + chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; + chip->ecc.read_page = denali_read_page; + chip->ecc.read_page_raw = denali_read_page_raw; + chip->ecc.write_page = denali_write_page; + chip->ecc.write_page_raw = denali_write_page_raw; + chip->ecc.read_oob = denali_read_oob; + chip->ecc.write_oob = denali_write_oob; + chip->erase = denali_erase; + + ret = denali_multidev_fixup(denali); + if (ret) + return ret; + + /* + * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not + * use devm_kmalloc() because the memory allocated by devm_ does not + * guarantee DMA-safe alignment. + */ + denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!denali->buf) + return -ENOMEM; + + ret = nand_scan_tail(mtd); + if (ret) + goto free_buf; + + ret = nand_register(0, mtd); + if (ret) { + dev_err(denali->dev, "Failed to register MTD: %d\n", ret); + goto free_buf; + } + return 0; + +free_buf: + kfree(denali->buf); + + return ret; +} diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h new file mode 100644 index 0000000000..9b797beffa --- /dev/null +++ b/drivers/mtd/nand/raw/denali.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2013-2014 Altera Corporation + * Copyright (C) 2009-2010, Intel Corporation and its suppliers. + */ + +#ifndef __DENALI_H__ +#define __DENALI_H__ + +#include +#include +#include + +#define DEVICE_RESET 0x0 +#define DEVICE_RESET__BANK(bank) BIT(bank) + +#define TRANSFER_SPARE_REG 0x10 +#define TRANSFER_SPARE_REG__FLAG BIT(0) + +#define LOAD_WAIT_CNT 0x20 +#define LOAD_WAIT_CNT__VALUE GENMASK(15, 0) + +#define PROGRAM_WAIT_CNT 0x30 +#define PROGRAM_WAIT_CNT__VALUE GENMASK(15, 0) + +#define ERASE_WAIT_CNT 0x40 +#define ERASE_WAIT_CNT__VALUE GENMASK(15, 0) + +#define INT_MON_CYCCNT 0x50 +#define INT_MON_CYCCNT__VALUE GENMASK(15, 0) + +#define RB_PIN_ENABLED 0x60 +#define RB_PIN_ENABLED__BANK(bank) BIT(bank) + +#define MULTIPLANE_OPERATION 0x70 +#define MULTIPLANE_OPERATION__FLAG BIT(0) + +#define MULTIPLANE_READ_ENABLE 0x80 +#define MULTIPLANE_READ_ENABLE__FLAG BIT(0) + +#define COPYBACK_DISABLE 0x90 +#define COPYBACK_DISABLE__FLAG BIT(0) + +#define CACHE_WRITE_ENABLE 0xa0 +#define CACHE_WRITE_ENABLE__FLAG BIT(0) + +#define CACHE_READ_ENABLE 0xb0 +#define CACHE_READ_ENABLE__FLAG BIT(0) + +#define PREFETCH_MODE 0xc0 +#define PREFETCH_MODE__PREFETCH_EN BIT(0) +#define PREFETCH_MODE__PREFETCH_BURST_LENGTH GENMASK(15, 4) + +#define CHIP_ENABLE_DONT_CARE 0xd0 +#define CHIP_EN_DONT_CARE__FLAG BIT(0) + +#define ECC_ENABLE 0xe0 +#define ECC_ENABLE__FLAG BIT(0) + +#define GLOBAL_INT_ENABLE 0xf0 +#define GLOBAL_INT_EN_FLAG BIT(0) + +#define TWHR2_AND_WE_2_RE 0x100 +#define TWHR2_AND_WE_2_RE__WE_2_RE GENMASK(5, 0) +#define TWHR2_AND_WE_2_RE__TWHR2 GENMASK(13, 8) + +#define TCWAW_AND_ADDR_2_DATA 0x110 +/* The width of ADDR_2_DATA is 6 bit for old IP, 7 bit for new IP */ +#define TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA GENMASK(6, 0) +#define TCWAW_AND_ADDR_2_DATA__TCWAW GENMASK(13, 8) + +#define RE_2_WE 0x120 +#define RE_2_WE__VALUE GENMASK(5, 0) + +#define ACC_CLKS 0x130 +#define ACC_CLKS__VALUE GENMASK(3, 0) + +#define NUMBER_OF_PLANES 0x140 +#define NUMBER_OF_PLANES__VALUE GENMASK(2, 0) + +#define PAGES_PER_BLOCK 0x150 +#define PAGES_PER_BLOCK__VALUE GENMASK(15, 0) + +#define DEVICE_WIDTH 0x160 +#define DEVICE_WIDTH__VALUE GENMASK(1, 0) + +#define DEVICE_MAIN_AREA_SIZE 0x170 +#define DEVICE_MAIN_AREA_SIZE__VALUE GENMASK(15, 0) + +#define DEVICE_SPARE_AREA_SIZE 0x180 +#define DEVICE_SPARE_AREA_SIZE__VALUE GENMASK(15, 0) + +#define TWO_ROW_ADDR_CYCLES 0x190 +#define TWO_ROW_ADDR_CYCLES__FLAG BIT(0) + +#define MULTIPLANE_ADDR_RESTRICT 0x1a0 +#define MULTIPLANE_ADDR_RESTRICT__FLAG BIT(0) + +#define ECC_CORRECTION 0x1b0 +#define ECC_CORRECTION__VALUE GENMASK(4, 0) +#define ECC_CORRECTION__ERASE_THRESHOLD GENMASK(31, 16) + +#define READ_MODE 0x1c0 +#define READ_MODE__VALUE GENMASK(3, 0) + +#define WRITE_MODE 0x1d0 +#define WRITE_MODE__VALUE GENMASK(3, 0) + +#define COPYBACK_MODE 0x1e0 +#define COPYBACK_MODE__VALUE GENMASK(3, 0) + +#define RDWR_EN_LO_CNT 0x1f0 +#define RDWR_EN_LO_CNT__VALUE GENMASK(4, 0) + +#define RDWR_EN_HI_CNT 0x200 +#define RDWR_EN_HI_CNT__VALUE GENMASK(4, 0) + +#define MAX_RD_DELAY 0x210 +#define MAX_RD_DELAY__VALUE GENMASK(3, 0) + +#define CS_SETUP_CNT 0x220 +#define CS_SETUP_CNT__VALUE GENMASK(4, 0) +#define CS_SETUP_CNT__TWB GENMASK(17, 12) + +#define SPARE_AREA_SKIP_BYTES 0x230 +#define SPARE_AREA_SKIP_BYTES__VALUE GENMASK(5, 0) + +#define SPARE_AREA_MARKER 0x240 +#define SPARE_AREA_MARKER__VALUE GENMASK(15, 0) + +#define DEVICES_CONNECTED 0x250 +#define DEVICES_CONNECTED__VALUE GENMASK(2, 0) + +#define DIE_MASK 0x260 +#define DIE_MASK__VALUE GENMASK(7, 0) + +#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 +#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE GENMASK(15, 0) + +#define WRITE_PROTECT 0x280 +#define WRITE_PROTECT__FLAG BIT(0) + +#define RE_2_RE 0x290 +#define RE_2_RE__VALUE GENMASK(5, 0) + +#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID__VALUE GENMASK(7, 0) + +#define DEVICE_ID 0x310 +#define DEVICE_ID__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_0 0x320 +#define DEVICE_PARAM_0__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_1 0x330 +#define DEVICE_PARAM_1__VALUE GENMASK(7, 0) + +#define DEVICE_PARAM_2 0x340 +#define DEVICE_PARAM_2__VALUE GENMASK(7, 0) + +#define LOGICAL_PAGE_DATA_SIZE 0x350 +#define LOGICAL_PAGE_DATA_SIZE__VALUE GENMASK(15, 0) + +#define LOGICAL_PAGE_SPARE_SIZE 0x360 +#define LOGICAL_PAGE_SPARE_SIZE__VALUE GENMASK(15, 0) + +#define REVISION 0x370 +#define REVISION__VALUE GENMASK(15, 0) + +#define ONFI_DEVICE_FEATURES 0x380 +#define ONFI_DEVICE_FEATURES__VALUE GENMASK(5, 0) + +#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS__VALUE GENMASK(5, 0) + +#define ONFI_TIMING_MODE 0x3a0 +#define ONFI_TIMING_MODE__VALUE GENMASK(5, 0) + +#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 +#define ONFI_PGM_CACHE_TIMING_MODE__VALUE GENMASK(5, 0) + +#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 +#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS GENMASK(7, 0) +#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE BIT(8) + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE GENMASK(15, 0) + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE GENMASK(15, 0) + +#define FEATURES 0x3f0 +#define FEATURES__N_BANKS GENMASK(1, 0) +#define FEATURES__ECC_MAX_ERR GENMASK(5, 2) +#define FEATURES__DMA BIT(6) +#define FEATURES__CMD_DMA BIT(7) +#define FEATURES__PARTITION BIT(8) +#define FEATURES__XDMA_SIDEBAND BIT(9) +#define FEATURES__GPREG BIT(10) +#define FEATURES__INDEX_ADDR BIT(11) + +#define TRANSFER_MODE 0x400 +#define TRANSFER_MODE__VALUE GENMASK(1, 0) + +#define INTR_STATUS(bank) (0x410 + (bank) * 0x50) +#define INTR_EN(bank) (0x420 + (bank) * 0x50) +/* bit[1:0] is used differently depending on IP version */ +#define INTR__ECC_UNCOR_ERR BIT(0) /* new IP */ +#define INTR__ECC_TRANSACTION_DONE BIT(0) /* old IP */ +#define INTR__ECC_ERR BIT(1) /* old IP */ +#define INTR__DMA_CMD_COMP BIT(2) +#define INTR__TIME_OUT BIT(3) +#define INTR__PROGRAM_FAIL BIT(4) +#define INTR__ERASE_FAIL BIT(5) +#define INTR__LOAD_COMP BIT(6) +#define INTR__PROGRAM_COMP BIT(7) +#define INTR__ERASE_COMP BIT(8) +#define INTR__PIPE_CPYBCK_CMD_COMP BIT(9) +#define INTR__LOCKED_BLK BIT(10) +#define INTR__UNSUP_CMD BIT(11) +#define INTR__INT_ACT BIT(12) +#define INTR__RST_COMP BIT(13) +#define INTR__PIPE_CMD_ERR BIT(14) +#define INTR__PAGE_XFER_INC BIT(15) +#define INTR__ERASED_PAGE BIT(16) + +#define PAGE_CNT(bank) (0x430 + (bank) * 0x50) +#define ERR_PAGE_ADDR(bank) (0x440 + (bank) * 0x50) +#define ERR_BLOCK_ADDR(bank) (0x450 + (bank) * 0x50) + +#define ECC_THRESHOLD 0x600 +#define ECC_THRESHOLD__VALUE GENMASK(9, 0) + +#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS__VALUE GENMASK(15, 0) + +#define ECC_ERROR_PAGE_ADDRESS 0x620 +#define ECC_ERROR_PAGE_ADDRESS__VALUE GENMASK(11, 0) +#define ECC_ERROR_PAGE_ADDRESS__BANK GENMASK(15, 12) + +#define ECC_ERROR_ADDRESS 0x630 +#define ECC_ERROR_ADDRESS__OFFSET GENMASK(11, 0) +#define ECC_ERROR_ADDRESS__SECTOR GENMASK(15, 12) + +#define ERR_CORRECTION_INFO 0x640 +#define ERR_CORRECTION_INFO__BYTE GENMASK(7, 0) +#define ERR_CORRECTION_INFO__DEVICE GENMASK(11, 8) +#define ERR_CORRECTION_INFO__UNCOR BIT(14) +#define ERR_CORRECTION_INFO__LAST_ERR BIT(15) + +#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10) +#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8) +#define ECC_COR_INFO__MAX_ERRORS GENMASK(6, 0) +#define ECC_COR_INFO__UNCOR_ERR BIT(7) + +#define CFG_DATA_BLOCK_SIZE 0x6b0 + +#define CFG_LAST_DATA_BLOCK_SIZE 0x6c0 + +#define CFG_NUM_DATA_BLOCKS 0x6d0 + +#define CFG_META_DATA_SIZE 0x6e0 + +#define DMA_ENABLE 0x700 +#define DMA_ENABLE__FLAG BIT(0) + +#define IGNORE_ECC_DONE 0x710 +#define IGNORE_ECC_DONE__FLAG BIT(0) + +#define DMA_INTR 0x720 +#define DMA_INTR_EN 0x730 +#define DMA_INTR__TARGET_ERROR BIT(0) +#define DMA_INTR__DESC_COMP_CHANNEL0 BIT(1) +#define DMA_INTR__DESC_COMP_CHANNEL1 BIT(2) +#define DMA_INTR__DESC_COMP_CHANNEL2 BIT(3) +#define DMA_INTR__DESC_COMP_CHANNEL3 BIT(4) +#define DMA_INTR__MEMCOPY_DESC_COMP BIT(5) + +#define TARGET_ERR_ADDR_LO 0x740 +#define TARGET_ERR_ADDR_LO__VALUE GENMASK(15, 0) + +#define TARGET_ERR_ADDR_HI 0x750 +#define TARGET_ERR_ADDR_HI__VALUE GENMASK(15, 0) + +#define CHNL_ACTIVE 0x760 +#define CHNL_ACTIVE__CHANNEL0 BIT(0) +#define CHNL_ACTIVE__CHANNEL1 BIT(1) +#define CHNL_ACTIVE__CHANNEL2 BIT(2) +#define CHNL_ACTIVE__CHANNEL3 BIT(3) + +struct udevice; + +struct denali_nand_info { + struct nand_chip nand; + unsigned long clk_x_rate; /* bus interface clock rate */ + int active_bank; /* currently selected bank */ + struct udevice *dev; + uint32_t page; + void __iomem *reg; /* Register Interface */ + void __iomem *host; /* Host Data/Command Interface */ + u32 irq_mask; /* interrupts we are waiting for */ + u32 irq_status; /* interrupts that have happened */ + int irq; + void *buf; /* for syndrome layout conversion */ + dma_addr_t dma_addr; + int dma_avail; /* can support DMA? */ + int devs_per_cs; /* devices connected in parallel */ + int oob_skip_bytes; /* number of bytes reserved for BBM */ + int max_banks; + unsigned int revision; /* IP revision */ + unsigned int caps; /* IP capability (or quirk) */ + const struct nand_ecc_caps *ecc_caps; + u32 (*host_read)(struct denali_nand_info *denali, u32 addr); + void (*host_write)(struct denali_nand_info *denali, u32 addr, u32 data); + void (*setup_dma)(struct denali_nand_info *denali, dma_addr_t dma_addr, + int page, int write); +}; + +#define DENALI_CAP_HW_ECC_FIXUP BIT(0) +#define DENALI_CAP_DMA_64BIT BIT(1) + +int denali_calc_ecc_bytes(int step_size, int strength); +int denali_init(struct denali_nand_info *denali); + +#endif /* __DENALI_H__ */ diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c new file mode 100644 index 0000000000..65a7797f0f --- /dev/null +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Socionext Inc. + * Author: Masahiro Yamada + */ + +#include +#include +#include +#include +#include + +#include "denali.h" + +struct denali_dt_data { + unsigned int revision; + unsigned int caps; + const struct nand_ecc_caps *ecc_caps; +}; + +NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes, + 512, 8, 15); +static const struct denali_dt_data denali_socfpga_data = { + .caps = DENALI_CAP_HW_ECC_FIXUP, + .ecc_caps = &denali_socfpga_ecc_caps, +}; + +NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes, + 1024, 8, 16, 24); +static const struct denali_dt_data denali_uniphier_v5a_data = { + .caps = DENALI_CAP_HW_ECC_FIXUP | + DENALI_CAP_DMA_64BIT, + .ecc_caps = &denali_uniphier_v5a_ecc_caps, +}; + +NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes, + 1024, 8, 16); +static const struct denali_dt_data denali_uniphier_v5b_data = { + .revision = 0x0501, + .caps = DENALI_CAP_HW_ECC_FIXUP | + DENALI_CAP_DMA_64BIT, + .ecc_caps = &denali_uniphier_v5b_ecc_caps, +}; + +static const struct udevice_id denali_nand_dt_ids[] = { + { + .compatible = "altr,socfpga-denali-nand", + .data = (unsigned long)&denali_socfpga_data, + }, + { + .compatible = "socionext,uniphier-denali-nand-v5a", + .data = (unsigned long)&denali_uniphier_v5a_data, + }, + { + .compatible = "socionext,uniphier-denali-nand-v5b", + .data = (unsigned long)&denali_uniphier_v5b_data, + }, + { /* sentinel */ } +}; + +static int denali_dt_probe(struct udevice *dev) +{ + struct denali_nand_info *denali = dev_get_priv(dev); + const struct denali_dt_data *data; + struct clk clk; + struct resource res; + int ret; + + data = (void *)dev_get_driver_data(dev); + if (data) { + denali->revision = data->revision; + denali->caps = data->caps; + denali->ecc_caps = data->ecc_caps; + } + + denali->dev = dev; + + ret = dev_read_resource_byname(dev, "denali_reg", &res); + if (ret) + return ret; + + denali->reg = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "nand_data", &res); + if (ret) + return ret; + + denali->host = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + return ret; + + denali->clk_x_rate = clk_get_rate(&clk); + + return denali_init(denali); +} + +U_BOOT_DRIVER(denali_nand_dt) = { + .name = "denali-nand-dt", + .id = UCLASS_MISC, + .of_match = denali_nand_dt_ids, + .probe = denali_dt_probe, + .priv_auto_alloc_size = sizeof(struct denali_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MISC, + DM_GET_DRIVER(denali_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize Denali NAND controller. (error %d)\n", + ret); +} diff --git a/drivers/mtd/nand/raw/denali_spl.c b/drivers/mtd/nand/raw/denali_spl.c new file mode 100644 index 0000000000..dbaba3cab2 --- /dev/null +++ b/drivers/mtd/nand/raw/denali_spl.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Panasonic Corporation + * Copyright (C) 2014-2015 Masahiro Yamada + */ + +#include +#include +#include +#include +#include "denali.h" + +#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ +#define DENALI_MAP10 (2 << 26) /* high-level control plane */ + +#define INDEX_CTRL_REG 0x0 +#define INDEX_DATA_REG 0x10 + +#define SPARE_ACCESS 0x41 +#define MAIN_ACCESS 0x42 +#define PIPELINE_ACCESS 0x2000 + +#define BANK(x) ((x) << 24) + +static void __iomem *denali_flash_mem = + (void __iomem *)CONFIG_SYS_NAND_DATA_BASE; +static void __iomem *denali_flash_reg = + (void __iomem *)CONFIG_SYS_NAND_REGS_BASE; + +static const int flash_bank; +static int page_size, oob_size, pages_per_block; + +static void index_addr(uint32_t address, uint32_t data) +{ + writel(address, denali_flash_mem + INDEX_CTRL_REG); + writel(data, denali_flash_mem + INDEX_DATA_REG); +} + +static int wait_for_irq(uint32_t irq_mask) +{ + unsigned long timeout = 1000000; + uint32_t intr_status; + + do { + intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank)); + + if (intr_status & INTR__ECC_UNCOR_ERR) { + debug("Uncorrected ECC detected\n"); + return -EBADMSG; + } + + if (intr_status & irq_mask) + break; + + udelay(1); + timeout--; + } while (timeout); + + if (!timeout) { + debug("Timeout with interrupt status %08x\n", intr_status); + return -EIO; + } + + return 0; +} + +static void read_data_from_flash_mem(uint8_t *buf, int len) +{ + int i; + uint32_t *buf32; + + /* transfer the data from the flash */ + buf32 = (uint32_t *)buf; + + /* + * Let's take care of unaligned access although it rarely happens. + * Avoid put_unaligned() for the normal use cases since it leads to + * a bit performance regression. + */ + if ((unsigned long)buf32 % 4) { + for (i = 0; i < len / 4; i++) + put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG), + buf32++); + } else { + for (i = 0; i < len / 4; i++) + *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG); + } + + if (len % 4) { + u32 tmp; + + tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG)); + buf = (uint8_t *)buf32; + for (i = 0; i < len % 4; i++) { + *buf++ = tmp; + tmp >>= 8; + } + } +} + +int denali_send_pipeline_cmd(int page, int ecc_en, int access_type) +{ + uint32_t addr, cmd; + static uint32_t page_count = 1; + + writel(ecc_en, denali_flash_reg + ECC_ENABLE); + + /* clear all bits of intr_status. */ + writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank)); + + addr = BANK(flash_bank) | page; + + /* setup the acccess type */ + cmd = DENALI_MAP10 | addr; + index_addr(cmd, access_type); + + /* setup the pipeline command */ + index_addr(cmd, PIPELINE_ACCESS | page_count); + + cmd = DENALI_MAP01 | addr; + writel(cmd, denali_flash_mem + INDEX_CTRL_REG); + + return wait_for_irq(INTR__LOAD_COMP); +} + +static int nand_read_oob(void *buf, int page) +{ + int ret; + + ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS); + if (ret < 0) + return ret; + + read_data_from_flash_mem(buf, oob_size); + + return 0; +} + +static int nand_read_page(void *buf, int page) +{ + int ret; + + ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS); + if (ret < 0) + return ret; + + read_data_from_flash_mem(buf, page_size); + + return 0; +} + +static int nand_block_isbad(void *buf, int block) +{ + int ret; + + ret = nand_read_oob(buf, block * pages_per_block); + if (ret < 0) + return ret; + + return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* access to main area */ + writel(0, denali_flash_reg + TRANSFER_SPARE_REG); + + /* + * These registers are expected to be already set by the hardware + * or earlier boot code. So we read these values out. + */ + page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE); + oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE); + pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK); +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + int block, page, column, readlen; + int ret; + int force_bad_block_check = 1; + + page = offs / page_size; + column = offs % page_size; + + block = page / pages_per_block; + page = page % pages_per_block; + + while (size) { + if (force_bad_block_check || page == 0) { + ret = nand_block_isbad(dst, block); + if (ret < 0) + return ret; + + if (ret) { + block++; + continue; + } + } + + force_bad_block_check = 0; + + ret = nand_read_page(dst, block * pages_per_block + page); + if (ret < 0) + return ret; + + readlen = min(page_size - column, (int)size); + + if (unlikely(column)) { + /* Partial page read */ + memmove(dst, dst + column, readlen); + column = 0; + } + + size -= readlen; + dst += readlen; + page++; + if (page == pages_per_block) { + block++; + page = 0; + } + } + + return 0; +} + +void nand_deselect(void) {} diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c new file mode 100644 index 0000000000..263d46ec8f --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -0,0 +1,810 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Freescale Enhanced Local Bus Controller FCM NAND driver + * + * Copyright (c) 2006-2008 Freescale Semiconductor + * + * Authors: Nick Spence , + * Scott Wood + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef VERBOSE_DEBUG +#define DEBUG_ELBC +#define vdbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define vdbg(format, arg...) do {} while (0) +#endif + +/* Can't use plain old DEBUG because the linux mtd + * headers define it as a macro. + */ +#ifdef DEBUG_ELBC +#define dbg(format, arg...) printf("DEBUG: " format, ##arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ + +#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) + +struct fsl_elbc_ctrl; + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct nand_chip chip; + struct fsl_elbc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + u8 __iomem *vbase; /* Chip select base virtual address */ + int page_size; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* overview of the fsl elbc controller */ + +struct fsl_elbc_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *chips[MAX_BANKS]; + + /* device info */ + fsl_lbc_t *regs; + u8 __iomem *addr; /* Address of assigned FCM buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ + unsigned int oob; /* Non zero if operating on OOB data */ +}; + +/* These map to the positions used by the FCM hardware ECC generator */ + +/* Small Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { + .eccbytes = 3, + .eccpos = {6, 7, 8}, + .oobfree = { {0, 5}, {9, 7} }, +}; + +/* Small Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { + .eccbytes = 3, + .eccpos = {8, 9, 10}, + .oobfree = { {0, 5}, {6, 2}, {11, 5} }, +}; + +/* Large Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { + .eccbytes = 12, + .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, + .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, +}; + +/* Large Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { + .eccbytes = 12, + .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, + .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, +}; + +/* + * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset + * 1, so we have to adjust bad block pattern. This pattern should be used for + * x8 chips only. So far hardware does not support x16 chips anyway. + */ +static u8 scan_ff_pattern[] = { 0xff, }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; + +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + if (priv->page_size) { + out_be32(&lbc->fbar, page_addr >> 6); + out_be32(&lbc->fpar, + ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + (oob ? FPAR_LP_MS : 0) | column); + buf_num = (page_addr & 1) << 2; + } else { + out_be32(&lbc->fbar, page_addr >> 5); + out_be32(&lbc->fpar, + ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + (oob ? FPAR_SP_MS : 0) | column); + buf_num = page_addr & 7; + } + + ctrl->addr = priv->vbase + buf_num * 1024; + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += priv->page_size ? 2048 : 512; + + vdbg("set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " + "index %x, pes %d ps %d\n", + buf_num, ctrl->addr, priv->vbase, ctrl->index, + chip->phys_erase_shift, chip->page_shift); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; + u32 time_start; + u32 ltesr; + + /* Setup the FMR[OP] to execute without write protection */ + out_be32(&lbc->fmr, priv->fmr | 3); + if (ctrl->use_mdr) + out_be32(&lbc->mdr, ctrl->mdr); + + vdbg("fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", + in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); + vdbg("fsl_elbc_run_command: fbar=%08x fpar=%08x " + "fbcr=%08x bank=%d\n", + in_be32(&lbc->fbar), in_be32(&lbc->fpar), + in_be32(&lbc->fbcr), priv->bank); + + /* execute special operation */ + out_be32(&lbc->lsor, priv->bank); + + /* wait for FCM complete flag or timeout */ + time_start = get_timer(0); + + ltesr = 0; + while (get_timer(time_start) < timeo) { + ltesr = in_be32(&lbc->ltesr); + if (ltesr & LTESR_CC) + break; + } + + ctrl->status = ltesr & LTESR_NAND_MASK; + out_be32(&lbc->ltesr, ctrl->status); + out_be32(&lbc->lteatr, 0); + + /* store mdr value in case it was needed */ + if (ctrl->use_mdr) + ctrl->mdr = in_be32(&lbc->mdr); + + ctrl->use_mdr = 0; + + vdbg("fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", + ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == LTESR_CC ? 0 : -EIO; +} + +static void fsl_elbc_do_read(struct nand_chip *chip, int oob) +{ + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + + if (oob) + out_be32(&lbc->fcr, + NAND_CMD_READOOB << FCR_CMD0_SHIFT); + else + out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + ctrl->use_mdr = 0; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ1: + column += 256; + + /* fall-through */ + case NAND_CMD_READ0: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + fsl_elbc_do_read(chip, 0); + fsl_elbc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + return; + + /* READID must read all 5 possible bytes while CEB is active */ + case NAND_CMD_READID: + case NAND_CMD_PARAM: + vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command); + + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT)); + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + ctrl->read_bytes = 256; + ctrl->use_mdr = 1; + ctrl->mdr = column; + set_addr(mtd, 0, 0, 0); + fsl_elbc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " + "page_addr: 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM1 << FIR_OP2_SHIFT)); + + out_be32(&lbc->fcr, + (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + + out_be32(&lbc->fbcr, 0); + ctrl->read_bytes = 0; + + fsl_elbc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 fcr; + vdbg("fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + "page_addr: 0x%x, column: 0x%x.\n", + page_addr, column); + + ctrl->column = column; + ctrl->oob = 0; + + if (priv->page_size) { + fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | + (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CW1 << FIR_OP4_SHIFT)); + } else { + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT)); + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; + ctrl->oob = 1; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } else { + /* Second 256 bytes --> READ1 */ + fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; + } + } + + out_be32(&lbc->fcr, fcr); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " + "writing %d bytes.\n", ctrl->index); + + /* if the write did not start at 0 or is not a full page + * then set the exact length, otherwise use a full page + * write so the HW generates the ECC. + */ + if (ctrl->oob || ctrl->column != 0 || + ctrl->index != mtd->writesize + mtd->oobsize) + out_be32(&lbc->fbcr, ctrl->index); + else + out_be32(&lbc->fbcr, 0); + + fsl_elbc_run_command(mtd); + + return; + } + + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return; + + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + dbg("fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); + out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); + fsl_elbc_run_command(mtd); + return; + + default: + printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", + command); + } +} + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + printf("write_buf of %d bytes", len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + printf("write_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + /* + * This is workaround for the weird elbc hangs during nand write, + * Scott Wood says: "...perhaps difference in how long it takes a + * write to make it through the localbus compared to a write to IMMR + * is causing problems, and sync isn't helping for some reason." + * Reading back the last byte helps though. + */ + in_8(&ctrl->addr[ctrl->index] + len - 1); + + ctrl->index += len; +} + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + /* If there are still bytes in the FCM, then use the next byte. */ + if (ctrl->index < ctrl->read_bytes) + return in_8(&ctrl->addr[ctrl->index++]); + + printf("read_byte beyond end of buffer\n"); + return ERR_BYTE; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); + ctrl->index += avail; + + if (len > avail) + printf("read_buf beyond end of buffer " + "(%d requested, %d available)\n", + len, avail); +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + fsl_lbc_t *lbc = ctrl->regs; + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ctrl->use_mdr = 0; + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return fsl_elbc_read_byte(mtd); +} + +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + fsl_elbc_read_buf(mtd, buf, mtd->writesize); + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static struct fsl_elbc_ctrl *elbc_ctrl; + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *buf, int oob_required, int page) +{ + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void fsl_elbc_ctrl_init(void) +{ + elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL); + if (!elbc_ctrl) + return; + + elbc_ctrl->regs = LBC_BASE_ADDR; + + /* clear event registers */ + out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK); + out_be32(&elbc_ctrl->regs->lteatr, 0); + + /* Enable interrupts for any detected events */ + out_be32(&elbc_ctrl->regs->lteir, LTESR_NAND_MASK); + + elbc_ctrl->read_bytes = 0; + elbc_ctrl->index = 0; + elbc_ctrl->addr = NULL; +} + +static int fsl_elbc_chip_init(int devnum, u8 *addr) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + struct fsl_elbc_mtd *priv; + uint32_t br = 0, or = 0; + int ret; + + if (!elbc_ctrl) { + fsl_elbc_ctrl_init(); + if (!elbc_ctrl) + return -1; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ctrl = elbc_ctrl; + priv->vbase = addr; + + /* Find which chip select it is connected to. It'd be nice + * if we could pass more than one datum to the NAND driver... + */ + for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { + phys_addr_t phys_addr = virt_to_phys(addr); + + br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); + or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + + if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && + (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) + break; + } + + if (priv->bank >= MAX_BANKS) { + printf("fsl_elbc_nand: address did not match any " + "chip selects\n"); + kfree(priv); + return -ENODEV; + } + + nand = &priv->chip; + mtd = nand_to_mtd(nand); + + elbc_ctrl->chips[priv->bank] = priv; + + /* fill in nand_chip structure */ + /* set up function call table */ + nand->read_byte = fsl_elbc_read_byte; + nand->write_buf = fsl_elbc_write_buf; + nand->read_buf = fsl_elbc_read_buf; + nand->select_chip = fsl_elbc_select_chip; + nand->cmdfunc = fsl_elbc_cmdfunc; + nand->waitfunc = fsl_elbc_wait; + + /* set up nand options */ + nand->bbt_td = &bbt_main_descr; + nand->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + nand->options = NAND_NO_SUBPAGE_WRITE; + nand->bbt_options = NAND_BBT_USE_FLASH; + + nand->controller = &elbc_ctrl->controller; + nand_set_controller_data(nand, priv); + + nand->ecc.read_page = fsl_elbc_read_page; + nand->ecc.write_page = fsl_elbc_write_page; + nand->ecc.write_subpage = fsl_elbc_write_subpage; + + priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); + + /* If CS Base Register selects full hardware ECC then use it */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.mode = NAND_ECC_HW; + + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : + &fsl_elbc_oob_sp_eccm0; + + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = 1; + nand->ecc.strength = 1; + } else { + /* otherwise fall back to software ECC */ +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + } + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + /* Large-page-specific setup */ + if (mtd->writesize == 2048) { + setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + + priv->page_size = 1; + nand->badblock_pattern = &largepage_memorybased; + + /* + * Hardware expects small page has ECCM0, large page has + * ECCM1 when booting from NAND, and we follow that even + * when not booting from NAND. + */ + priv->fmr |= FMR_ECCM; + + /* adjust ecc setup if needed */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.steps = 4; + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_lp_eccm1 : + &fsl_elbc_oob_lp_eccm0; + } + } else if (mtd->writesize == 512) { + clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + } else { + return -ENODEV; + } + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum, mtd); + if (ret) + return ret; + + return 0; +} + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_elbc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/drivers/mtd/nand/raw/fsl_elbc_spl.c b/drivers/mtd/nand/raw/fsl_elbc_spl.c new file mode 100644 index 0000000000..30c3308940 --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_elbc_spl.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NAND boot for Freescale Enhanced Local Bus Controller, Flash Control Machine + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * Copyright (c) 2008 Freescale Semiconductor, Inc. + * Author: Scott Wood + */ + +#include +#include +#include +#include + +#define WINDOW_SIZE 8192 + +static void nand_wait(void) +{ + fsl_lbc_t *regs = LBC_BASE_ADDR; + + for (;;) { + uint32_t status = in_be32(®s->ltesr); + + if (status == 1) + return; + + if (status & 1) { + puts("read failed (ltesr)\n"); + for (;;); + } + } +} + +#ifdef CONFIG_TPL_BUILD +int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +#else +static int nand_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +#endif +{ + fsl_lbc_t *regs = LBC_BASE_ADDR; + uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; + const int large = CONFIG_SYS_NAND_OR_PRELIM & OR_FCM_PGS; + const int block_shift = large ? 17 : 14; + const int block_size = 1 << block_shift; + const int page_size = large ? 2048 : 512; + const int bad_marker = large ? page_size + 0 : page_size + 5; + int fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT) | 2; + int pos = 0; + char *dst = vdst; + + if (offs & (block_size - 1)) { + puts("bad offset\n"); + for (;;); + } + + if (large) { + fmr |= FMR_ECCM; + out_be32(®s->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + out_be32(®s->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + } else { + out_be32(®s->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + out_be32(®s->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + } + + out_be32(®s->fbcr, 0); + clrsetbits_be32(®s->bank[0].br, BR_DECC, BR_DECC_CHK_GEN); + + while (pos < uboot_size) { + int i = 0; + out_be32(®s->fbar, offs >> block_shift); + + do { + int j; + unsigned int page_offs = (offs & (block_size - 1)) << 1; + + out_be32(®s->ltesr, ~0); + out_be32(®s->lteatr, 0); + out_be32(®s->fpar, page_offs); + out_be32(®s->fmr, fmr); + out_be32(®s->lsor, 0); + nand_wait(); + + page_offs %= WINDOW_SIZE; + + /* + * If either of the first two pages are marked bad, + * continue to the next block. + */ + if (i++ < 2 && buf[page_offs + bad_marker] != 0xff) { + puts("skipping\n"); + offs = (offs + block_size) & ~(block_size - 1); + pos &= ~(block_size - 1); + break; + } + + for (j = 0; j < page_size; j++) + dst[pos + j] = buf[page_offs + j]; + + pos += page_size; + offs += page_size; + } while ((offs & (block_size - 1)) && (pos < uboot_size)); + } + + return 0; +} + +/* + * Defines a static function nand_load_image() here, because non-static makes + * the code too large for certain SPLs(minimal SPL, maximum size <= 4Kbytes) + */ +#ifndef CONFIG_TPL_BUILD +#define nand_spl_load_image(offs, uboot_size, vdst) \ + nand_load_image(offs, uboot_size, vdst) +#endif + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + +#ifdef CONFIG_SPL_FLUSH_IMAGE + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif + + puts("transfering control\n"); + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +} diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c new file mode 100644 index 0000000000..29f30d8ccc --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -0,0 +1,1064 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Integrated Flash Controller NAND Machine Driver + * + * Copyright (c) 2012 Freescale Semiconductor, Inc + * + * Authors: Dipen Dudhat + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifndef CONFIG_SYS_FSL_IFC_BANK_COUNT +#define CONFIG_SYS_FSL_IFC_BANK_COUNT 4 +#endif + +#define MAX_BANKS CONFIG_SYS_FSL_IFC_BANK_COUNT +#define ERR_BYTE 0xFF /* Value returned for read bytes + when read failed */ + +struct fsl_ifc_ctrl; + +/* mtd information per set */ +struct fsl_ifc_mtd { + struct nand_chip chip; + struct fsl_ifc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ + u8 __iomem *vbase; /* Chip select base virtual address */ +}; + +/* overview of the fsl ifc controller */ +struct fsl_ifc_ctrl { + struct nand_hw_control controller; + struct fsl_ifc_mtd *chips[MAX_BANKS]; + + /* device info */ + struct fsl_ifc regs; + void __iomem *addr; /* Address of assigned IFC buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from NEESR after last op */ + unsigned int oob; /* Non zero if operating on OOB data */ + unsigned int eccread; /* Non zero for a full-page ECC read */ +}; + +static struct fsl_ifc_ctrl *ifc_ctrl; + +/* 512-byte page with 4-bit ECC, 8-bit */ +static struct nand_ecclayout oob_512_8bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {0, 5}, {6, 2} }, +}; + +/* 512-byte page with 4-bit ECC, 16-bit */ +static struct nand_ecclayout oob_512_16bit_ecc4 = { + .eccbytes = 8, + .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 6}, }, +}; + +/* 2048-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_2048_ecc4 = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobfree = { {2, 6}, {40, 24} }, +}; + +/* 4096-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_4096_ecc4 = { + .eccbytes = 64, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + }, + .oobfree = { {2, 6}, {72, 56} }, +}; + +/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_4096_ecc8 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 82} }, +}; + +/* 8192-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_8192_ecc4 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 208} }, +}; + +/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_8192_ecc8 = { + .eccbytes = 256, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, + }, + .oobfree = { {2, 6}, {264, 80} }, +}; + +/* + * Generic flash bbt descriptors + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 2, /* 0 on 8-bit small page */ + .len = 4, + .veroffs = 6, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* + * Set up the IFC hardware block and page address fields, and the ifc nand + * structure addr field to point to the correct IFC buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; + int buf_num; + + ctrl->page = page_addr; + + /* Program ROW0/COL0 */ + ifc_out32(&ifc->ifc_nand.row0, page_addr); + ifc_out32(&ifc->ifc_nand.col0, (oob ? IFC_NAND_COL_MS : 0) | column); + + buf_num = page_addr & priv->bufnum_mask; + + ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += mtd->writesize; +} + +/* returns nonzero if entire page is blank */ +static int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, + u32 eccstat, unsigned int bufnum) +{ + return (eccstat >> ((3 - bufnum % 4) * 8)) & 15; +} + +/* + * execute IFC NAND command and wait for it to complete + */ +static int fsl_ifc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; + u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; + u32 time_start; + u32 eccstat; + int i; + + /* set the chip select for NAND Transaction */ + ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); + + /* start read/write seq */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, + IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for NAND Machine complete flag or timeout */ + time_start = get_timer(0); + + while (get_timer(time_start) < timeo) { + ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (ctrl->status & IFC_NAND_EVTER_STAT_OPC) + break; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, ctrl->status); + + if (ctrl->status & IFC_NAND_EVTER_STAT_FTOER) + printf("%s: Flash Time Out Error\n", __func__); + if (ctrl->status & IFC_NAND_EVTER_STAT_WPER) + printf("%s: Write Protect Error\n", __func__); + + if (ctrl->eccread) { + int errors; + int bufnum = ctrl->page & priv->bufnum_mask; + int sector_start = bufnum * chip->ecc.steps; + int sector_end = sector_start + chip->ecc.steps - 1; + u32 *eccstat_regs; + + eccstat_regs = ifc->ifc_nand.nand_eccstat; + eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); + + for (i = sector_start; i <= sector_end; i++) { + if ((i != sector_start) && !(i % 4)) + eccstat = ifc_in32(&eccstat_regs[i / 4]); + + errors = check_read_ecc(mtd, ctrl, eccstat, i); + + if (errors == 15) { + /* + * Uncorrectable error. + * We'll check for blank pages later. + * + * We disable ECCER reporting due to erratum + * IFC-A002770 -- so report it now if we + * see an uncorrectable error in ECCSTAT. + */ + ctrl->status |= IFC_NAND_EVTER_STAT_ECCER; + continue; + } + + mtd->ecc_stats.corrected += errors; + } + + ctrl->eccread = 0; + } + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == IFC_NAND_EVTER_STAT_OPC ? 0 : -EIO; +} + +static void fsl_ifc_do_read(struct nand_chip *chip, + int oob, + struct mtd_info *mtd) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; + + /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ + if (mtd->writesize > 512) { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT)); + + if (oob) + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT); + else + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the IFC NAND Machine */ +static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ0: { + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + if (chip->ecc.mode == NAND_ECC_HW) + ctrl->eccread = 1; + + fsl_ifc_do_read(chip, 0, mtd); + fsl_ifc_run_command(mtd); + return; + } + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + ifc_out32(&ifc->ifc_nand.nand_fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_ifc_do_read(chip, 1, mtd); + fsl_ifc_run_command(mtd); + + return; + + /* READID must read all possible bytes while CEB is active */ + case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (timing << IFC_NAND_FIR0_OP2_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + command << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.row3, column); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + ifc_out32(&ifc->ifc_nand.nand_fbcr, 256); + ctrl->read_bytes = 256; + + set_addr(mtd, 0, 0, 0); + fsl_ifc_run_command(mtd); + return; + } + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + ctrl->read_bytes = 0; + fsl_ifc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + u32 nand_fcr0; + ctrl->column = column; + ctrl->oob = 0; + + if (mtd->writesize > 512) { + nand_fcr0 = + (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << + IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT)); + } else { + nand_fcr0 = ((NAND_CMD_PAGEPROG << + IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_SEQIN << + IFC_NAND_FCR0_CMD2_SHIFT) | + (NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD3_SHIFT)); + + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT)); + + if (column >= mtd->writesize) + nand_fcr0 |= + NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; + else + nand_fcr0 |= + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; + } + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + ctrl->oob = 1; + } + ifc_out32(&ifc->ifc_nand.nand_fcr0, nand_fcr0); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: + if (ctrl->oob) + ifc_out32(&ifc->ifc_nand.nand_fbcr, + ctrl->index - ctrl->column); + else + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + + fsl_ifc_run_command(mtd); + return; + + case NAND_CMD_STATUS: + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + /* + * The chip always seems to report that it is + * write-protected, even when it is not. + */ + if (chip->options & NAND_BUSWIDTH_16) + ifc_out16(ctrl->addr, + ifc_in16(ctrl->addr) | NAND_STATUS_WP); + else + out_8(ctrl->addr, in_8(ctrl->addr) | NAND_STATUS_WP); + return; + + case NAND_CMD_RESET: + ifc_out32(&ifc->ifc_nand.nand_fir0, + IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT); + fsl_ifc_run_command(mtd); + return; + + default: + printf("%s: error, unsupported command 0x%x.\n", + __func__, command); + } +} + +/* + * Write buf to the IFC NAND Controller Data Buffer + */ +static void fsl_ifc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len <= 0) { + printf("%s of %d bytes", __func__, len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + printf("%s beyond end of buffer " + "(%d requested, %u available)\n", + __func__, len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(ctrl->addr + ctrl->index, buf, len); + ctrl->index += len; +} + +/* + * read a byte from either the IFC hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_ifc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + unsigned int offset; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ctrl->index < ctrl->read_bytes) { + offset = ctrl->index++; + return in_8(ctrl->addr + offset); + } + + printf("%s beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read two bytes from the IFC hardware buffer + * read function for 16-bit buswith + */ +static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + uint16_t data; + + /* + * If there are still bytes in the IFC buffer, then use the + * next byte. + */ + if (ctrl->index < ctrl->read_bytes) { + data = ifc_in16(ctrl->addr + ctrl->index); + ctrl->index += 2; + return (uint8_t)data; + } + + printf("%s beyond end of buffer\n", __func__); + return ERR_BYTE; +} + +/* + * Read from the IFC Controller Data Buffer + */ +static void fsl_ifc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, ctrl->addr + ctrl->index, avail); + ctrl->index += avail; + + if (len > avail) + printf("%s beyond end of buffer " + "(%d requested, %d available)\n", + __func__, len, avail); +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_runtime *ifc = ctrl->regs.rregs; + u32 nand_fsr; + int status; + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.nand_fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_ifc_run_command(mtd); + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + return NAND_STATUS_FAIL; + + nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); + status = nand_fsr >> 24; + + /* Chip sometimes reporting write protect even when it's not */ + return status | NAND_STATUS_WP; +} + +/* + * The controller does not check for bitflips in erased pages, + * therefore software must check instead. + */ +static int +check_erased_page(struct nand_chip *chip, u8 *buf, struct mtd_info *mtd) +{ + u8 *ecc = chip->oob_poi; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int i, res, bitflips; + + /* IFC starts ecc bytes at offset 8 in the spare area. */ + ecc += 8; + bitflips = 0; + for (i = 0; i < chip->ecc.steps; i++) { + res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, + NULL, 0, chip->ecc.strength); + + if (res < 0) { + printf("fsl-ifc: NAND Flash ECC Uncorrectable Error\n"); + mtd->ecc_stats.failed++; + } else if (res > 0) { + mtd->ecc_stats.corrected += res; + } + bitflips = max(res, bitflips); + buf += pkt_size; + ecc += ecc_size; + } + + return bitflips; +} + +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + + fsl_ifc_read_buf(mtd, buf, mtd->writesize); + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (ctrl->status & IFC_NAND_EVTER_STAT_ECCER) + return check_erased_page(chip, buf, mtd); + + if (ctrl->status != IFC_NAND_EVTER_STAT_OPC) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + fsl_ifc_write_buf(mtd, buf, mtd->writesize); + fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static void fsl_ifc_ctrl_init(void) +{ + uint32_t ver = 0; + ifc_ctrl = kzalloc(sizeof(*ifc_ctrl), GFP_KERNEL); + if (!ifc_ctrl) + return; + + ifc_ctrl->regs.gregs = IFC_FCM_BASE_ADDR; + + ver = ifc_in32(&ifc_ctrl->regs.gregs->ifc_rev); + if (ver >= FSL_IFC_V2_0_0) + ifc_ctrl->regs.rregs = + (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET; + else + ifc_ctrl->regs.rregs = + (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET; + + /* clear event registers */ + ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_stat, ~0U); + ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.pgrdcmpl_evt_stat, ~0U); + + /* Enable error and event for any detected errors */ + ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.nand_evter_en, + IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_PGRDCMPL_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN); + + ifc_out32(&ifc_ctrl->regs.rregs->ifc_nand.ncfgr, 0x0); +} + +static void fsl_ifc_select_chip(struct mtd_info *mtd, int chip) +{ +} + +static int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv, uint32_t ver) +{ + struct fsl_ifc_runtime *ifc = ifc_ctrl->regs.rregs; + uint32_t cs = 0, csor = 0, csor_8k = 0, csor_ext = 0; + uint32_t ncfgr = 0; + u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; + u32 time_start; + + if (ver > FSL_IFC_V1_1_0) { + ncfgr = ifc_in32(&ifc->ifc_nand.ncfgr); + ifc_out32(&ifc->ifc_nand.ncfgr, ncfgr | IFC_NAND_SRAM_INIT_EN); + + /* wait for SRAM_INIT bit to be clear or timeout */ + time_start = get_timer(0); + while (get_timer(time_start) < timeo) { + ifc_ctrl->status = + ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (!(ifc_ctrl->status & IFC_NAND_SRAM_INIT_EN)) + return 0; + } + printf("fsl-ifc: Failed to Initialise SRAM\n"); + return 1; + } + + cs = priv->bank; + + /* Save CSOR and CSOR_ext */ + csor = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext); + + /* chage PageSize 8K and SpareSize 1K*/ + csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; + ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor_8k); + ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, 0x0000400); + + /* READID */ + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); + ifc_out32(&ifc->ifc_nand.row3, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0x0); + + /* Program ROW0/COL0 */ + ifc_out32(&ifc->ifc_nand.row0, 0x0); + ifc_out32(&ifc->ifc_nand.col0, 0x0); + + /* set the chip select for NAND Transaction */ + ifc_out32(&ifc->ifc_nand.nand_csel, priv->bank << IFC_NAND_CSEL_SHIFT); + + /* start read seq */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, IFC_NAND_SEQ_STRT_FIR_STRT); + + time_start = get_timer(0); + + while (get_timer(time_start) < timeo) { + ifc_ctrl->status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + + if (ifc_ctrl->status & IFC_NAND_EVTER_STAT_OPC) + break; + } + + if (ifc_ctrl->status != IFC_NAND_EVTER_STAT_OPC) { + printf("fsl-ifc: Failed to Initialise SRAM\n"); + return 1; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, ifc_ctrl->status); + + /* Restore CSOR and CSOR_ext */ + ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor, csor); + ifc_out32(&ifc_ctrl->regs.gregs->csor_cs[cs].csor_ext, csor_ext); + + return 0; +} + +static int fsl_ifc_chip_init(int devnum, u8 *addr) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + struct fsl_ifc_mtd *priv; + struct nand_ecclayout *layout; + struct fsl_ifc_fcm *gregs = NULL; + uint32_t cspr = 0, csor = 0, ver = 0; + int ret = 0; + + if (!ifc_ctrl) { + fsl_ifc_ctrl_init(); + if (!ifc_ctrl) + return -1; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ctrl = ifc_ctrl; + priv->vbase = addr; + gregs = ifc_ctrl->regs.gregs; + + /* Find which chip select it is connected to. + */ + for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { + phys_addr_t phys_addr = virt_to_phys(addr); + + cspr = ifc_in32(&gregs->cspr_cs[priv->bank].cspr); + csor = ifc_in32(&gregs->csor_cs[priv->bank].csor); + + if ((cspr & CSPR_V) && (cspr & CSPR_MSEL) == CSPR_MSEL_NAND && + (cspr & CSPR_BA) == CSPR_PHYS_ADDR(phys_addr)) + break; + } + + if (priv->bank >= MAX_BANKS) { + printf("%s: address did not match any " + "chip selects\n", __func__); + kfree(priv); + return -ENODEV; + } + + nand = &priv->chip; + mtd = nand_to_mtd(nand); + + ifc_ctrl->chips[priv->bank] = priv; + + /* fill in nand_chip structure */ + /* set up function call table */ + + nand->write_buf = fsl_ifc_write_buf; + nand->read_buf = fsl_ifc_read_buf; + nand->select_chip = fsl_ifc_select_chip; + nand->cmdfunc = fsl_ifc_cmdfunc; + nand->waitfunc = fsl_ifc_wait; + + /* set up nand options */ + nand->bbt_td = &bbt_main_descr; + nand->bbt_md = &bbt_mirror_descr; + + /* set up nand options */ + nand->options = NAND_NO_SUBPAGE_WRITE; + nand->bbt_options = NAND_BBT_USE_FLASH; + + if (cspr & CSPR_PORT_SIZE_16) { + nand->read_byte = fsl_ifc_read_byte16; + nand->options |= NAND_BUSWIDTH_16; + } else { + nand->read_byte = fsl_ifc_read_byte; + } + + nand->controller = &ifc_ctrl->controller; + nand_set_controller_data(nand, priv); + + nand->ecc.read_page = fsl_ifc_read_page; + nand->ecc.write_page = fsl_ifc_write_page; + + /* Hardware generates ECC per 512 Bytes */ + nand->ecc.size = 512; + nand->ecc.bytes = 8; + + switch (csor & CSOR_NAND_PGS_MASK) { + case CSOR_NAND_PGS_512: + if (nand->options & NAND_BUSWIDTH_16) { + layout = &oob_512_16bit_ecc4; + } else { + layout = &oob_512_8bit_ecc4; + + /* Avoid conflict with bad block marker */ + bbt_main_descr.offs = 0; + bbt_mirror_descr.offs = 0; + } + + nand->ecc.strength = 4; + priv->bufnum_mask = 15; + break; + + case CSOR_NAND_PGS_2K: + layout = &oob_2048_ecc4; + nand->ecc.strength = 4; + priv->bufnum_mask = 3; + break; + + case CSOR_NAND_PGS_4K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_4096_ecc4; + nand->ecc.strength = 4; + } else { + layout = &oob_4096_ecc8; + nand->ecc.strength = 8; + nand->ecc.bytes = 16; + } + + priv->bufnum_mask = 1; + break; + + case CSOR_NAND_PGS_8K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_8192_ecc4; + nand->ecc.strength = 4; + } else { + layout = &oob_8192_ecc8; + nand->ecc.strength = 8; + nand->ecc.bytes = 16; + } + + priv->bufnum_mask = 0; + break; + + + default: + printf("ifc nand: bad csor %#x: bad page size\n", csor); + return -ENODEV; + } + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = layout; + } else { + nand->ecc.mode = NAND_ECC_SOFT; + } + + ver = ifc_in32(&gregs->ifc_rev); + if (ver >= FSL_IFC_V1_1_0) + ret = fsl_ifc_sram_init(priv, ver); + if (ret) + return ret; + + if (ver >= FSL_IFC_V2_0_0) + priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum, mtd); + if (ret) + return ret; + return 0; +} + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_ifc_chip_init(i, (u8 *)base_address[i]); +} diff --git a/drivers/mtd/nand/raw/fsl_ifc_spl.c b/drivers/mtd/nand/raw/fsl_ifc_spl.c new file mode 100644 index 0000000000..7137eb4108 --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_ifc_spl.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NAND boot for Freescale Integrated Flash Controller, NAND FCM + * + * Copyright 2011 Freescale Semiconductor, Inc. + * Author: Dipen Dudhat + */ + +#include +#include +#include +#include +#ifdef CONFIG_CHAIN_OF_TRUST +#include +#endif + +static inline int is_blank(uchar *addr, int page_size) +{ + int i; + + for (i = 0; i < page_size; i++) { + if (__raw_readb(&addr[i]) != 0xff) + return 0; + } + + /* + * For the SPL, don't worry about uncorrectable errors + * where the main area is all FFs but shouldn't be. + */ + return 1; +} + +/* returns nonzero if entire page is blank */ +static inline int check_read_ecc(uchar *buf, u32 *eccstat, + unsigned int bufnum, int page_size) +{ + u32 reg = eccstat[bufnum / 4]; + int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; + + if (errors == 0xf) { /* uncorrectable */ + /* Blank pages fail hw ECC checks */ + if (is_blank(buf, page_size)) + return 1; + + puts("ecc error\n"); + for (;;) + ; + } + + return 0; +} + +static inline struct fsl_ifc_runtime *runtime_regs_address(void) +{ + struct fsl_ifc regs = {(void *)CONFIG_SYS_IFC_ADDR, NULL}; + int ver = 0; + + ver = ifc_in32(®s.gregs->ifc_rev); + if (ver >= FSL_IFC_V2_0_0) + regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET; + else + regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET; + + return regs.rregs; +} + +static inline void nand_wait(uchar *buf, int bufnum, int page_size) +{ + struct fsl_ifc_runtime *ifc = runtime_regs_address(); + u32 status; + u32 eccstat[8]; + int bufperpage = page_size / 512; + int bufnum_end, i; + + bufnum *= bufperpage; + bufnum_end = bufnum + bufperpage - 1; + + do { + status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); + } while (!(status & IFC_NAND_EVTER_STAT_OPC)); + + if (status & IFC_NAND_EVTER_STAT_FTOER) { + puts("flash time out error\n"); + for (;;) + ; + } + + for (i = bufnum / 4; i <= bufnum_end / 4; i++) + eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); + + for (i = bufnum; i <= bufnum_end; i++) { + if (check_read_ecc(buf, eccstat, i, page_size)) + break; + } + + ifc_out32(&ifc->ifc_nand.nand_evter_stat, status); +} + +static inline int bad_block(uchar *marker, int port_size) +{ + if (port_size == 8) + return __raw_readb(marker) != 0xff; + else + return __raw_readw((u16 *)marker) != 0xffff; +} + +int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) +{ + struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR; + struct fsl_ifc_runtime *ifc = NULL; + uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; + int page_size; + int port_size; + int pages_per_blk; + int blk_size; + int bad_marker = 0; + int bufnum_mask, bufnum, ver = 0; + + int csor, cspr; + int pos = 0; + int j = 0; + + int sram_addr; + int pg_no; + uchar *dst = vdst; + + ifc = runtime_regs_address(); + + /* Get NAND Flash configuration */ + csor = CONFIG_SYS_NAND_CSOR; + cspr = CONFIG_SYS_NAND_CSPR; + + port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; + + if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) { + page_size = 8192; + bufnum_mask = 0x0; + } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) { + page_size = 4096; + bufnum_mask = 0x1; + } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) { + page_size = 2048; + bufnum_mask = 0x3; + } else { + page_size = 512; + bufnum_mask = 0xf; + + if (port_size == 8) + bad_marker = 5; + } + + ver = ifc_in32(&gregs->ifc_rev); + if (ver >= FSL_IFC_V2_0_0) + bufnum_mask = (bufnum_mask * 2) + 1; + + pages_per_blk = + 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); + + blk_size = pages_per_blk * page_size; + + /* Open Full SRAM mapping for spare are access */ + ifc_out32(&ifc->ifc_nand.ncfgr, 0x0); + + /* Clear Boot events */ + ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); + + /* Program FIR/FCR for Large/Small page */ + if (page_size > 512) { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); + } else { + ifc_out32(&ifc->ifc_nand.nand_fir0, + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); + ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); + + ifc_out32(&ifc->ifc_nand.nand_fcr0, + NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); + } + + /* Program FBCR = 0 for full page read */ + ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); + + /* Read and copy u-boot on SDRAM from NAND device, In parallel + * check for Bad block if found skip it and read continue to + * next Block + */ + while (pos < uboot_size) { + int i = 0; + do { + pg_no = offs / page_size; + bufnum = pg_no & bufnum_mask; + sram_addr = bufnum * page_size * 2; + + ifc_out32(&ifc->ifc_nand.row0, pg_no); + ifc_out32(&ifc->ifc_nand.col0, 0); + /* start read */ + ifc_out32(&ifc->ifc_nand.nandseq_strt, + IFC_NAND_SEQ_STRT_FIR_STRT); + + /* wait for read to complete */ + nand_wait(&buf[sram_addr], bufnum, page_size); + + /* + * If either of the first two pages are marked bad, + * continue to the next block. + */ + if (i++ < 2 && + bad_block(&buf[sram_addr + page_size + bad_marker], + port_size)) { + puts("skipping\n"); + offs = (offs + blk_size) & ~(blk_size - 1); + pos &= ~(blk_size - 1); + break; + } + + for (j = 0; j < page_size; j++) + dst[pos + j] = __raw_readb(&buf[sram_addr + j]); + + pos += page_size; + offs += page_size; + } while ((offs & (blk_size - 1)) && (pos < uboot_size)); + } + + return 0; +} + +/* + * Main entrypoint for NAND Boot. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + /* + * Jump to U-Boot image + */ +#ifdef CONFIG_SPL_FLUSH_IMAGE + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); +#endif + +#ifdef CONFIG_CHAIN_OF_TRUST + /* + * U-Boot header is appended at end of U-boot image, so + * calculate U-boot header address using U-boot header size. + */ +#define CONFIG_U_BOOT_HDR_ADDR \ + ((CONFIG_SYS_NAND_U_BOOT_START + \ + CONFIG_SYS_NAND_U_BOOT_SIZE) - \ + CONFIG_U_BOOT_HDR_SIZE) + spl_validate_uboot(CONFIG_U_BOOT_HDR_ADDR, + CONFIG_SYS_NAND_U_BOOT_START); + /* + * In case of failure in validation, spl_validate_uboot would + * not return back in case of Production environment with ITS=1. + * Thus U-Boot will not start. + * In Development environment (ITS=0 and SB_EN=1), the function + * may return back in case of non-fatal failures. + */ +#endif + + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); +} + +#ifndef CONFIG_SPL_NAND_INIT +void nand_init(void) +{ +} + +void nand_deselect(void) +{ +} +#endif diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c new file mode 100644 index 0000000000..dfbdbca3ae --- /dev/null +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * FSL UPM NAND driver + * + * Copyright (C) 2007 MontaVista Software, Inc. + * Anton Vorontsov + */ + +#include +#include +#include +#include +#include +#include +#include + +static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) +{ + clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); + (void)in_be32(upm->mxmr); +} + +static void fsl_upm_end_pattern(struct fsl_upm *upm) +{ + clrbits_be32(upm->mxmr, MxMR_OP_RUNP); + + while (in_be32(upm->mxmr) & MxMR_OP_RUNP) + eieio(); +} + +static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, + void __iomem *io_addr, u32 mar) +{ + out_be32(upm->mar, mar); + (void)in_be32(upm->mar); + switch (width) { + case 8: + out_8(io_addr, 0x0); + break; + case 16: + out_be16(io_addr, 0x0); + break; + case 32: + out_be32(io_addr, 0x0); + break; + } +} + +static void fun_wait(struct fsl_upm_nand *fun) +{ + if (fun->dev_ready) { + while (!fun->dev_ready(fun->chip_nr)) + debug("unexpected busy state\n"); + } else { + /* + * If the R/B pin is not connected, + * a short delay is necessary. + */ + udelay(1); + } +} + +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 +static void fun_select_chip(struct mtd_info *mtd, int chip_nr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = nand_get_controller_data(chip); + + if (chip_nr >= 0) { + fun->chip_nr = chip_nr; + chip->IO_ADDR_R = chip->IO_ADDR_W = + fun->upm.io_addr + fun->chip_offset * chip_nr; + } else if (chip_nr == -1) { + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + } +} +#endif + +static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = nand_get_controller_data(chip); + void __iomem *io_addr; + u32 mar; + + if (!(ctrl & fun->last_ctrl)) { + fsl_upm_end_pattern(&fun->upm); + + if (cmd == NAND_CMD_NONE) + return; + + fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); + } + + if (ctrl & NAND_CTRL_CHANGE) { + if (ctrl & NAND_ALE) + fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); + else if (ctrl & NAND_CLE) + fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); + } + + mar = cmd << (32 - fun->width); + io_addr = fun->upm.io_addr; +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 + if (fun->chip_nr > 0) { + io_addr += fun->chip_offset * fun->chip_nr; + if (fun->upm_mar_chip_offset) + mar |= fun->upm_mar_chip_offset * fun->chip_nr; + } +#endif + fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar); + + /* + * Some boards/chips needs this. At least the MPC8360E-RDK + * needs it. Probably weird chip, because I don't see any + * need for this on MPC8555E + Samsung K9F1G08U0A. Usually + * here are 0-2 unexpected busy states per block read. + */ + if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) + fun_wait(fun); +} + +static u8 upm_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + return in_8(chip->IO_ADDR_R); +} + +static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = nand_get_controller_data(chip); + + for (i = 0; i < len; i++) { + out_8(chip->IO_ADDR_W, buf[i]); + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) + fun_wait(fun); + } + + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) + fun_wait(fun); +} + +static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + + for (i = 0; i < len; i++) + buf[i] = in_8(chip->IO_ADDR_R); +} + +static int nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_upm_nand *fun = nand_get_controller_data(chip); + + return fun->dev_ready(fun->chip_nr); +} + +int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) +{ + if (fun->width != 8 && fun->width != 16 && fun->width != 32) + return -ENOSYS; + + fun->last_ctrl = NAND_CLE; + + nand_set_controller_data(chip, fun); + chip->chip_delay = fun->chip_delay; + chip->ecc.mode = NAND_ECC_SOFT; + chip->cmd_ctrl = fun_cmd_ctrl; +#if CONFIG_SYS_NAND_MAX_CHIPS > 1 + chip->select_chip = fun_select_chip; +#endif + chip->read_byte = upm_nand_read_byte; + chip->read_buf = upm_nand_read_buf; + chip->write_buf = upm_nand_write_buf; + if (fun->dev_ready) + chip->dev_ready = nand_dev_ready; + + return 0; +} diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c new file mode 100644 index 0000000000..1f4c74f0f6 --- /dev/null +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * (C) Copyright 2012 + * Amit Virdi, ST Microelectronics, amit.virdi@st.com. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 fsmc_version; +static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) + CONFIG_SYS_FSMC_BASE; + +/* + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of + * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can + * correct 1 bit in 512 bytes + */ + +static struct nand_ecclayout fsmc_ecc4_lp_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC placement definitions in oobfree type format + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data + * Managing the ecc bytes in the following way makes it easier for software to + * read ecc bytes consecutive to data bytes. This way is similar to + * oobfree structure maintained already in u-boot nand driver + */ +static struct fsmc_eccplace fsmc_eccpl_lp = { + .eccplace = { + {.offset = 2, .length = 13}, + {.offset = 18, .length = 13}, + {.offset = 34, .length = 13}, + {.offset = 50, .length = 13}, + {.offset = 66, .length = 13}, + {.offset = 82, .length = 13}, + {.offset = 98, .length = 13}, + {.offset = 114, .length = 13} + } +}; + +static struct nand_ecclayout fsmc_ecc4_sp_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +static struct fsmc_eccplace fsmc_eccpl_sp = { + .eccplace = { + {.offset = 0, .length = 4}, + {.offset = 6, .length = 9} + } +}; + +static struct nand_ecclayout fsmc_ecc1_layout = { + .eccbytes = 24, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, + 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + {.offset = 72, .length = 8}, + {.offset = 88, .length = 8}, + {.offset = 104, .length = 8}, + {.offset = 120, .length = 8} + } +}; + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + ulong IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (ulong)this->IO_ADDR_W; + + IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + + if (ctrl & NAND_NCE) { + writel(readl(&fsmc_regs_p->pc) | + FSMC_ENABLE, &fsmc_regs_p->pc); + } else { + writel(readl(&fsmc_regs_p->pc) & + ~FSMC_ENABLE, &fsmc_regs_p->pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + /* The calculated ecc is actually the correction index in data */ + u32 err_idx[8]; + u32 num_err, i; + u32 ecc1, ecc2, ecc3, ecc4; + + num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; + + if (likely(num_err == 0)) + return 0; + + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, 13, 8); + int bits_data = count_written_bits(dat, 512, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, 512); + return bits_data + bits_ecc; + } + + return -EBADMSG; + } + + ecc1 = readl(&fsmc_regs_p->ecc1); + ecc2 = readl(&fsmc_regs_p->ecc2); + ecc3 = readl(&fsmc_regs_p->ecc3); + ecc4 = readl(&fsmc_regs_p->sts); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + + i = 0; + while (i < num_err) { + err_idx[i] ^= 3; + + if (err_idx[i] < 512 * 8) + __change_bit(err_idx[i], dat); + + i++; + } + + return num_err; +} + +static int fsmc_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char *ecc) +{ + u_int ecc_tmp; + int timeout = CONFIG_SYS_HZ; + ulong start; + + switch (fsmc_version) { + case FSMC_VER8: + start = get_timer(0); + while (get_timer(start) < timeout) { + /* + * Busy waiting for ecc computation + * to finish for 512 bytes + */ + if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) + break; + } + + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + ecc[3] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc2); + ecc[4] = (u_char) (ecc_tmp >> 0); + ecc[5] = (u_char) (ecc_tmp >> 8); + ecc[6] = (u_char) (ecc_tmp >> 16); + ecc[7] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc3); + ecc[8] = (u_char) (ecc_tmp >> 0); + ecc[9] = (u_char) (ecc_tmp >> 8); + ecc[10] = (u_char) (ecc_tmp >> 16); + ecc[11] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->sts); + ecc[12] = (u_char) (ecc_tmp >> 16); + break; + + default: + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + break; + } + + return 0; +} + +void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, + &fsmc_regs_p->pc); +} + +/* + * fsmc_read_page_hwecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * @page: page number to read + * + * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(upto a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + struct fsmc_eccplace *fsmc_eccpl; + int i, j, s, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + int off, len, group = 0; + uint8_t oob[13] __attribute__ ((aligned (2))); + + /* Differentiate between small and large page ecc place definitions */ + if (mtd->writesize == 512) + fsmc_eccpl = &fsmc_eccpl_sp; + else + fsmc_eccpl = &fsmc_eccpl_lp; + + for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + for (j = 0; j < eccbytes;) { + off = fsmc_eccpl->eccplace[group].offset; + len = fsmc_eccpl->eccplace[group].length; + group++; + + /* + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); + chip->read_buf(mtd, oob + j, len); + j += len; + } + + memcpy(&ecc_code[i], oob, 13); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + return 0; +} + +#ifndef CONFIG_SPL_BUILD +/* + * fsmc_nand_switch_ecc - switch the ECC operation between different engines + * + * @eccstrength - the number of bits that could be corrected + * (1 - HW, 4 - SW BCH4) + */ +int fsmc_nand_switch_ecc(uint32_t eccstrength) +{ + struct nand_chip *nand; + struct mtd_info *mtd; + int err; + + /* + * This functions is only called on SPEAr600 platforms, supporting + * 1 bit HW ECC. The BCH8 HW ECC (FSMC_VER8) from the ST-Ericsson + * Nomadik SoC is currently supporting this fsmc_nand_switch_ecc() + * function, as it doesn't need to switch to a different ECC layout. + */ + mtd = get_nand_dev_by_index(nand_curr_device); + nand = mtd_to_nand(mtd); + + /* Setup the ecc configurations again */ + if (eccstrength == 1) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->ecc.layout = &fsmc_ecc1_layout; + nand->ecc.calculate = fsmc_read_hwecc; + nand->ecc.correct = nand_correct_data; + } else if (eccstrength == 4) { + /* + * .calculate .correct and .bytes will be set in + * nand_scan_tail() + */ + nand->ecc.mode = NAND_ECC_SOFT_BCH; + nand->ecc.strength = 4; + nand->ecc.layout = NULL; + } else { + printf("Error: ECC strength %d not supported!\n", eccstrength); + } + + /* Update NAND handling after ECC mode switch */ + err = nand_scan_tail(mtd); + + return err; +} +#endif /* CONFIG_SPL_BUILD */ + +int fsmc_nand_init(struct nand_chip *nand) +{ + static int chip_nr; + struct mtd_info *mtd; + u32 peripid2 = readl(&fsmc_regs_p->peripid2); + + fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & + FSMC_REVISION_MSK; + + writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); + +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#else +#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT +#endif + writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->attrib); + + nand->options = 0; +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + nand->options |= NAND_BUSWIDTH_16; +#endif + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.calculate = fsmc_read_hwecc; + nand->ecc.hwctl = fsmc_enable_hwecc; + nand->cmd_ctrl = fsmc_nand_hwcontrol; + nand->IO_ADDR_R = nand->IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->badblockbits = 7; + + mtd = nand_to_mtd(nand); + + switch (fsmc_version) { + case FSMC_VER8: + nand->ecc.bytes = 13; + nand->ecc.strength = 8; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.read_page = fsmc_read_page_hwecc; + if (mtd->writesize == 512) + nand->ecc.layout = &fsmc_ecc4_sp_layout; + else { + if (mtd->oobsize == 224) + nand->ecc.layout = &fsmc_ecc4_224_layout; + else + nand->ecc.layout = &fsmc_ecc4_lp_layout; + } + + break; + default: + nand->ecc.bytes = 3; + nand->ecc.strength = 1; + nand->ecc.layout = &fsmc_ecc1_layout; + nand->ecc.correct = nand_correct_data; + break; + } + + /* Detect NAND chips */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + if (nand_register(chip_nr++, mtd)) + return -ENXIO; + + return 0; +} diff --git a/drivers/mtd/nand/raw/kb9202_nand.c b/drivers/mtd/nand/raw/kb9202_nand.c new file mode 100644 index 0000000000..0f68f1cd86 --- /dev/null +++ b/drivers/mtd/nand/raw/kb9202_nand.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2006 + * KwikByte + * + * (C) Copyright 2009 + * Matthias Kaehlcke + */ + +#include +#include +#include +#include + +#include + +/* + * hardware specific access to control-lines + */ + +#define MASK_ALE (1 << 22) /* our ALE is A22 */ +#define MASK_CLE (1 << 21) /* our CLE is A21 */ + +#define KB9202_NAND_NCE (1 << 28) /* EN* on D28 */ +#define KB9202_NAND_BUSY (1 << 29) /* RB* on D29 */ + +#define KB9202_SMC2_NWS (1 << 2) +#define KB9202_SMC2_TDF (1 << 8) +#define KB9202_SMC2_RWSETUP (1 << 24) +#define KB9202_SMC2_RWHOLD (1 << 29) + +/* + * Board-specific function to access device control signals + */ +static void kb9202_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + + /* clear ALE and CLE bits */ + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + this->IO_ADDR_W = (void *) IO_ADDR_W; + + if (ctrl & NAND_NCE) + writel(KB9202_NAND_NCE, AT91C_PIOC_CODR); + else + writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + + +/* + * Board-specific function to access the device ready signal. + */ +static int kb9202_nand_ready(struct mtd_info *mtd) +{ + return readl(AT91C_PIOC_PDSR) & KB9202_NAND_BUSY; +} + + +/* + * Board-specific NAND init. Copied from include/linux/mtd/nand.h for reference. + * + * struct nand_chip - NAND Private Flash Chip Data + * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device + * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device + * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line + * If set to NULL no access to ready/busy is available and the ready/busy information + * is read from the chip status register + * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only + * be provided if a hardware ECC is available + * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines + * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) + * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about + * special functionality. See the defines for further explanation +*/ +/* + * This routine initializes controller and GPIOs. + */ +int board_nand_init(struct nand_chip *nand) +{ + unsigned int value; + + nand->ecc.mode = NAND_ECC_SOFT; + nand->cmd_ctrl = kb9202_nand_hwcontrol; + nand->dev_ready = kb9202_nand_ready; + + /* in case running outside of bootloader */ + writel(1 << AT91C_ID_PIOC, AT91C_PMC_PCER); + + /* setup nand flash access (allow ample margin) */ + /* 4 wait states, 1 setup, 1 hold, 1 float for 8-bit device */ + writel(AT91C_SMC2_WSEN | KB9202_SMC2_NWS | KB9202_SMC2_TDF | + AT91C_SMC2_DBW_8 | KB9202_SMC2_RWSETUP | KB9202_SMC2_RWHOLD, + AT91C_SMC_CSR3); + + /* enable internal NAND controller */ + value = readl(AT91C_EBI_CSA); + value |= AT91C_EBI_CS3A_SMC_SmartMedia; + writel(value, AT91C_EBI_CSA); + + /* enable SMOE/SMWE */ + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_ASR); + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_PDR); + writel(AT91C_PC1_BFRDY_SMOE | AT91C_PC3_BFBAA_SMWE, AT91C_PIOC_OER); + + /* set NCE to high */ + writel(KB9202_NAND_NCE, AT91C_PIOC_SODR); + + /* disable output on pin connected to the busy line of the NAND */ + writel(KB9202_NAND_BUSY, AT91C_PIOC_ODR); + + /* enable the PIO to control NCE and BUSY */ + writel(KB9202_NAND_NCE | KB9202_NAND_BUSY, AT91C_PIOC_PER); + + /* enable output for NCE */ + writel(KB9202_NAND_NCE, AT91C_PIOC_OER); + + return (0); +} diff --git a/drivers/mtd/nand/raw/kirkwood_nand.c b/drivers/mtd/nand/raw/kirkwood_nand.c new file mode 100644 index 0000000000..0757fa840b --- /dev/null +++ b/drivers/mtd/nand/raw/kirkwood_nand.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Prafulla Wadaskar + */ + +#include +#include +#include +#include +#include + +/* NAND Flash Soc registers */ +struct kwnandf_registers { + u32 rd_params; /* 0x10418 */ + u32 wr_param; /* 0x1041c */ + u8 pad[0x10470 - 0x1041c - 4]; + u32 ctrl; /* 0x10470 */ +}; + +static struct kwnandf_registers *nf_reg = + (struct kwnandf_registers *)KW_NANDF_BASE; + +static u32 nand_mpp_backup[9] = { 0 }; + +/* + * hardware specific access to control-lines/bits + */ +#define NAND_ACTCEBOOT_BIT 0x02 + +static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nc = mtd_to_nand(mtd); + u32 offs; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + offs = (1 << 0); /* Commands with A[1:0] == 01 */ + else if (ctrl & NAND_ALE) + offs = (1 << 1); /* Addresses with A[1:0] == 10 */ + else + return; + + writeb(cmd, nc->IO_ADDR_W + offs); +} + +void kw_nand_select_chip(struct mtd_info *mtd, int chip) +{ + u32 data; + static const u32 nand_config[] = { + MPP0_NF_IO2, + MPP1_NF_IO3, + MPP2_NF_IO4, + MPP3_NF_IO5, + MPP4_NF_IO6, + MPP5_NF_IO7, + MPP18_NF_IO0, + MPP19_NF_IO1, + 0 + }; + + if (chip >= 0) + kirkwood_mpp_conf(nand_config, nand_mpp_backup); + else + kirkwood_mpp_conf(nand_mpp_backup, NULL); + + data = readl(&nf_reg->ctrl); + data |= NAND_ACTCEBOOT_BIT; + writel(data, &nf_reg->ctrl); +} + +int board_nand_init(struct nand_chip *nand) +{ + nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; +#if defined(CONFIG_SYS_NAND_NO_SUBPAGE_WRITE) + nand->options |= NAND_NO_SUBPAGE_WRITE; +#endif +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + nand->cmd_ctrl = kw_nand_hwcontrol; + nand->chip_delay = 40; + nand->select_chip = kw_nand_select_chip; + return 0; +} diff --git a/drivers/mtd/nand/raw/kmeter1_nand.c b/drivers/mtd/nand/raw/kmeter1_nand.c new file mode 100644 index 0000000000..7103300060 --- /dev/null +++ b/drivers/mtd/nand/raw/kmeter1_nand.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de + */ + +#include +#include +#include + +#define CONFIG_NAND_MODE_REG (void *)(CONFIG_SYS_NAND_BASE + 0x20000) +#define CONFIG_NAND_DATA_REG (void *)(CONFIG_SYS_NAND_BASE + 0x30000) + +#define read_mode() in_8(CONFIG_NAND_MODE_REG) +#define write_mode(val) out_8(CONFIG_NAND_MODE_REG, val) +#define read_data() in_8(CONFIG_NAND_DATA_REG) +#define write_data(val) out_8(CONFIG_NAND_DATA_REG, val) + +#define KPN_RDY2 (1 << 7) +#define KPN_RDY1 (1 << 6) +#define KPN_WPN (1 << 4) +#define KPN_CE2N (1 << 3) +#define KPN_CE1N (1 << 2) +#define KPN_ALE (1 << 1) +#define KPN_CLE (1 << 0) + +#define KPN_DEFAULT_CHIP_DELAY 50 + +static int kpn_chip_ready(void) +{ + if (read_mode() & KPN_RDY1) + return 1; + + return 0; +} + +static void kpn_wait_rdy(void) +{ + int cnt = 1000000; + + while (--cnt && !kpn_chip_ready()) + udelay(1); + + if (!cnt) + printf ("timeout while waiting for RDY\n"); +} + +static void kpn_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + u8 reg_val = read_mode(); + + if (ctrl & NAND_CTRL_CHANGE) { + reg_val = reg_val & ~(KPN_ALE + KPN_CLE); + + if (ctrl & NAND_CLE) + reg_val = reg_val | KPN_CLE; + if (ctrl & NAND_ALE) + reg_val = reg_val | KPN_ALE; + if (ctrl & NAND_NCE) + reg_val = reg_val & ~KPN_CE1N; + else + reg_val = reg_val | KPN_CE1N; + + write_mode(reg_val); + } + if (cmd != NAND_CMD_NONE) + write_data(cmd); + + /* wait until flash is ready */ + kpn_wait_rdy(); +} + +static u_char kpn_nand_read_byte(struct mtd_info *mtd) +{ + return read_data(); +} + +static void kpn_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + write_data(buf[i]); + kpn_wait_rdy(); + } +} + +static void kpn_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = read_data(); +} + +static int kpn_nand_dev_ready(struct mtd_info *mtd) +{ + kpn_wait_rdy(); + + return 1; +} + +int board_nand_init(struct nand_chip *nand) +{ +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else + nand->ecc.mode = NAND_ECC_SOFT; +#endif + + /* Reference hardware control function */ + nand->cmd_ctrl = kpn_nand_hwcontrol; + nand->read_byte = kpn_nand_read_byte; + nand->write_buf = kpn_nand_write_buf; + nand->read_buf = kpn_nand_read_buf; + nand->dev_ready = kpn_nand_dev_ready; + nand->chip_delay = KPN_DEFAULT_CHIP_DELAY; + + /* reset mode register */ + write_mode(KPN_CE1N + KPN_CE2N + KPN_WPN); + return 0; +} diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c new file mode 100644 index 0000000000..5d4ffea608 --- /dev/null +++ b/drivers/mtd/nand/raw/lpc32xx_nand_mlc.c @@ -0,0 +1,761 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LPC32xx MLC NAND flash controller driver + * + * (C) Copyright 2014 3ADEV + * Written by Albert ARIBAUD + * + * NOTE: + * + * The MLC NAND flash controller provides hardware Reed-Solomon ECC + * covering in- and out-of-band data together. Therefore, in- and out- + * of-band data must be written together in order to have a valid ECC. + * + * Consequently, pages with meaningful in-band data are written with + * blank (all-ones) out-of-band data and a valid ECC, and any later + * out-of-band data write will void the ECC. + * + * Therefore, code which reads such late-written out-of-band data + * should not rely on the ECC validity. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * MLC NAND controller registers. + */ +struct lpc32xx_nand_mlc_registers { + u8 buff[32768]; /* controller's serial data buffer */ + u8 data[32768]; /* NAND's raw data buffer */ + u32 cmd; + u32 addr; + u32 ecc_enc_reg; + u32 ecc_dec_reg; + u32 ecc_auto_enc_reg; + u32 ecc_auto_dec_reg; + u32 rpr; + u32 wpr; + u32 rubp; + u32 robp; + u32 sw_wp_add_low; + u32 sw_wp_add_hig; + u32 icr; + u32 time_reg; + u32 irq_mr; + u32 irq_sr; + u32 lock_pr; + u32 isr; + u32 ceh; +}; + +/* LOCK_PR register defines */ +#define LOCK_PR_UNLOCK_KEY 0x0000A25E /* Magic unlock value */ + +/* ICR defines */ +#define ICR_LARGE_BLOCKS 0x00000004 /* configure for 2KB blocks */ +#define ICR_ADDR4 0x00000002 /* configure for 4-word addrs */ + +/* CEH defines */ +#define CEH_NORMAL_CE 0x00000001 /* do not force CE ON */ + +/* ISR register defines */ +#define ISR_NAND_READY 0x00000001 +#define ISR_CONTROLLER_READY 0x00000002 +#define ISR_ECC_READY 0x00000004 +#define ISR_DECODER_ERRORS(s) ((((s) >> 4) & 3)+1) +#define ISR_DECODER_FAILURE 0x00000040 +#define ISR_DECODER_ERROR 0x00000008 + +/* time-out for NAND chip / controller loops, in us */ +#define LPC32X_NAND_TIMEOUT 5000 + +/* + * There is a single instance of the NAND MLC controller + */ + +static struct lpc32xx_nand_mlc_registers __iomem *lpc32xx_nand_mlc_registers + = (struct lpc32xx_nand_mlc_registers __iomem *)MLC_NAND_BASE; + +#define clkdiv(v, w, o) (((1+(clk/v)) & w) << o) + +/** + * OOB data in each small page are 6 'free' then 10 ECC bytes. + * To make things easier, when reading large pages, the four pages' + * 'free' OOB bytes are grouped in the first 24 bytes of the OOB buffer, + * while the the four ECC bytes are groupe in its last 40 bytes. + * + * The struct below represents how free vs ecc oob bytes are stored + * in the buffer. + * + * Note: the OOB bytes contain the bad block marker at offsets 0 and 1. + */ + +struct lpc32xx_oob { + struct { + uint8_t free_oob_bytes[6]; + } free[4]; + struct { + uint8_t ecc_oob_bytes[10]; + } ecc[4]; +}; + +/* + * Initialize the controller + */ + +static void lpc32xx_nand_init(void) +{ + unsigned int clk; + + /* Configure controller for no software write protection, x8 bus + width, large block device, and 4 address words */ + + /* unlock controller registers with magic key */ + writel(LOCK_PR_UNLOCK_KEY, + &lpc32xx_nand_mlc_registers->lock_pr); + + /* enable large blocks and large NANDs */ + writel(ICR_LARGE_BLOCKS | ICR_ADDR4, + &lpc32xx_nand_mlc_registers->icr); + + /* Make sure MLC interrupts are disabled */ + writel(0, &lpc32xx_nand_mlc_registers->irq_mr); + + /* Normal chip enable operation */ + writel(CEH_NORMAL_CE, + &lpc32xx_nand_mlc_registers->ceh); + + /* Setup NAND timing */ + clk = get_hclk_clk_rate(); + + writel( + clkdiv(CONFIG_LPC32XX_NAND_MLC_TCEA_DELAY, 0x03, 24) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_BUSY_DELAY, 0x1F, 19) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_NAND_TA, 0x07, 16) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_HIGH, 0x0F, 12) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_RD_LOW, 0x0F, 8) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_HIGH, 0x0F, 4) | + clkdiv(CONFIG_LPC32XX_NAND_MLC_WR_LOW, 0x0F, 0), + &lpc32xx_nand_mlc_registers->time_reg); +} + +#if !defined(CONFIG_SPL_BUILD) + +/** + * lpc32xx_cmd_ctrl - write command to either cmd or data register + */ + +static void lpc32xx_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->cmd); + else if (ctrl & NAND_ALE) + writeb(cmd & 0Xff, &lpc32xx_nand_mlc_registers->addr); +} + +/** + * lpc32xx_read_byte - read a byte from the NAND + * @mtd: MTD device structure + */ + +static uint8_t lpc32xx_read_byte(struct mtd_info *mtd) +{ + return readb(&lpc32xx_nand_mlc_registers->data); +} + +/** + * lpc32xx_dev_ready - test if NAND device (actually controller) is ready + * @mtd: MTD device structure + * @mode: mode to set the ECC HW to. + */ + +static int lpc32xx_dev_ready(struct mtd_info *mtd) +{ + /* means *controller* ready for us */ + int status = readl(&lpc32xx_nand_mlc_registers->isr); + return status & ISR_CONTROLLER_READY; +} + +/** + * ECC layout -- this is needed whatever ECC mode we are using. + * In a 2KB (4*512B) page, R/S codes occupy 40 (4*10) bytes. + * To make U-Boot's life easier, we pack 'useable' OOB at the + * front and R/S ECC at the back. + */ + +static struct nand_ecclayout lpc32xx_largepage_ecclayout = { + .eccbytes = 40, + .eccpos = {24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { + /* bytes 0 and 1 are used for the bad block marker */ + { + .offset = 2, + .length = 22 + }, + } +}; + +/** + * lpc32xx_read_page_hwecc - read in- and out-of-band data with ECC + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Use large block Auto Decode Read Mode(1) as described in User Manual + * section 8.6.2.1. + * + * The initial Read Mode and Read Start commands are sent by the caller. + * + * ECC will be false if out-of-band data has been updated since in-band + * data was initially written. + */ + +static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + unsigned int i, status, timeout, err, max_bitflips = 0; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + /* go through all four small pages */ + for (i = 0; i < 4; i++) { + /* start auto decode (reads 528 NAND bytes) */ + writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); + /* wait for controller to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_CONTROLLER_READY) + break; + udelay(1); + } + /* if decoder failed, return failure */ + if (status & ISR_DECODER_FAILURE) + return -1; + /* keep count of maximum bitflips performed */ + if (status & ISR_DECODER_ERROR) { + err = ISR_DECODER_ERRORS(status); + if (err > max_bitflips) + max_bitflips = err; + } + /* copy first 512 bytes into buffer */ + memcpy(buf+512*i, lpc32xx_nand_mlc_registers->buff, 512); + /* copy next 6 bytes at front of OOB buffer */ + memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); + /* copy last 10 bytes (R/S ECC) at back of OOB buffer */ + memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10); + } + return max_bitflips; +} + +/** + * lpc32xx_read_page_raw - read raw (in-band, out-of-band and ECC) data + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Read NAND directly; can read pages with invalid ECC. + */ + +static int lpc32xx_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + unsigned int i, status, timeout; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + /* when we get here we've already had the Read Mode(1) */ + + /* go through all four small pages */ + for (i = 0; i < 4; i++) { + /* wait for NAND to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_NAND_READY) + break; + udelay(1); + } + /* if NAND stalled, return failure */ + if (!(status & ISR_NAND_READY)) + return -1; + /* copy first 512 bytes into buffer */ + memcpy(buf+512*i, lpc32xx_nand_mlc_registers->data, 512); + /* copy next 6 bytes at front of OOB buffer */ + memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->data, 6); + /* copy last 10 bytes (R/S ECC) at back of OOB buffer */ + memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->data, 10); + } + return 0; +} + +/** + * lpc32xx_read_oob - read out-of-band data + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * + * Read out-of-band data. User Manual section 8.6.4 suggests using Read + * Mode(3) which the controller will turn into a Read Mode(1) internally + * but nand_base.c will turn Mode(3) into Mode(0), so let's use Mode(0) + * directly. + * + * ECC covers in- and out-of-band data and was written when out-of-band + * data was blank. Therefore, if the out-of-band being read here is not + * blank, then the ECC will be false and the read will return bitflips, + * even in case of ECC failure where we will return 5 bitflips. The + * caller should be prepared to handle this. + */ + +static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + unsigned int i, status, timeout, err, max_bitflips = 0; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + /* No command was sent before calling read_oob() so send one */ + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + /* go through all four small pages */ + for (i = 0; i < 4; i++) { + /* start auto decode (reads 528 NAND bytes) */ + writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); + /* wait for controller to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_CONTROLLER_READY) + break; + udelay(1); + } + /* if decoder failure, count 'one too many' bitflips */ + if (status & ISR_DECODER_FAILURE) + max_bitflips = 5; + /* keep count of maximum bitflips performed */ + if (status & ISR_DECODER_ERROR) { + err = ISR_DECODER_ERRORS(status); + if (err > max_bitflips) + max_bitflips = err; + } + /* set read pointer to OOB area */ + writel(0, &lpc32xx_nand_mlc_registers->robp); + /* copy next 6 bytes at front of OOB buffer */ + memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); + /* copy next 10 bytes (R/S ECC) at back of OOB buffer */ + memcpy(&oob->ecc[i], lpc32xx_nand_mlc_registers->buff, 10); + } + return max_bitflips; +} + +/** + * lpc32xx_write_page_hwecc - write in- and out-of-band data with ECC + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * Use large block Auto Encode as per User Manual section 8.6.4. + * + * The initial Write Serial Input and final Auto Program commands are + * sent by the caller. + */ + +static int lpc32xx_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + unsigned int i, status, timeout; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + /* when we get here we've already had the SEQIN */ + for (i = 0; i < 4; i++) { + /* start encode (expects 518 writes to buff) */ + writel(0, &lpc32xx_nand_mlc_registers->ecc_enc_reg); + /* copy first 512 bytes from buffer */ + memcpy(&lpc32xx_nand_mlc_registers->buff, buf+512*i, 512); + /* copy next 6 bytes from OOB buffer -- excluding ECC */ + memcpy(&lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6); + /* wait for ECC to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_ECC_READY) + break; + udelay(1); + } + /* if ECC stalled, return failure */ + if (!(status & ISR_ECC_READY)) + return -1; + /* Trigger auto encode (writes 528 bytes to NAND) */ + writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_enc_reg); + /* wait for controller to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_CONTROLLER_READY) + break; + udelay(1); + } + /* if controller stalled, return error */ + if (!(status & ISR_CONTROLLER_READY)) + return -1; + } + return 0; +} + +/** + * lpc32xx_write_page_raw - write raw (in-band, out-of-band and ECC) data + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Use large block write but without encode. + * + * The initial Write Serial Input and final Auto Program commands are + * sent by the caller. + * + * This function will write the full out-of-band data, including the + * ECC area. Therefore, it can write pages with valid *or* invalid ECC. + */ + +static int lpc32xx_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + unsigned int i; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + /* when we get here we've already had the Read Mode(1) */ + for (i = 0; i < 4; i++) { + /* copy first 512 bytes from buffer */ + memcpy(lpc32xx_nand_mlc_registers->buff, buf+512*i, 512); + /* copy next 6 bytes into OOB buffer -- excluding ECC */ + memcpy(lpc32xx_nand_mlc_registers->buff, &oob->free[i], 6); + /* copy next 10 bytes into OOB buffer -- that is 'ECC' */ + memcpy(lpc32xx_nand_mlc_registers->buff, &oob->ecc[i], 10); + } + return 0; +} + +/** + * lpc32xx_write_oob - write out-of-band data + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * + * Since ECC covers in- and out-of-band data, writing out-of-band data + * with ECC will render the page ECC wrong -- or, if the page was blank, + * then it will produce a good ECC but a later in-band data write will + * render it wrong. + * + * Therefore, do not compute or write any ECC, and always return success. + * + * This implies that we do four writes, since non-ECC out-of-band data + * are not contiguous in a large page. + */ + +static int lpc32xx_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + /* update oob on all 4 subpages in sequence */ + unsigned int i, status, timeout; + struct lpc32xx_oob *oob = (struct lpc32xx_oob *)chip->oob_poi; + + for (i = 0; i < 4; i++) { + /* start data input */ + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x200+0x210*i, page); + /* copy 6 non-ECC out-of-band bytes directly into NAND */ + memcpy(lpc32xx_nand_mlc_registers->data, &oob->free[i], 6); + /* program page */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + /* wait for NAND to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_NAND_READY) + break; + udelay(1); + } + /* if NAND stalled, return error */ + if (!(status & ISR_NAND_READY)) + return -1; + } + return 0; +} + +/** + * lpc32xx_waitfunc - wait until a command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * + * Wait for controller and FLASH to both be ready. + */ + +static int lpc32xx_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + int status; + unsigned int timeout; + /* wait until both controller and NAND are ready */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY)) + == (ISR_CONTROLLER_READY || ISR_NAND_READY)) + break; + udelay(1); + } + /* if controller or NAND stalled, return error */ + if ((status & (ISR_CONTROLLER_READY || ISR_NAND_READY)) + != (ISR_CONTROLLER_READY || ISR_NAND_READY)) + return -1; + /* write NAND status command */ + writel(NAND_CMD_STATUS, &lpc32xx_nand_mlc_registers->cmd); + /* read back status and return it */ + return readb(&lpc32xx_nand_mlc_registers->data); +} + +/* + * We are self-initializing, so we need our own chip struct + */ + +static struct nand_chip lpc32xx_chip; + +/* + * Initialize the controller + */ + +void board_nand_init(void) +{ + struct mtd_info *mtd = nand_to_mtd(&lpc32xx_chip); + int ret; + + /* Set all BOARDSPECIFIC (actually core-specific) fields */ + + lpc32xx_chip.IO_ADDR_R = &lpc32xx_nand_mlc_registers->buff; + lpc32xx_chip.IO_ADDR_W = &lpc32xx_nand_mlc_registers->buff; + lpc32xx_chip.cmd_ctrl = lpc32xx_cmd_ctrl; + /* do not set init_size: nand_base.c will read sizes from chip */ + lpc32xx_chip.dev_ready = lpc32xx_dev_ready; + /* do not set setup_read_retry: this is NAND-chip-specific */ + /* do not set chip_delay: we have dev_ready defined. */ + lpc32xx_chip.options |= NAND_NO_SUBPAGE_WRITE; + + /* Set needed ECC fields */ + + lpc32xx_chip.ecc.mode = NAND_ECC_HW; + lpc32xx_chip.ecc.layout = &lpc32xx_largepage_ecclayout; + lpc32xx_chip.ecc.size = 512; + lpc32xx_chip.ecc.bytes = 10; + lpc32xx_chip.ecc.strength = 4; + lpc32xx_chip.ecc.read_page = lpc32xx_read_page_hwecc; + lpc32xx_chip.ecc.read_page_raw = lpc32xx_read_page_raw; + lpc32xx_chip.ecc.write_page = lpc32xx_write_page_hwecc; + lpc32xx_chip.ecc.write_page_raw = lpc32xx_write_page_raw; + lpc32xx_chip.ecc.read_oob = lpc32xx_read_oob; + lpc32xx_chip.ecc.write_oob = lpc32xx_write_oob; + lpc32xx_chip.waitfunc = lpc32xx_waitfunc; + + lpc32xx_chip.read_byte = lpc32xx_read_byte; /* FIXME: NEEDED? */ + + /* BBT options: read from last two pages */ + lpc32xx_chip.bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_LASTBLOCK + | NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE + | NAND_BBT_WRITE; + + /* Initialize NAND interface */ + lpc32xx_nand_init(); + + /* identify chip */ + ret = nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_CHIPS, NULL); + if (ret) { + pr_err("nand_scan_ident returned %i", ret); + return; + } + + /* finish scanning the chip */ + ret = nand_scan_tail(mtd); + if (ret) { + pr_err("nand_scan_tail returned %i", ret); + return; + } + + /* chip is good, register it */ + ret = nand_register(0, mtd); + if (ret) + pr_err("nand_register returned %i", ret); +} + +#else /* defined(CONFIG_SPL_BUILD) */ + +void nand_init(void) +{ + /* enable NAND controller */ + lpc32xx_mlc_nand_init(); + /* initialize NAND controller */ + lpc32xx_nand_init(); +} + +void nand_deselect(void) +{ + /* nothing to do, but SPL requires this function */ +} + +static int read_single_page(uint8_t *dest, int page, + struct lpc32xx_oob *oob) +{ + int status, i, timeout, err, max_bitflips = 0; + + /* enter read mode */ + writel(NAND_CMD_READ0, &lpc32xx_nand_mlc_registers->cmd); + /* send column (lsb then MSB) and page (lsb to MSB) */ + writel(0, &lpc32xx_nand_mlc_registers->addr); + writel(0, &lpc32xx_nand_mlc_registers->addr); + writel(page & 0xff, &lpc32xx_nand_mlc_registers->addr); + writel((page>>8) & 0xff, &lpc32xx_nand_mlc_registers->addr); + writel((page>>16) & 0xff, &lpc32xx_nand_mlc_registers->addr); + /* start reading */ + writel(NAND_CMD_READSTART, &lpc32xx_nand_mlc_registers->cmd); + + /* large page auto decode read */ + for (i = 0; i < 4; i++) { + /* start auto decode (reads 528 NAND bytes) */ + writel(0, &lpc32xx_nand_mlc_registers->ecc_auto_dec_reg); + /* wait for controller to return to ready state */ + for (timeout = LPC32X_NAND_TIMEOUT; timeout; timeout--) { + status = readl(&lpc32xx_nand_mlc_registers->isr); + if (status & ISR_CONTROLLER_READY) + break; + udelay(1); + } + /* if controller stalled, return error */ + if (!(status & ISR_CONTROLLER_READY)) + return -1; + /* if decoder failure, return error */ + if (status & ISR_DECODER_FAILURE) + return -1; + /* keep count of maximum bitflips performed */ + if (status & ISR_DECODER_ERROR) { + err = ISR_DECODER_ERRORS(status); + if (err > max_bitflips) + max_bitflips = err; + } + /* copy first 512 bytes into buffer */ + memcpy(dest+i*512, lpc32xx_nand_mlc_registers->buff, 512); + /* copy next 6 bytes bytes into OOB buffer */ + memcpy(&oob->free[i], lpc32xx_nand_mlc_registers->buff, 6); + } + return max_bitflips; +} + +/* + * Load U-Boot signed image. + * This loads an image from NAND, skipping bad blocks. + * A block is declared bad if at least one of its readable pages has + * a bad block marker in its OOB at position 0. + * If all pages ion a block are unreadable, the block is considered + * bad (i.e., assumed not to be part of the image) and skipped. + * + * IMPORTANT NOTE: + * + * If the first block of the image is fully unreadable, it will be + * ignored and skipped as if it had been marked bad. If it was not + * actually marked bad at the time of writing the image, the resulting + * image loaded will lack a header and magic number. It could thus be + * considered as a raw, headerless, image and SPL might erroneously + * jump into it. + * + * In order to avoid this risk, LPC32XX-based boards which use this + * driver MUST define CONFIG_SPL_PANIC_ON_RAW_IMAGE. + */ + +#define BYTES_PER_PAGE 2048 +#define PAGES_PER_BLOCK 64 +#define BYTES_PER_BLOCK (BYTES_PER_PAGE * PAGES_PER_BLOCK) +#define PAGES_PER_CHIP_MAX 524288 + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + int bytes_left = size; + int pages_left = DIV_ROUND_UP(size, BYTES_PER_PAGE); + int blocks_left = DIV_ROUND_UP(size, BYTES_PER_BLOCK); + int block = 0; + int page = offs / BYTES_PER_PAGE; + /* perform reads block by block */ + while (blocks_left) { + /* compute first page number to read */ + void *block_page_dst = dst; + /* read at most one block, possibly less */ + int block_bytes_left = bytes_left; + if (block_bytes_left > BYTES_PER_BLOCK) + block_bytes_left = BYTES_PER_BLOCK; + /* keep track of good, failed, and "bad" pages */ + int block_pages_good = 0; + int block_pages_bad = 0; + int block_pages_err = 0; + /* we shall read a full block of pages, maybe less */ + int block_pages_left = pages_left; + if (block_pages_left > PAGES_PER_BLOCK) + block_pages_left = PAGES_PER_BLOCK; + int block_pages = block_pages_left; + int block_page = page; + /* while pages are left and the block is not known as bad */ + while ((block_pages > 0) && (block_pages_bad == 0)) { + /* we will read OOB, too, for bad block markers */ + struct lpc32xx_oob oob; + /* read page */ + int res = read_single_page(block_page_dst, block_page, + &oob); + /* count readable pages */ + if (res >= 0) { + /* this page is good */ + block_pages_good++; + /* this page is bad */ + if ((oob.free[0].free_oob_bytes[0] != 0xff) + | (oob.free[0].free_oob_bytes[1] != 0xff)) + block_pages_bad++; + } else + /* count errors */ + block_pages_err++; + /* we're done with this page */ + block_page++; + block_page_dst += BYTES_PER_PAGE; + if (block_pages) + block_pages--; + } + /* a fully unreadable block is considered bad */ + if (block_pages_good == 0) + block_pages_bad = block_pages_err; + /* errors are fatal only in good blocks */ + if ((block_pages_err > 0) && (block_pages_bad == 0)) + return -1; + /* we keep reads only of good blocks */ + if (block_pages_bad == 0) { + dst += block_bytes_left; + bytes_left -= block_bytes_left; + pages_left -= block_pages_left; + blocks_left--; + } + /* good or bad, we're done with this block */ + block++; + page += PAGES_PER_BLOCK; + } + + /* report success */ + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ diff --git a/drivers/mtd/nand/raw/lpc32xx_nand_slc.c b/drivers/mtd/nand/raw/lpc32xx_nand_slc.c new file mode 100644 index 0000000000..99f6e15f4e --- /dev/null +++ b/drivers/mtd/nand/raw/lpc32xx_nand_slc.c @@ -0,0 +1,597 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * LPC32xx SLC NAND flash controller driver + * + * (C) Copyright 2015 Vladimir Zapolskiy + * + * Hardware ECC support original source code + * Copyright (C) 2008 by NXP Semiconductors + * Author: Kevin Wells + * + * Copyright (c) 2015 Tyco Fire Protection Products. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DMA_LPC32XX) && defined(CONFIG_SPL_BUILD) +#warning "DMA support in SPL image is not tested" +#endif + +struct lpc32xx_nand_slc_regs { + u32 data; + u32 addr; + u32 cmd; + u32 stop; + u32 ctrl; + u32 cfg; + u32 stat; + u32 int_stat; + u32 ien; + u32 isr; + u32 icr; + u32 tac; + u32 tc; + u32 ecc; + u32 dma_data; +}; + +/* CFG register */ +#define CFG_CE_LOW (1 << 5) +#define CFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ +#define CFG_ECC_EN (1 << 3) /* ECC enable bit */ +#define CFG_DMA_BURST (1 << 2) /* DMA burst bit */ +#define CFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ + +/* CTRL register */ +#define CTRL_SW_RESET (1 << 2) +#define CTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ +#define CTRL_DMA_START (1 << 0) /* Start DMA channel bit */ + +/* STAT register */ +#define STAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ +#define STAT_NAND_READY (1 << 0) + +/* INT_STAT register */ +#define INT_STAT_TC (1 << 1) +#define INT_STAT_RDY (1 << 0) + +/* TAC register bits, be aware of overflows */ +#define TAC_W_RDY(n) (max_t(uint32_t, (n), 0xF) << 28) +#define TAC_W_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 24) +#define TAC_W_HOLD(n) (max_t(uint32_t, (n), 0xF) << 20) +#define TAC_W_SETUP(n) (max_t(uint32_t, (n), 0xF) << 16) +#define TAC_R_RDY(n) (max_t(uint32_t, (n), 0xF) << 12) +#define TAC_R_WIDTH(n) (max_t(uint32_t, (n), 0xF) << 8) +#define TAC_R_HOLD(n) (max_t(uint32_t, (n), 0xF) << 4) +#define TAC_R_SETUP(n) (max_t(uint32_t, (n), 0xF) << 0) + +/* NAND ECC Layout for small page NAND devices + * Note: For large page devices, the default layouts are used. */ +static struct nand_ecclayout lpc32xx_nand_oob_16 = { + .eccbytes = 6, + .eccpos = {10, 11, 12, 13, 14, 15}, + .oobfree = { + {.offset = 0, + . length = 4}, + {.offset = 6, + . length = 4} + } +}; + +#if defined(CONFIG_DMA_LPC32XX) +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / CONFIG_SYS_NAND_ECCSIZE) + +/* + * DMA Descriptors + * For Large Block: 17 descriptors = ((16 Data and ECC Read) + 1 Spare Area) + * For Small Block: 5 descriptors = ((4 Data and ECC Read) + 1 Spare Area) + */ +static struct lpc32xx_dmac_ll dmalist[ECCSTEPS * 2 + 1]; +static u32 ecc_buffer[8]; /* MAX ECC size */ +static unsigned int dmachan = (unsigned int)-1; /* Invalid channel */ + +/* + * Helper macro for the DMA client (i.e. NAND SLC): + * - to write the next DMA linked list item address + * (see arch/include/asm/arch-lpc32xx/dma.h). + * - to assign the DMA data register to DMA source or destination address. + * - to assign the ECC register to DMA source or destination address. + */ +#define lpc32xx_dmac_next_lli(x) ((u32)x) +#define lpc32xx_dmac_set_dma_data() ((u32)&lpc32xx_nand_slc_regs->dma_data) +#define lpc32xx_dmac_set_ecc() ((u32)&lpc32xx_nand_slc_regs->ecc) +#endif + +static struct lpc32xx_nand_slc_regs __iomem *lpc32xx_nand_slc_regs + = (struct lpc32xx_nand_slc_regs __iomem *)SLC_NAND_BASE; + +static void lpc32xx_nand_init(void) +{ + uint32_t hclk = get_hclk_clk_rate(); + + /* Reset SLC NAND controller */ + writel(CTRL_SW_RESET, &lpc32xx_nand_slc_regs->ctrl); + + /* 8-bit bus, no DMA, no ECC, ordinary CE signal */ + writel(0, &lpc32xx_nand_slc_regs->cfg); + + /* Interrupts disabled and cleared */ + writel(0, &lpc32xx_nand_slc_regs->ien); + writel(INT_STAT_TC | INT_STAT_RDY, + &lpc32xx_nand_slc_regs->icr); + + /* Configure NAND flash timings */ + writel(TAC_W_RDY(CONFIG_LPC32XX_NAND_SLC_WDR_CLKS) | + TAC_W_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_WWIDTH) | + TAC_W_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_WHOLD) | + TAC_W_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_WSETUP) | + TAC_R_RDY(CONFIG_LPC32XX_NAND_SLC_RDR_CLKS) | + TAC_R_WIDTH(hclk / CONFIG_LPC32XX_NAND_SLC_RWIDTH) | + TAC_R_HOLD(hclk / CONFIG_LPC32XX_NAND_SLC_RHOLD) | + TAC_R_SETUP(hclk / CONFIG_LPC32XX_NAND_SLC_RSETUP), + &lpc32xx_nand_slc_regs->tac); +} + +static void lpc32xx_nand_cmd_ctrl(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + debug("ctrl: 0x%08x, cmd: 0x%08x\n", ctrl, cmd); + + if (ctrl & NAND_NCE) + setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW); + else + clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_CE_LOW); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->cmd); + else if (ctrl & NAND_ALE) + writel(cmd & 0xFF, &lpc32xx_nand_slc_regs->addr); +} + +static int lpc32xx_nand_dev_ready(struct mtd_info *mtd) +{ + return readl(&lpc32xx_nand_slc_regs->stat) & STAT_NAND_READY; +} + +#if defined(CONFIG_DMA_LPC32XX) +/* + * Prepares DMA descriptors for NAND RD/WR operations + * If the size is < 256 Bytes then it is assumed to be + * an OOB transfer + */ +static void lpc32xx_nand_dma_configure(struct nand_chip *chip, + const u8 *buffer, int size, + int read) +{ + u32 i, dmasrc, ctrl, ecc_ctrl, oob_ctrl, dmadst; + struct lpc32xx_dmac_ll *dmalist_cur; + struct lpc32xx_dmac_ll *dmalist_cur_ecc; + + /* + * CTRL descriptor entry for reading ECC + * Copy Multiple times to sync DMA with Flash Controller + */ + ecc_ctrl = 0x5 | + DMAC_CHAN_SRC_BURST_1 | + DMAC_CHAN_DEST_BURST_1 | + DMAC_CHAN_SRC_WIDTH_32 | + DMAC_CHAN_DEST_WIDTH_32 | + DMAC_CHAN_DEST_AHB1; + + /* CTRL descriptor entry for reading/writing Data */ + ctrl = (CONFIG_SYS_NAND_ECCSIZE / 4) | + DMAC_CHAN_SRC_BURST_4 | + DMAC_CHAN_DEST_BURST_4 | + DMAC_CHAN_SRC_WIDTH_32 | + DMAC_CHAN_DEST_WIDTH_32 | + DMAC_CHAN_DEST_AHB1; + + /* CTRL descriptor entry for reading/writing Spare Area */ + oob_ctrl = (CONFIG_SYS_NAND_OOBSIZE / 4) | + DMAC_CHAN_SRC_BURST_4 | + DMAC_CHAN_DEST_BURST_4 | + DMAC_CHAN_SRC_WIDTH_32 | + DMAC_CHAN_DEST_WIDTH_32 | + DMAC_CHAN_DEST_AHB1; + + if (read) { + dmasrc = lpc32xx_dmac_set_dma_data(); + dmadst = (u32)buffer; + ctrl |= DMAC_CHAN_DEST_AUTOINC; + } else { + dmadst = lpc32xx_dmac_set_dma_data(); + dmasrc = (u32)buffer; + ctrl |= DMAC_CHAN_SRC_AUTOINC; + } + + /* + * Write Operation Sequence for Small Block NAND + * ---------------------------------------------------------- + * 1. X'fer 256 bytes of data from Memory to Flash. + * 2. Copy generated ECC data from Register to Spare Area + * 3. X'fer next 256 bytes of data from Memory to Flash. + * 4. Copy generated ECC data from Register to Spare Area. + * 5. X'fer 16 byets of Spare area from Memory to Flash. + * Read Operation Sequence for Small Block NAND + * ---------------------------------------------------------- + * 1. X'fer 256 bytes of data from Flash to Memory. + * 2. Copy generated ECC data from Register to ECC calc Buffer. + * 3. X'fer next 256 bytes of data from Flash to Memory. + * 4. Copy generated ECC data from Register to ECC calc Buffer. + * 5. X'fer 16 bytes of Spare area from Flash to Memory. + * Write Operation Sequence for Large Block NAND + * ---------------------------------------------------------- + * 1. Steps(1-4) of Write Operations repeate for four times + * which generates 16 DMA descriptors to X'fer 2048 bytes of + * data & 32 bytes of ECC data. + * 2. X'fer 64 bytes of Spare area from Memory to Flash. + * Read Operation Sequence for Large Block NAND + * ---------------------------------------------------------- + * 1. Steps(1-4) of Read Operations repeate for four times + * which generates 16 DMA descriptors to X'fer 2048 bytes of + * data & 32 bytes of ECC data. + * 2. X'fer 64 bytes of Spare area from Flash to Memory. + */ + + for (i = 0; i < size/CONFIG_SYS_NAND_ECCSIZE; i++) { + dmalist_cur = &dmalist[i * 2]; + dmalist_cur_ecc = &dmalist[(i * 2) + 1]; + + dmalist_cur->dma_src = (read ? (dmasrc) : (dmasrc + (i*256))); + dmalist_cur->dma_dest = (read ? (dmadst + (i*256)) : dmadst); + dmalist_cur->next_lli = lpc32xx_dmac_next_lli(dmalist_cur_ecc); + dmalist_cur->next_ctrl = ctrl; + + dmalist_cur_ecc->dma_src = lpc32xx_dmac_set_ecc(); + dmalist_cur_ecc->dma_dest = (u32)&ecc_buffer[i]; + dmalist_cur_ecc->next_lli = + lpc32xx_dmac_next_lli(&dmalist[(i * 2) + 2]); + dmalist_cur_ecc->next_ctrl = ecc_ctrl; + } + + if (i) { /* Data only transfer */ + dmalist_cur_ecc = &dmalist[(i * 2) - 1]; + dmalist_cur_ecc->next_lli = 0; + dmalist_cur_ecc->next_ctrl |= DMAC_CHAN_INT_TC_EN; + return; + } + + /* OOB only transfer */ + if (read) { + dmasrc = lpc32xx_dmac_set_dma_data(); + dmadst = (u32)buffer; + oob_ctrl |= DMAC_CHAN_DEST_AUTOINC; + } else { + dmadst = lpc32xx_dmac_set_dma_data(); + dmasrc = (u32)buffer; + oob_ctrl |= DMAC_CHAN_SRC_AUTOINC; + } + + /* Read/ Write Spare Area Data To/From Flash */ + dmalist_cur = &dmalist[i * 2]; + dmalist_cur->dma_src = dmasrc; + dmalist_cur->dma_dest = dmadst; + dmalist_cur->next_lli = 0; + dmalist_cur->next_ctrl = (oob_ctrl | DMAC_CHAN_INT_TC_EN); +} + +static void lpc32xx_nand_xfer(struct mtd_info *mtd, const u8 *buf, + int len, int read) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u32 config; + int ret; + + /* DMA Channel Configuration */ + config = (read ? DMAC_CHAN_FLOW_D_P2M : DMAC_CHAN_FLOW_D_M2P) | + (read ? DMAC_DEST_PERIP(0) : DMAC_DEST_PERIP(DMA_PERID_NAND1)) | + (read ? DMAC_SRC_PERIP(DMA_PERID_NAND1) : DMAC_SRC_PERIP(0)) | + DMAC_CHAN_ENABLE; + + /* Prepare DMA descriptors */ + lpc32xx_nand_dma_configure(chip, buf, len, read); + + /* Setup SLC controller and start transfer */ + if (read) + setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR); + else /* NAND_ECC_WRITE */ + clrbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_DIR); + setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_DMA_BURST); + + /* Write length for new transfers */ + if (!((readl(&lpc32xx_nand_slc_regs->stat) & STAT_DMA_FIFO) | + readl(&lpc32xx_nand_slc_regs->tc))) { + int tmp = (len != mtd->oobsize) ? mtd->oobsize : 0; + writel(len + tmp, &lpc32xx_nand_slc_regs->tc); + } + + setbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START); + + /* Start DMA transfers */ + ret = lpc32xx_dma_start_xfer(dmachan, dmalist, config); + if (unlikely(ret < 0)) + BUG(); + + + /* Wait for NAND to be ready */ + while (!lpc32xx_nand_dev_ready(mtd)) + ; + + /* Wait till DMA transfer is DONE */ + if (lpc32xx_dma_wait_status(dmachan)) + pr_err("NAND DMA transfer error!\r\n"); + + /* Stop DMA & HW ECC */ + clrbits_le32(&lpc32xx_nand_slc_regs->ctrl, CTRL_DMA_START); + clrbits_le32(&lpc32xx_nand_slc_regs->cfg, + CFG_DMA_DIR | CFG_DMA_BURST | CFG_ECC_EN | CFG_DMA_ECC); +} + +static u32 slc_ecc_copy_to_buffer(u8 *spare, const u32 *ecc, int count) +{ + int i; + for (i = 0; i < (count * CONFIG_SYS_NAND_ECCBYTES); + i += CONFIG_SYS_NAND_ECCBYTES) { + u32 ce = ecc[i / CONFIG_SYS_NAND_ECCBYTES]; + ce = ~(ce << 2) & 0xFFFFFF; + spare[i+2] = (u8)(ce & 0xFF); ce >>= 8; + spare[i+1] = (u8)(ce & 0xFF); ce >>= 8; + spare[i] = (u8)(ce & 0xFF); + } + return 0; +} + +static int lpc32xx_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return slc_ecc_copy_to_buffer(ecc_code, ecc_buffer, ECCSTEPS); +} + +/* + * Enables and prepares SLC NAND controller + * for doing data transfers with H/W ECC enabled. + */ +static void lpc32xx_hwecc_enable(struct mtd_info *mtd, int mode) +{ + /* Clear ECC */ + writel(CTRL_ECC_CLEAR, &lpc32xx_nand_slc_regs->ctrl); + + /* Setup SLC controller for H/W ECC operations */ + setbits_le32(&lpc32xx_nand_slc_regs->cfg, CFG_ECC_EN | CFG_DMA_ECC); +} + +/* + * lpc32xx_correct_data - [NAND Interface] Detect and correct bit error(s) + * mtd: MTD block structure + * dat: raw data read from the chip + * read_ecc: ECC from the chip + * calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int lpc32xx_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + unsigned int i; + int ret1, ret2 = 0; + u_char *r = read_ecc; + u_char *c = calc_ecc; + u16 data_offset = 0; + + for (i = 0 ; i < ECCSTEPS ; i++) { + r += CONFIG_SYS_NAND_ECCBYTES; + c += CONFIG_SYS_NAND_ECCBYTES; + data_offset += CONFIG_SYS_NAND_ECCSIZE; + + ret1 = nand_correct_data(mtd, dat + data_offset, r, c); + if (ret1 < 0) + return -EBADMSG; + else + ret2 += ret1; + } + + return ret2; +} +#endif + +#if defined(CONFIG_DMA_LPC32XX) +static void lpc32xx_dma_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + lpc32xx_nand_xfer(mtd, buf, len, 1); +} +#else +static void lpc32xx_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + while (len-- > 0) + *buf++ = readl(&lpc32xx_nand_slc_regs->data); +} +#endif + +static uint8_t lpc32xx_read_byte(struct mtd_info *mtd) +{ + return readl(&lpc32xx_nand_slc_regs->data); +} + +#if defined(CONFIG_DMA_LPC32XX) +static void lpc32xx_dma_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + lpc32xx_nand_xfer(mtd, buf, len, 0); +} +#else +static void lpc32xx_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + while (len-- > 0) + writel(*buf++, &lpc32xx_nand_slc_regs->data); +} +#endif + +static void lpc32xx_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + writel(byte, &lpc32xx_nand_slc_regs->data); +} + +#if defined(CONFIG_DMA_LPC32XX) +/* Reuse the logic from "nand_read_page_hwecc()" */ +static int lpc32xx_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i; + int stat; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; + + /* + * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes + * and section 9.7, the NAND SLC & DMA allowed single DMA transaction + * of a page size using DMA controller scatter/gather mode through + * linked list; the ECC read is done without any software intervention. + */ + + lpc32xx_hwecc_enable(mtd, NAND_ECC_READ); + lpc32xx_dma_read_buf(mtd, p, chip->ecc.size * chip->ecc.steps); + lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]); + lpc32xx_dma_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + stat = chip->ecc.correct(mtd, p, &ecc_code[0], &ecc_calc[0]); + if (stat < 0) + mtd->ecc_stats.failed++; + else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + + return max_bitflips; +} + +/* Reuse the logic from "nand_write_page_hwecc()" */ +static int lpc32xx_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* + * As per the "LPC32x0 and LPC32x0/01 User manual" table 173 notes + * and section 9.7, the NAND SLC & DMA allowed single DMA transaction + * of a page size using DMA controller scatter/gather mode through + * linked list; the ECC read is done without any software intervention. + */ + + lpc32xx_hwecc_enable(mtd, NAND_ECC_WRITE); + lpc32xx_dma_write_buf(mtd, p, chip->ecc.size * chip->ecc.steps); + lpc32xx_ecc_calculate(mtd, p, &ecc_calc[0]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + lpc32xx_dma_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} +#endif + +/* + * LPC32xx has only one SLC NAND controller, don't utilize + * CONFIG_SYS_NAND_SELF_INIT to be able to reuse this function + * both in SPL NAND and U-Boot images. + */ +int board_nand_init(struct nand_chip *lpc32xx_chip) +{ +#if defined(CONFIG_DMA_LPC32XX) + int ret; + + /* Acquire a channel for our use */ + ret = lpc32xx_dma_get_channel(); + if (unlikely(ret < 0)) { + pr_info("Unable to get free DMA channel for NAND transfers\n"); + return -1; + } + dmachan = (unsigned int)ret; +#endif + + lpc32xx_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; + lpc32xx_chip->dev_ready = lpc32xx_nand_dev_ready; + + /* + * The implementation of these functions is quite common, but + * they MUST be defined, because access to data register + * is strictly 32-bit aligned. + */ + lpc32xx_chip->read_byte = lpc32xx_read_byte; + lpc32xx_chip->write_byte = lpc32xx_write_byte; + +#if defined(CONFIG_DMA_LPC32XX) + /* Hardware ECC calculation is supported when DMA driver is selected */ + lpc32xx_chip->ecc.mode = NAND_ECC_HW; + + lpc32xx_chip->read_buf = lpc32xx_dma_read_buf; + lpc32xx_chip->write_buf = lpc32xx_dma_write_buf; + + lpc32xx_chip->ecc.calculate = lpc32xx_ecc_calculate; + lpc32xx_chip->ecc.correct = lpc32xx_correct_data; + lpc32xx_chip->ecc.hwctl = lpc32xx_hwecc_enable; + lpc32xx_chip->chip_delay = 2000; + + lpc32xx_chip->ecc.read_page = lpc32xx_read_page_hwecc; + lpc32xx_chip->ecc.write_page = lpc32xx_write_page_hwecc; + lpc32xx_chip->options |= NAND_NO_SUBPAGE_WRITE; +#else + /* + * Hardware ECC calculation is not supported by the driver, + * because it requires DMA support, see LPC32x0 User Manual, + * note after SLC_ECC register description (UM10326, p.198) + */ + lpc32xx_chip->ecc.mode = NAND_ECC_SOFT; + + /* + * The implementation of these functions is quite common, but + * they MUST be defined, because access to data register + * is strictly 32-bit aligned. + */ + lpc32xx_chip->read_buf = lpc32xx_read_buf; + lpc32xx_chip->write_buf = lpc32xx_write_buf; +#endif + + /* + * These values are predefined + * for both small and large page NAND flash devices. + */ + lpc32xx_chip->ecc.size = CONFIG_SYS_NAND_ECCSIZE; + lpc32xx_chip->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; + lpc32xx_chip->ecc.strength = 1; + + if (CONFIG_SYS_NAND_PAGE_SIZE != NAND_LARGE_BLOCK_PAGE_SIZE) + lpc32xx_chip->ecc.layout = &lpc32xx_nand_oob_16; + +#if defined(CONFIG_SYS_NAND_USE_FLASH_BBT) + lpc32xx_chip->bbt_options |= NAND_BBT_USE_FLASH; +#endif + + /* Initialize NAND interface */ + lpc32xx_nand_init(); + + return 0; +} diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c new file mode 100644 index 0000000000..cf97e0f74f --- /dev/null +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -0,0 +1,1307 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + * Copyright 2009 Ilya Yanok, + */ + +#include +#include +#include +#include +#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) +#include +#endif +#include "mxc_nand.h" + +#define DRIVER_NAME "mxc_nand" + +struct mxc_nand_host { + struct nand_chip *nand; + + struct mxc_nand_regs __iomem *regs; +#ifdef MXC_NFC_V3_2 + struct mxc_nand_ip_regs __iomem *ip_regs; +#endif + int spare_only; + int status_request; + int pagesize_2k; + int clk_act; + uint16_t col_addr; + unsigned int page_addr; +}; + +static struct mxc_nand_host mxc_host; +static struct mxc_nand_host *host = &mxc_host; + +/* Define delays in microsec for NAND device operations */ +#define TROP_US_DELAY 2000 +/* Macros to get byte and bit positions of ECC */ +#define COLPOS(x) ((x) >> 3) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +/* OOB placement block for use with hardware ecc generation */ +#if defined(MXC_NFC_V1) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { {0, 5}, {11, 5}, } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 20, + .eccpos = { + 6, 7, 8, 9, 10, + 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, + 54, 55, 56, 57, 58, + }, + .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, +}; +#endif +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +#ifndef CONFIG_SYS_NAND_LARGEPAGE +static struct nand_ecclayout nand_hw_eccoob = { + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = { {2, 5} } +}; +#else +static struct nand_ecclayout nand_hw_eccoob2k = { + .eccbytes = 36, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + }, + .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} }, +}; +#endif +#endif + +static int is_16bit_nand(void) +{ +#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) + return 1; +#else + return 0; +#endif +} + +static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) +{ + uint32_t *d = dest; + + size >>= 2; + while (size--) + __raw_writel(__raw_readl(source++), d++); + return dest; +} + +/* + * This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit. + */ +static void wait_op_done(struct mxc_nand_host *host, int max_retries, + uint16_t param) +{ + uint32_t tmp; + + while (max_retries-- > 0) { +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + tmp = readnfc(&host->regs->config2); + if (tmp & NFC_V1_V2_CONFIG2_INT) { + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &host->regs->config2); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->ip_regs->ipc); + if (tmp & NFC_V3_IPC_INT) { + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &host->ip_regs->ipc); +#endif + break; + } + udelay(1); + } + if (max_retries < 0) { + pr_debug("%s(%d): INT not set\n", + __func__, param); + } +} + +/* + * This function issues the specified command to the NAND device and + * waits for completion. + */ +static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) +{ + pr_debug("send_cmd(host, 0x%x)\n", cmd); + + writenfc(cmd, &host->regs->flash_cmd); + writenfc(NFC_CMD, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, cmd); +} + +/* + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + */ +static void send_addr(struct mxc_nand_host *host, uint16_t addr) +{ + pr_debug("send_addr(host, 0x%x)\n", addr); + + writenfc(addr, &host->regs->flash_addr); + writenfc(NFC_ADDR, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, addr); +} + +/* + * This function requests the NANDFC to initiate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + */ +static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + if (spare_only) + pr_debug("send_prog_page (%d)\n", spare_only); + + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + int i; + /* + * The controller copies the 64 bytes of spare data from + * the first 16 bytes of each of the 4 64 byte spare buffers. + * Copy the contiguous data starting in spare_area[0] to + * the four spare area buffers. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[0][i * 16]; + void __iomem *dst = &host->regs->spare_area[i][0]; + + mxc_nand_memcpy32(dst, src, 16); + } + } + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readnfc(&host->regs->config1); + if (spare_only) + config1 |= NFC_CONFIG1_SP_EN; + else + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); + } + + writenfc(NFC_INPUT, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); +} + +/* + * Requests NANDFC to initiate the transfer of data from the + * NAND device into in the NANDFC ram buffer. + */ +static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + pr_debug("send_read_page (%d)\n", spare_only); + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(buf_id, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + tmp |= NFC_V3_CONFIG1_RBA(buf_id); + writenfc(tmp, &host->regs->config1); +#endif + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readnfc(&host->regs->config1); + if (spare_only) + config1 |= NFC_CONFIG1_SP_EN; + else + config1 &= ~NFC_CONFIG1_SP_EN; + writenfc(config1, &host->regs->config1); + } + + writenfc(NFC_OUTPUT, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only); + + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + int i; + + /* + * The controller copies the 64 bytes of spare data to + * the first 16 bytes of each of the 4 spare buffers. + * Make the data contiguous starting in spare_area[0]. + */ + for (i = 1; i < 4; i++) { + void __iomem *src = &host->regs->spare_area[i][0]; + void __iomem *dst = &host->regs->spare_area[0][i * 16]; + + mxc_nand_memcpy32(dst, src, 16); + } + } +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id(struct mxc_nand_host *host) +{ + uint32_t tmp; + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* NANDFC buffer 0 is used for device ID output */ + writenfc(0x0, &host->regs->buf_addr); +#elif defined(MXC_NFC_V3_2) + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(tmp, &host->regs->config1); +#endif + + /* Read ID into main buffer */ + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); + + writenfc(NFC_ID, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); +} + +/* + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + */ +static uint16_t get_dev_status(struct mxc_nand_host *host) +{ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + void __iomem *main_buf = host->regs->main_area[1]; + uint32_t store; +#endif + uint32_t ret, tmp; + /* Issue status request to NAND device */ + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* store the main area1 first word, later do recovery */ + store = readl(main_buf); + /* NANDFC buffer 1 is used for device status */ + writenfc(1, &host->regs->buf_addr); +#endif + + /* Read status into main buffer */ + tmp = readnfc(&host->regs->config1); + tmp &= ~NFC_CONFIG1_SP_EN; + writenfc(tmp, &host->regs->config1); + + writenfc(NFC_STATUS, &host->regs->operation); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0); + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + /* + * Status is placed in first word of main buffer + * get status, then recovery area 1 data + */ + ret = readw(main_buf); + writel(store, main_buf); +#elif defined(MXC_NFC_V3_2) + ret = readnfc(&host->regs->config1) >> 16; +#endif + + return ret; +} + +/* This function is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + uint16_t tmp = readnfc(&host->regs->config1); + + if (on) + tmp |= NFC_V1_V2_CONFIG1_ECC_EN; + else + tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; + writenfc(tmp, &host->regs->config1); +#elif defined(MXC_NFC_V3_2) + uint32_t tmp = readnfc(&host->ip_regs->config2); + + if (on) + tmp |= NFC_V3_CONFIG2_ECC_EN; + else + tmp &= ~NFC_V3_CONFIG2_ECC_EN; + writenfc(tmp, &host->ip_regs->config2); +#endif +} + +#ifdef CONFIG_MXC_NAND_HWECC +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *bufpoi = buf; + int i, toread; + + pr_debug("%s: Reading OOB area of page %u to oob %p\n", + __func__, page, buf); + + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.prepad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + bufpoi += chip->ecc.bytes; + host->col_addr += chip->ecc.bytes; + length -= chip->ecc.bytes; + + toread = min_t(int, length, chip->ecc.postpad); + if (toread) { + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, + mtd->writesize + chip->ecc.prepad, page); + bufpoi = buf + chip->ecc.prepad; + length = mtd->oobsize - chip->ecc.prepad; + for (i = 0; i < chip->ecc.steps; i++) { + toread = min_t(int, length, chip->ecc.bytes); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += eccpitch; + length -= eccpitch; + host->col_addr += chip->ecc.postpad + chip->ecc.prepad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 1; +} + +static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int oob_required, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + _mxc_nand_enable_hwecc(mtd, 1); + + return 0; +} + +static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, + int oob_required, + int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + pr_debug("Reading page %u to buf %p oob %p\n", + page, buf, oob); + + /* first read the data area and the available portion of OOB */ + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + int stat; + + host->col_addr = n * eccsize; + + chip->read_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + n = mtd->oobsize - (oob - chip->oob_poi); + if (n) + chip->read_buf(mtd, oob, n); + + /* Then switch ECC off and read the OOB area to get the ECC code */ + _mxc_nand_enable_hwecc(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); + eccsteps = chip->ecc.steps; + oob = chip->oob_poi + chip->ecc.prepad; + for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { + host->col_addr = mtd->writesize + + n * eccpitch + + chip->ecc.prepad; + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes + chip->ecc.postpad; + } + _mxc_nand_enable_hwecc(mtd, 1); + return 0; +} + +static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int length = mtd->oobsize; + int i, len, status, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + for (i = 0; i < steps; i++) { + len = min_t(int, length, eccpitch); + + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + host->col_addr += chip->ecc.prepad + chip->ecc.postpad; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *oob = chip->oob_poi; + int steps, size; + int n; + + for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { + host->col_addr = n * eccsize; + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + host->col_addr += eccbytes; + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); + return 0; +} + +static int mxc_nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int i, n, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + for (i = n = 0; + eccsteps; + n++, eccsteps--, i += eccbytes, p += eccsize) { + host->col_addr = n * eccsize; + + chip->write_buf(mtd, p, eccsize); + + host->col_addr = mtd->writesize + n * eccpitch; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); + return 0; +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint32_t ecc_status = readl(&host->regs->ecc_status_result); + int subpages = mtd->writesize / nand_chip->subpagesize; + int pg2blk_shift = nand_chip->phys_erase_shift - + nand_chip->page_shift; + + do { + if ((ecc_status & 0xf) > 4) { + static int last_bad = -1; + + if (last_bad != host->page_addr >> pg2blk_shift) { + last_bad = host->page_addr >> pg2blk_shift; + printk(KERN_DEBUG + "MXC_NAND: HWECC uncorrectable ECC error" + " in block %u page %u subpage %d\n", + last_bad, host->page_addr, + mtd->writesize / nand_chip->subpagesize + - subpages); + } + return -EBADMSG; + } + ecc_status >>= 4; + subpages--; + } while (subpages > 0); + + return 0; +} +#else +#define mxc_nand_read_page_syndrome NULL +#define mxc_nand_read_page_raw_syndrome NULL +#define mxc_nand_read_oob_syndrome NULL +#define mxc_nand_write_page_syndrome NULL +#define mxc_nand_write_page_raw_syndrome NULL +#define mxc_nand_write_oob_syndrome NULL + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + uint16_t ecc_status = readnfc(&host->regs->ecc_status_result); + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -EBADMSG; + } + + return 0; +} +#endif + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} +#endif + +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint8_t ret = 0; + uint16_t col; + uint16_t __iomem *main_buf = + (uint16_t __iomem *)host->regs->main_area[0]; + uint16_t __iomem *spare_buf = + (uint16_t __iomem *)host->regs->spare_area[0]; + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + /* Get column for 16-bit access */ + col = host->col_addr >> 1; + + /* If we are accessing the spare region */ + if (host->spare_only) + nfc_word.word = readw(&spare_buf[col]); + else + nfc_word.word = readw(&main_buf[col]); + + /* Pick upper/lower byte of word from RAM buffer */ + ret = nfc_word.bytes[host->col_addr & 0x1]; + + /* Update saved column address */ + if (nand_chip->options & NAND_BUSWIDTH_16) + host->col_addr += 2; + else + host->col_addr++; + + return ret; +} + +static uint16_t mxc_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t col, ret; + uint16_t __iomem *p; + + pr_debug("mxc_nand_read_word(col = %d)\n", host->col_addr); + + col = host->col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + if (col < mtd->writesize) { + p = (uint16_t __iomem *)(host->regs->main_area[0] + + (col >> 1)); + } else { + p = (uint16_t __iomem *)(host->regs->spare_area[0] + + ((col - mtd->writesize) >> 1)); + } + + if (col & 1) { + union { + uint16_t word; + uint8_t bytes[2]; + } nfc_word[3]; + + nfc_word[0].word = readw(p); + nfc_word[1].word = readw(p + 1); + + nfc_word[2].bytes[0] = nfc_word[0].bytes[1]; + nfc_word[2].bytes[1] = nfc_word[1].bytes[0]; + + ret = nfc_word[2].word; + } else { + ret = readw(p); + } + + /* Update saved column address */ + host->col_addr = col + 2; + + return ret; +} + +/* + * Write data of length len to buffer buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int n, col, i = 0; + + pr_debug("mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, + len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + pr_debug("%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + pr_debug("%s:%d: p = %p\n", __func__, + __LINE__, p); + + if (((col | (unsigned long)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + nfc_word.bytes[col & 3] = buf[i++]; + n--; + col++; + + writel(nfc_word.word, p); + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + pr_debug("%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __func__, __LINE__, n, m, i, col); + + mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * Read the data buffer from the NAND Flash. To read the data from NAND + * Flash first the data output cycle is initiated by the NFC, which copies + * the data to RAMbuffer. This data of length len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int n, col, i = 0; + + pr_debug("mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, + len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n > 0) { + void __iomem *p; + + if (col < mtd->writesize) { + p = host->regs->main_area[0] + (col & ~3); + } else { + p = host->regs->spare_area[0] - + mtd->writesize + (col & ~3); + } + + if (((col | (int)&buf[i]) & 3) || n < 4) { + union { + uint32_t word; + uint8_t bytes[4]; + } nfc_word; + + nfc_word.word = readl(p); + buf[i++] = nfc_word.bytes[col & 3]; + n--; + col++; + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + mxc_nand_memcpy32((uint32_t *)&buf[i], p, m); + + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* + * This function is used by upper layer for select and + * deselect of the NAND chip + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + switch (chip) { + case -1: + /* TODO: Disable the NFC clock */ + if (host->clk_act) + host->clk_act = 0; + break; + case 0: + /* TODO: Enable the NFC clock */ + if (!host->clk_act) + host->clk_act = 1; + break; + + default: + break; + } +} + +/* + * Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash + */ +void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + host->status_request = false; + + /* Command pre-processing step */ + switch (command) { + + case NAND_CMD_STATUS: + host->col_addr = 0; + host->status_request = true; + break; + + case NAND_CMD_READ0: + host->page_addr = page_addr; + host->col_addr = column; + host->spare_only = false; + break; + + case NAND_CMD_READOOB: + host->col_addr = column; + host->spare_only = true; + if (host->pagesize_2k) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + /* + * before sending SEQIN command for partial write, + * we need read one page out. FSL NFC does not support + * partial write. It always sends out 512+ecc+512+ecc + * for large page nand flash. But for small page nand + * flash, it does support SPARE ONLY operation. + */ + if (host->pagesize_2k) { + /* call ourself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + + host->col_addr = column - mtd->writesize; + host->spare_only = true; + + /* Set program pointer to spare region */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READOOB); + } else { + host->spare_only = false; + host->col_addr = column; + + /* Set program pointer to page start */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READ0); + } + break; + + case NAND_CMD_PAGEPROG: + send_prog_page(host, 0, host->spare_only); + + if (host->pagesize_2k && is_mxc_nfc_1()) { + /* data in 4 areas */ + send_prog_page(host, 1, host->spare_only); + send_prog_page(host, 2, host->spare_only); + send_prog_page(host, 3, host->spare_only); + } + + break; + } + + /* Write out the command to the device. */ + send_cmd(host, command); + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers perform + * a read/write buffer operation, we will use the saved + * column address to index into the full page. + */ + send_addr(host, 0); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + u32 page_mask = nand_chip->pagemask; + do { + send_addr(host, page_addr & 0xFF); + page_addr >>= 8; + page_mask >>= 8; + } while (page_mask); + } + + /* Command post-processing step */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART); + /* read for each AREA */ + send_read_page(host, 0, host->spare_only); + if (is_mxc_nfc_1()) { + send_read_page(host, 1, host->spare_only); + send_read_page(host, 2, host->spare_only); + send_read_page(host, 3, host->spare_only); + } + } else { + send_read_page(host, 0, host->spare_only); + } + break; + + case NAND_CMD_READID: + host->col_addr = 0; + send_read_id(host); + break; + + case NAND_CMD_PAGEPROG: + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +#endif + +int board_nand_init(struct nand_chip *this) +{ + struct mtd_info *mtd; +#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + uint32_t tmp; +#endif + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + this->bbt_options |= NAND_BBT_USE_FLASH; + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; +#endif + + /* structures must be linked */ + mtd = &this->mtd; + host->nand = this; + + /* 5 us command delay time */ + this->chip_delay = 5; + + nand_set_controller_data(this, host); + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + + host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; +#ifdef MXC_NFC_V3_2 + host->ip_regs = + (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; +#endif + host->clk_act = 1; + +#ifdef CONFIG_MXC_NAND_HWECC + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + if (is_mxc_nfc_21() || is_mxc_nfc_32()) { + this->ecc.mode = NAND_ECC_HW_SYNDROME; + this->ecc.read_page = mxc_nand_read_page_syndrome; + this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; + this->ecc.read_oob = mxc_nand_read_oob_syndrome; + this->ecc.write_page = mxc_nand_write_page_syndrome; + this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome; + this->ecc.write_oob = mxc_nand_write_oob_syndrome; + this->ecc.bytes = 9; + this->ecc.prepad = 7; + } else { + this->ecc.mode = NAND_ECC_HW; + } + + if (is_mxc_nfc_1()) + this->ecc.strength = 1; + else + this->ecc.strength = 4; + + host->pagesize_2k = 0; + + this->ecc.size = 512; + _mxc_nand_enable_hwecc(mtd, 1); +#else + this->ecc.layout = &nand_soft_eccoob; + this->ecc.mode = NAND_ECC_SOFT; + _mxc_nand_enable_hwecc(mtd, 0); +#endif + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* NAND bus width determines access functions used by upper layer */ + if (is_16bit_nand()) + this->options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_SYS_NAND_LARGEPAGE + host->pagesize_2k = 1; + this->ecc.layout = &nand_hw_eccoob2k; +#else + host->pagesize_2k = 0; + this->ecc.layout = &nand_hw_eccoob; +#endif + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#ifdef MXC_NFC_V2_1 + tmp = readnfc(&host->regs->config1); + tmp |= NFC_V2_CONFIG1_ONE_CYCLE; + tmp |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(tmp, &host->regs->config1); + if (host->pagesize_2k) + writenfc(64/2, &host->regs->spare_area_size); + else + writenfc(16/2, &host->regs->spare_area_size); +#endif + + /* + * preset operation + * Unlock the internal RAM Buffer + */ + writenfc(0x2, &host->regs->config); + + /* Blocks to be unlocked */ + writenfc(0x0, &host->regs->unlockstart_blkaddr); + /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the + * unlockend_blkaddr, but the magic 0x4000 does not always work + * when writing more than some 32 megabytes (on 2k page nands) + * However 0xFFFF doesn't seem to have this kind + * of limitation (tried it back and forth several times). + * The linux kernel driver sets this to 0xFFFF for the v2 controller + * only, but probably this was not tested there for v1. + * The very same limitation seems to apply to this kernel driver. + * This might be NAND chip specific and the i.MX31 datasheet is + * extremely vague about the semantics of this register. + */ + writenfc(0xFFFF, &host->regs->unlockend_blkaddr); + + /* Unlock Block Command for given address range */ + writenfc(0x4, &host->regs->wrprot); +#elif defined(MXC_NFC_V3_2) + writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1); + writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc); + + /* Unlock the internal RAM Buffer */ + writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + &host->ip_regs->wrprot); + + /* Blocks to be unlocked */ + for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++) + writenfc(0x0 | 0xFFFF << 16, + &host->ip_regs->wrprot_unlock_blkaddr[tmp]); + + writenfc(0, &host->ip_regs->ipc); + + tmp = readnfc(&host->ip_regs->config2); + tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK | + NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK); + tmp |= NFC_V3_CONFIG2_ONE_CYCLE; + + if (host->pagesize_2k) { + tmp |= NFC_V3_CONFIG2_SPAS(64/2); + tmp |= NFC_V3_CONFIG2_PS_2048; + } else { + tmp |= NFC_V3_CONFIG2_SPAS(16/2); + tmp |= NFC_V3_CONFIG2_PS_512; + } + + writenfc(tmp, &host->ip_regs->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(this->options & NAND_BUSWIDTH_16)) + tmp |= NFC_V3_CONFIG3_FW8; + + writenfc(tmp, &host->ip_regs->config3); + + writenfc(0, &host->ip_regs->delay_line); +#endif + + return 0; +} diff --git a/drivers/mtd/nand/raw/mxc_nand.h b/drivers/mtd/nand/raw/mxc_nand.h new file mode 100644 index 0000000000..1c7f3a2e22 --- /dev/null +++ b/drivers/mtd/nand/raw/mxc_nand.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (c) 2009 Magnus Lilja + */ + +#ifndef __MXC_NAND_H +#define __MXC_NAND_H + +/* + * Register map and bit definitions for the Freescale NAND Flash Controller + * present in various i.MX devices. + * + * MX31 and MX27 have version 1, which has: + * 4 512-byte main buffers and + * 4 16-byte spare buffers + * to support up to 2K byte pagesize nand. + * Reading or writing a 2K page requires 4 FDI/FDO cycles. + * + * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which + * have: + * 8 512-byte main buffers and + * 8 64-byte spare buffers + * to support up to 4K byte pagesize nand. + * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle. + * Also some of registers are moved and/or changed meaning as seen below. + */ +#if defined(CONFIG_MX27) || defined(CONFIG_MX31) +#define MXC_NFC_V1 +#define is_mxc_nfc_1() 1 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) +#define MXC_NFC_V2_1 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 1 +#define is_mxc_nfc_32() 0 +#elif defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define MXC_NFC_V3 +#define MXC_NFC_V3_2 +#define is_mxc_nfc_1() 0 +#define is_mxc_nfc_21() 0 +#define is_mxc_nfc_32() 1 +#else +#error "MXC NFC implementation not supported" +#endif +#define is_mxc_nfc_3() is_mxc_nfc_32() + +#if defined(MXC_NFC_V1) +#define NAND_MXC_NR_BUFS 4 +#define NAND_MXC_SPARE_BUF_SIZE 16 +#define NAND_MXC_REG_OFFSET 0xe00 +#define NAND_MXC_2K_MULTI_CYCLE +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) +#define NAND_MXC_NR_BUFS 8 +#define NAND_MXC_SPARE_BUF_SIZE 64 +#define NAND_MXC_REG_OFFSET 0x1e00 +#endif + +struct mxc_nand_regs { + u8 main_area[NAND_MXC_NR_BUFS][0x200]; + u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE]; + /* + * reserved size is offset of nfc registers + * minus total main and spare sizes + */ + u8 reserved1[NAND_MXC_REG_OFFSET + - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)]; +#if defined(MXC_NFC_V1) + u16 buf_size; + u16 reserved2; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u16 ecc_status_result; + u16 rsltmain_area; + u16 rsltspare_area; + u16 wrprot; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 nf_wrprst; + u16 config1; + u16 config2; +#elif defined(MXC_NFC_V2_1) + u16 reserved2[2]; + u16 buf_addr; + u16 flash_addr; + u16 flash_cmd; + u16 config; + u32 ecc_status_result; + u16 spare_area_size; + u16 wrprot; + u16 reserved3[2]; + u16 nf_wrprst; + u16 config1; + u16 config2; + u16 reserved4; + u16 unlockstart_blkaddr; + u16 unlockend_blkaddr; + u16 unlockstart_blkaddr1; + u16 unlockend_blkaddr1; + u16 unlockstart_blkaddr2; + u16 unlockend_blkaddr2; + u16 unlockstart_blkaddr3; + u16 unlockend_blkaddr3; +#elif defined(MXC_NFC_V3_2) + u32 flash_cmd; + u32 flash_addr[12]; + u32 config1; + u32 ecc_status_result; + u32 status_sum; + u32 launch; +#endif +}; + +#ifdef MXC_NFC_V3_2 +struct mxc_nand_ip_regs { + u32 wrprot; + u32 wrprot_unlock_blkaddr[8]; + u32 config2; + u32 config3; + u32 ipc; + u32 err_addr; + u32 delay_line; +}; +#endif + +/* Set FCMD to 1, rest to 0 for Command operation */ +#define NFC_CMD 0x1 + +/* Set FADD to 1, rest to 0 for Address operation */ +#define NFC_ADDR 0x2 + +/* Set FDI to 1, rest to 0 for Input operation */ +#define NFC_INPUT 0x4 + +/* Set FDO to 001, rest to 0 for Data Output operation */ +#define NFC_OUTPUT 0x8 + +/* Set FDO to 010, rest to 0 for Read ID operation */ +#define NFC_ID 0x10 + +/* Set FDO to 100, rest to 0 for Read Status operation */ +#define NFC_STATUS 0x20 + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define NFC_CONFIG1_SP_EN (1 << 2) +#define NFC_CONFIG1_RST (1 << 6) +#define NFC_CONFIG1_CE (1 << 7) +#elif defined(MXC_NFC_V3_2) +#define NFC_CONFIG1_SP_EN (1 << 0) +#define NFC_CONFIG1_CE (1 << 1) +#define NFC_CONFIG1_RST (1 << 2) +#endif +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) +#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) +#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7) +#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9) +#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9) +#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) +#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) + +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +#define operation config2 +#define readnfc readw +#define writenfc writew +#elif defined(MXC_NFC_V3_2) +#define operation launch +#define readnfc readl +#define writenfc writel +#endif + +#endif /* __MXC_NAND_H */ diff --git a/drivers/mtd/nand/raw/mxc_nand_spl.c b/drivers/mtd/nand/raw/mxc_nand_spl.c new file mode 100644 index 0000000000..6c03db8428 --- /dev/null +++ b/drivers/mtd/nand/raw/mxc_nand_spl.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2009 + * Magnus Lilja + * + * (C) Copyright 2008 + * Maxim Artamonov, + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr at denx.de. + */ + +#include +#include +#include +#include +#include "mxc_nand.h" + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR; +#elif defined(MXC_NFC_V3_2) +static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI; +static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR; +#endif + +static void nfc_wait_ready(void) +{ + uint32_t tmp; + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc->config2); + tmp &= ~NFC_V1_V2_CONFIG2_INT; + writenfc(tmp, &nfc->config2); +#elif defined(MXC_NFC_V3_2) + while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT)) + ; + + /* Reset interrupt flag */ + tmp = readnfc(&nfc_ip->ipc); + tmp &= ~NFC_V3_IPC_INT; + writenfc(tmp, &nfc_ip->ipc); +#endif +} + +static void nfc_nand_init(void) +{ +#if defined(MXC_NFC_V3_2) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int tmp; + + tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK | + NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) | + NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) | + NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN | + NFC_V3_CONFIG2_ONE_CYCLE; + if (CONFIG_SYS_NAND_PAGE_SIZE == 4096) + tmp |= NFC_V3_CONFIG2_PS_4096; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048) + tmp |= NFC_V3_CONFIG2_PS_2048; + else if (CONFIG_SYS_NAND_PAGE_SIZE == 512) + tmp |= NFC_V3_CONFIG2_PS_512; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + tmp |= NFC_V3_CONFIG2_ECC_MODE_8; + else + tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8; + writenfc(tmp, &nfc_ip->config2); + + tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); +#ifndef CONFIG_SYS_NAND_BUSWIDTH_16 + tmp |= NFC_V3_CONFIG3_FW8; +#endif + writenfc(tmp, &nfc_ip->config3); + + writenfc(0, &nfc_ip->delay_line); +#elif defined(MXC_NFC_V2_1) + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int config1; + + writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size); + + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN | + NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE | + NFC_V2_CONFIG1_FP_INT; + /* + * if spare size is larger that 16 bytes per 512 byte hunk + * then use 8 symbol correction instead of 4 + */ + if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16) + config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4; + else + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + writenfc(config1, &nfc->config1); +#elif defined(MXC_NFC_V1) + /* unlocking RAM Buff */ + writenfc(0x2, &nfc->config); + + /* hardware ECC checking and correct */ + writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK, + &nfc->config1); +#endif +} + +static void nfc_nand_command(unsigned short command) +{ + writenfc(command, &nfc->flash_cmd); + writenfc(NFC_CMD, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_address(unsigned short address) +{ + writenfc(address, &nfc->flash_addr); + writenfc(NFC_ADDR, &nfc->operation); + nfc_wait_ready(); +} + +static void nfc_nand_page_address(unsigned int page_address) +{ + unsigned int page_count; + + nfc_nand_address(0x00); + + /* code only for large page flash */ + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_address(0x00); + + page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE; + + if (page_address <= page_count) { + page_count--; /* transform 0x01000000 to 0x00ffffff */ + do { + nfc_nand_address(page_address & 0xff); + page_address = page_address >> 8; + page_count = page_count >> 8; + } while (page_count); + } + + nfc_nand_address(0x00); +} + +static void nfc_nand_data_output(void) +{ +#ifdef NAND_MXC_2K_MULTI_CYCLE + int i; +#endif + +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); +#ifdef NAND_MXC_2K_MULTI_CYCLE + /* + * This NAND controller requires multiple input commands + * for pages larger than 512 bytes. + */ + for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) { + writenfc(i, &nfc->buf_addr); + writenfc(NFC_OUTPUT, &nfc->operation); + nfc_wait_ready(); + } +#endif +} + +static int nfc_nand_check_ecc(void) +{ +#if defined(MXC_NFC_V1) + u16 ecc_status = readw(&nfc->ecc_status_result); + return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2; +#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) + u32 ecc_status = readl(&nfc->ecc_status_result); + int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512; + int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4; + int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512; + + do { + if ((ecc_status & 0xf) > err_limit) + return 1; + ecc_status >>= 4; + } while (--subpages); + + return 0; +#endif +} + +static void nfc_nand_read_page(unsigned int page_address) +{ + /* read in first 0 buffer */ +#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) + writenfc(0, &nfc->buf_addr); +#elif defined(MXC_NFC_V3_2) + int config1 = readnfc(&nfc->config1); + config1 &= ~NFC_V3_CONFIG1_RBA_MASK; + writenfc(config1, &nfc->config1); +#endif + nfc_nand_command(NAND_CMD_READ0); + nfc_nand_page_address(page_address); + + if (CONFIG_SYS_NAND_PAGE_SIZE > 512) + nfc_nand_command(NAND_CMD_READSTART); + + nfc_nand_data_output(); /* fill the main buffer 0 */ +} + +static int nfc_read_page(unsigned int page_address, unsigned char *buf) +{ + int i; + u32 *src; + u32 *dst; + + nfc_nand_read_page(page_address); + + if (nfc_nand_check_ecc()) + return -EBADMSG; + + src = (u32 *)&nfc->main_area[0][0]; + dst = (u32 *)buf; + + /* main copy loop from NAND-buffer to SDRAM memory */ + for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) { + writel(readl(src), dst); + src++; + dst++; + } + + return 0; +} + +static int is_badblock(int pagenumber) +{ + int page = pagenumber; + u32 badblock; + u32 *src; + + /* Check the first two pages for bad block markers */ + for (page = pagenumber; page < pagenumber + 2; page++) { + nfc_nand_read_page(page); + + src = (u32 *)&nfc->spare_area[0][0]; + + /* + * IMPORTANT NOTE: The nand flash controller uses a non- + * standard layout for large page devices. This can + * affect the position of the bad block marker. + */ + /* Get the bad block marker */ + badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]); + badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4); + badblock &= 0xff; + + /* bad block marker verify */ + if (badblock != 0xff) + return 1; /* potential bad block */ + } + + return 0; +} + +int nand_spl_load_image(uint32_t from, unsigned int size, void *buf) +{ + int i; + unsigned int page; + unsigned int maxpages = CONFIG_SYS_NAND_SIZE / + CONFIG_SYS_NAND_PAGE_SIZE; + + nfc_nand_init(); + + /* Convert to page number */ + page = from / CONFIG_SYS_NAND_PAGE_SIZE; + i = 0; + + size = roundup(size, CONFIG_SYS_NAND_PAGE_SIZE); + while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) { + if (nfc_read_page(page, buf) < 0) + return -1; + + page++; + i++; + buf = buf + CONFIG_SYS_NAND_PAGE_SIZE; + + /* + * Check if we have crossed a block boundary, and if so + * check for bad block. + */ + if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { + /* + * Yes, new block. See if this block is good. If not, + * loop until we find a good block. + */ + while (is_badblock(page)) { + page = page + CONFIG_SYS_NAND_PAGE_COUNT; + /* Check i we've reached the end of flash. */ + if (page >= maxpages) + return -1; + } + } + } + + return 0; +} + +#ifndef CONFIG_SPL_FRAMEWORK +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* + * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must + * be aligned to full pages + */ + if (!nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { + /* Copy from NAND successful, start U-Boot */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + uboot(); + } else { + /* Unrecoverable error when copying from NAND */ + hang(); + } +} +#endif + +void nand_init(void) {} +void nand_deselect(void) {} diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c new file mode 100644 index 0000000000..e3341812a2 --- /dev/null +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -0,0 +1,1302 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale i.MX28 NAND flash driver + * + * Copyright (C) 2011 Marek Vasut + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxs_nand.h" + +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 + +#if (defined(CONFIG_MX6) || defined(CONFIG_MX7)) +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 +#else +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 +#endif +#define MXS_NAND_METADATA_SIZE 10 +#define MXS_NAND_BITS_PER_ECC_LEVEL 13 + +#if !defined(CONFIG_SYS_CACHELINE_SIZE) || CONFIG_SYS_CACHELINE_SIZE < 32 +#define MXS_NAND_COMMAND_BUFFER_SIZE 32 +#else +#define MXS_NAND_COMMAND_BUFFER_SIZE CONFIG_SYS_CACHELINE_SIZE +#endif + +#define MXS_NAND_BCH_TIMEOUT 10000 + +struct nand_ecclayout fake_ecc_layout; + +/* + * Cache management functions + */ +#ifndef CONFIG_SYS_DCACHE_OFF +static void mxs_nand_flush_data_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->data_buf; + + flush_dcache_range(addr, addr + info->data_buf_size); +} + +static void mxs_nand_inval_data_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->data_buf; + + invalidate_dcache_range(addr, addr + info->data_buf_size); +} + +static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) +{ + uint32_t addr = (uint32_t)info->cmd_buf; + + flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE); +} +#else +static inline void mxs_nand_flush_data_buf(struct mxs_nand_info *info) {} +static inline void mxs_nand_inval_data_buf(struct mxs_nand_info *info) {} +static inline void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) {} +#endif + +static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) +{ + struct mxs_dma_desc *desc; + + if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) { + printf("MXS NAND: Too many DMA descriptors requested\n"); + return NULL; + } + + desc = info->desc[info->desc_index]; + info->desc_index++; + + return desc; +} + +static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) +{ + int i; + struct mxs_dma_desc *desc; + + for (i = 0; i < info->desc_index; i++) { + desc = info->desc[i]; + memset(desc, 0, sizeof(struct mxs_dma_desc)); + desc->address = (dma_addr_t)desc; + } + + info->desc_index = 0; +} + +static uint32_t mxs_nand_aux_status_offset(void) +{ + return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; +} + +static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, + uint32_t page_data_size) +{ + uint32_t chunk_data_size_in_bits = geo->ecc_chunk_size * 8; + uint32_t chunk_ecc_size_in_bits = geo->ecc_strength * geo->gf_len; + uint32_t chunk_total_size_in_bits; + uint32_t block_mark_chunk_number; + uint32_t block_mark_chunk_bit_offset; + uint32_t block_mark_bit_offset; + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = page_data_size * 8; + + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + block_mark_chunk_bit_offset = block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) + return -EINVAL; + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + geo->block_mark_byte_offset = block_mark_bit_offset >> 3; + geo->block_mark_bit_offset = block_mark_bit_offset & 0x7; + + return 0; +} + +static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, + struct mtd_info *mtd, + unsigned int ecc_strength, + unsigned int ecc_step) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + + switch (ecc_step) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + return -EINVAL; + } + + geo->ecc_chunk_size = ecc_step; + geo->ecc_strength = round_up(ecc_strength, 2); + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) + return -EINVAL; + + if (geo->ecc_strength > nand_info->max_ecc_strength_supported) + return -EINVAL; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + return 0; +} + +static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, + struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + + /* The default for the length of Galois Field. */ + geo->gf_len = 13; + + /* The default for chunk size. */ + geo->ecc_chunk_size = 512; + + if (geo->ecc_chunk_size < mtd->oobsize) { + geo->gf_len = 14; + geo->ecc_chunk_size *= 2; + } + + if (mtd->oobsize > geo->ecc_chunk_size) { + printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", + geo->ecc_chunk_size); + return -EINVAL; + } + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* + * Determine the ECC layout with the formula: + * ECC bits per chunk = (total page spare data bits) / + * (bits per ECC level) / (chunks per page) + * where: + * total page spare data bits = + * (page oob size - meta data size) * (bits per byte) + */ + geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + + geo->ecc_strength = min(round_down(geo->ecc_strength, 2), + nand_info->max_ecc_strength_supported); + + return 0; +} + +/* + * Wait for BCH complete IRQ and clear the IRQ + */ +static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info) +{ + int timeout = MXS_NAND_BCH_TIMEOUT; + int ret; + + ret = mxs_wait_mask_set(&nand_info->bch_regs->hw_bch_ctrl_reg, + BCH_CTRL_COMPLETE_IRQ, timeout); + + writel(BCH_CTRL_COMPLETE_IRQ, &nand_info->bch_regs->hw_bch_ctrl_clr); + + return ret; +} + +/* + * This is the function that we install in the cmd_ctrl function pointer of the + * owning struct nand_chip. The only functions in the reference implementation + * that use these functions pointers are cmdfunc and select_chip. + * + * In this driver, we implement our own select_chip, so this function will only + * be called by the reference implementation's cmdfunc. For this reason, we can + * ignore the chip enable bit and concentrate only on sending bytes to the NAND + * Flash. + */ +static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + /* + * If this condition is true, something is _VERY_ wrong in MTD + * subsystem! + */ + if (nand_info->cmd_queue_len == MXS_NAND_COMMAND_BUFFER_SIZE) { + printf("MXS NAND: Command queue too long\n"); + return; + } + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will + * deasert both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. + */ + if (ctrl & (NAND_ALE | NAND_CLE)) { + if (data != NAND_CMD_NONE) + nand_info->cmd_buf[nand_info->cmd_queue_len++] = data; + return; + } + + /* + * If control arrives here, MTD has deasserted both the ALE and CLE, + * which means it's ready to run an operation. Check if we have any + * bytes to send. + */ + if (nand_info->cmd_queue_len == 0) + return; + + /* Compile the DMA descriptor -- a descriptor that sends command. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->cmd_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + nand_info->cmd_queue_len; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_cmd_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: Error sending command\n"); + + mxs_nand_return_dma_descs(nand_info); + + /* Reset the command queue. */ + nand_info->cmd_queue_len = 0; +} + +/* + * Test if the NAND flash is ready. + */ +static int mxs_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + uint32_t tmp; + + tmp = readl(&nand_info->gpmi_regs->hw_gpmi_stat); + tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip); + + return tmp & 1; +} + +/* + * Select the NAND chip. + */ +static void mxs_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + + nand_info->cur_chip = chip; +} + +/* + * Handle block mark swapping. + * + * Note that, when this function is called, it doesn't know whether it's + * swapping the block mark, or swapping it *back* -- but it doesn't matter + * because the the operation is the same. + */ +static void mxs_nand_swap_block_mark(struct bch_geometry *geo, + uint8_t *data_buf, uint8_t *oob_buf) +{ + uint32_t bit_offset = geo->block_mark_bit_offset; + uint32_t buf_offset = geo->block_mark_byte_offset; + + uint32_t src; + uint32_t dst; + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + src = data_buf[buf_offset] >> bit_offset; + src |= data_buf[buf_offset + 1] << (8 - bit_offset); + + dst = oob_buf[0]; + + oob_buf[0] = src; + + data_buf[buf_offset] &= ~(0xff << bit_offset); + data_buf[buf_offset + 1] &= 0xff << bit_offset; + + data_buf[buf_offset] |= dst << bit_offset; + data_buf[buf_offset + 1] |= dst >> (8 - bit_offset); +} + +/* + * Read data from NAND. + */ +static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + /* Compile the DMA descriptor - a descriptor that reads data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* + * A DMA descriptor that waits for the command to end and the chip to + * become ready. + * + * I think we actually should *not* be waiting for the chip to become + * ready because, after all, we don't care. I think the original code + * did that and no one has re-thought it yet. + */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + memcpy(buf, nand_info->data_buf, length); + +rtn: + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Write data to NAND. + */ +static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int length) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + memcpy(nand_info->data_buf, buf, length); + + /* Compile the DMA descriptor - a descriptor that writes data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: DMA write error\n"); + + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Read a single byte from NAND. + */ +static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t buf; + mxs_nand_read_buf(mtd, &buf, 1); + return buf; +} + +/* + * Read a page from NAND. + */ +static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int oob_required, + int page) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + uint32_t corrected = 0, failed = 0; + uint8_t *status; + int i, ret; + + /* Compile the DMA descriptor - wait for ready. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - enable the BCH block and read. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_DECODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - disable the BCH block. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM; + + d->cmd.address = 0; + + mxs_dma_desc_append(channel, d); + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(nand_info); + if (ret) { + printf("MXS NAND: BCH read timeout\n"); + goto rtn; + } + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + + /* Read DMA completed, now do the mark swapping. */ + mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); + + /* Loop over status bytes, accumulating ECC status. */ + status = nand_info->oob_buf + mxs_nand_aux_status_offset(); + for (i = 0; i < geo->ecc_chunk_count; i++) { + if (status[i] == 0x00) + continue; + + if (status[i] == 0xff) + continue; + + if (status[i] == 0xfe) { + failed++; + continue; + } + + corrected += status[i]; + } + + /* Propagate ECC status to the owning MTD. */ + mtd->ecc_stats.failed += failed; + mtd->ecc_stats.corrected += corrected; + + /* + * It's time to deliver the OOB bytes. See mxs_nand_ecc_read_oob() for + * details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the block + * mark to the caller's buffer. Note that, if block mark swapping was + * necessary, it has already been done, so we can rely on the first + * byte of the auxiliary buffer to contain the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->oob_poi[0] = nand_info->oob_buf[0]; + + memcpy(buf, nand_info->data_buf, mtd->writesize); + +rtn: + mxs_nand_return_dma_descs(nand_info); + + return ret; +} + +/* + * Write a page to NAND. + */ +static int mxs_nand_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *nand, const uint8_t *buf, + int oob_required, int page) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + memcpy(nand_info->data_buf, buf, mtd->writesize); + memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize); + + /* Handle block mark swapping. */ + mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); + + /* Compile the DMA descriptor - write data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_ENCODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Flush caches */ + mxs_nand_flush_data_buf(nand_info); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA write error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(nand_info); + if (ret) { + printf("MXS NAND: BCH write timeout\n"); + goto rtn; + } + +rtn: + mxs_nand_return_dma_descs(nand_info); + return 0; +} + +/* + * Read OOB from NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + int ret; + + if (ops->mode == MTD_OPS_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_read_oob(mtd, from, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Write OOB to NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + int ret; + + if (ops->mode == MTD_OPS_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_write_oob(mtd, to, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Mark a block bad in NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + int ret; + + nand_info->marking_block_bad = 1; + + ret = nand_info->hooked_block_markbad(mtd, ofs); + + nand_info->marking_block_bad = 0; + + return ret; +} + +/* + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark, so all + * write operations take measures to protect it. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * What we do for this specific read operation depends on whether we're doing + * "raw" read, or an ECC-based read. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + * + * Since MTD assumes the OOB is not covered by ECC, there is no pair of + * ECC-based/raw functions for reading or or writing the OOB. The fact that the + * caller wants an ECC-based or raw view of the page is not propagated down to + * this driver. + * + * Since our OOB *is* covered by ECC, we need this information. So, we hook the + * ecc.read_oob and ecc.write_oob function pointers in the owning + * struct mtd_info with our own functions. These hook functions set the + * raw_oob_mode field so that, when control finally arrives here, we'll know + * what to do. + */ +static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + + /* + * First, fill in the OOB buffer. If we're doing a raw read, we need to + * get the bytes from the physical page. If we're not doing a raw read, + * we need to fill the buffer with set bits. + */ + if (nand_info->raw_oob_mode) { + /* + * If control arrives here, we're doing a "raw" read. Send the + * command to read the conventional OOB and read it. + */ + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + nand->read_buf(mtd, nand->oob_poi, mtd->oobsize); + } else { + /* + * If control arrives here, we're not doing a "raw" read. Fill + * the OOB buffer with set bits and correct the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + mxs_nand_read_buf(mtd, nand->oob_poi, 1); + } + + return 0; + +} + +/* + * Write OOB data to NAND. + */ +static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + uint8_t block_mark = 0; + + /* + * There are fundamental incompatibilities between the i.MX GPMI NFC and + * the NAND Flash MTD model that make it essentially impossible to write + * the out-of-band bytes. + * + * We permit *ONE* exception. If the *intent* of writing the OOB is to + * mark a block bad, we can do that. + */ + + if (!nand_info->marking_block_bad) { + printf("NXS NAND: Writing OOB isn't supported\n"); + return -EIO; + } + + /* Write the block mark. */ + nand->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + nand->write_buf(mtd, &block_mark, 1); + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* Check if it worked. */ + if (nand->waitfunc(mtd, nand) & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/* + * Claims all blocks are good. + * + * In principle, this function is *only* called when the NAND Flash MTD system + * isn't allowed to keep an in-memory bad block table, so it is forced to ask + * the driver for bad block information. + * + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so + * this function is *only* called when we take it away. + * + * Thus, this function is only called when we want *all* blocks to look good, + * so it *always* return success. + */ +static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + return 0; +} + +static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + + if (chip->ecc.strength > 0 && chip->ecc.size > 0) + return mxs_nand_calc_ecc_layout_by_info(geo, mtd, + chip->ecc.strength, chip->ecc.size); + + if (nand_info->use_minimum_ecc || + mxs_nand_calc_ecc_layout(geo, mtd)) { + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + return mxs_nand_calc_ecc_layout_by_info(geo, mtd, + chip->ecc_strength_ds, chip->ecc_step_ds); + } + + return 0; +} + +/* + * At this point, the physical NAND Flash chips have been identified and + * counted, so we know the physical geometry. This enables us to make some + * important configuration decisions. + * + * The return value of this function propagates directly back to this driver's + * board_nand_init(). Anything other than zero will cause this driver to + * tear everything down and declare failure. + */ +int mxs_nand_setup_ecc(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; + struct mxs_bch_regs *bch_regs = nand_info->bch_regs; + uint32_t tmp; + int ret; + + ret = mxs_nand_set_geometry(mtd, geo); + if (ret) + return ret; + + mxs_nand_calc_mark_offset(geo, mtd->writesize); + + /* Configure BCH and set NFC geometry */ + mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + + /* Configure layout 0 */ + tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; + tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; + tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; + tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (geo->gf_len == 14 ? 1 : 0) << + BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; + writel(tmp, &bch_regs->hw_bch_flash0layout0); + + tmp = (mtd->writesize + mtd->oobsize) + << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; + tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; + tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (geo->gf_len == 14 ? 1 : 0) << + BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; + writel(tmp, &bch_regs->hw_bch_flash0layout1); + + /* Set *all* chip selects to use layout 0 */ + writel(0, &bch_regs->hw_bch_layoutselect); + + /* Enable BCH complete interrupt */ + writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); + + /* Hook some operations at the MTD level. */ + if (mtd->_read_oob != mxs_nand_hook_read_oob) { + nand_info->hooked_read_oob = mtd->_read_oob; + mtd->_read_oob = mxs_nand_hook_read_oob; + } + + if (mtd->_write_oob != mxs_nand_hook_write_oob) { + nand_info->hooked_write_oob = mtd->_write_oob; + mtd->_write_oob = mxs_nand_hook_write_oob; + } + + if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { + nand_info->hooked_block_markbad = mtd->_block_markbad; + mtd->_block_markbad = mxs_nand_hook_block_markbad; + } + + return 0; +} + +/* + * Allocate DMA buffers + */ +int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) +{ + uint8_t *buf; + const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; + + nand_info->data_buf_size = roundup(size, MXS_DMA_ALIGNMENT); + + /* DMA buffers */ + buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size); + if (!buf) { + printf("MXS NAND: Error allocating DMA buffers\n"); + return -ENOMEM; + } + + memset(buf, 0, nand_info->data_buf_size); + + nand_info->data_buf = buf; + nand_info->oob_buf = buf + NAND_MAX_PAGESIZE; + /* Command buffers */ + nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT, + MXS_NAND_COMMAND_BUFFER_SIZE); + if (!nand_info->cmd_buf) { + free(buf); + printf("MXS NAND: Error allocating command buffers\n"); + return -ENOMEM; + } + memset(nand_info->cmd_buf, 0, MXS_NAND_COMMAND_BUFFER_SIZE); + nand_info->cmd_queue_len = 0; + + return 0; +} + +/* + * Initializes the NFC hardware. + */ +int mxs_nand_init_dma(struct mxs_nand_info *info) +{ + int i = 0, j, ret = 0; + + info->desc = malloc(sizeof(struct mxs_dma_desc *) * + MXS_NAND_DMA_DESCRIPTOR_COUNT); + if (!info->desc) { + ret = -ENOMEM; + goto err1; + } + + /* Allocate the DMA descriptors. */ + for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { + info->desc[i] = mxs_dma_desc_alloc(); + if (!info->desc[i]) { + ret = -ENOMEM; + goto err2; + } + } + + /* Init the DMA controller. */ + mxs_dma_init(); + for (j = MXS_DMA_CHANNEL_AHB_APBH_GPMI0; + j <= MXS_DMA_CHANNEL_AHB_APBH_GPMI7; j++) { + ret = mxs_dma_init_channel(j); + if (ret) + goto err3; + } + + /* Reset the GPMI block. */ + mxs_reset_block(&info->gpmi_regs->hw_gpmi_ctrl0_reg); + mxs_reset_block(&info->bch_regs->hw_bch_ctrl_reg); + + /* + * Choose NAND mode, set IRQ polarity, disable write protection and + * select BCH ECC. + */ + clrsetbits_le32(&info->gpmi_regs->hw_gpmi_ctrl1, + GPMI_CTRL1_GPMI_MODE, + GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | + GPMI_CTRL1_BCH_MODE); + + return 0; + +err3: + for (--j; j >= MXS_DMA_CHANNEL_AHB_APBH_GPMI0; j--) + mxs_dma_release(j); +err2: + for (--i; i >= 0; i--) + mxs_dma_desc_free(info->desc[i]); + free(info->desc); +err1: + if (ret == -ENOMEM) + printf("MXS NAND: Unable to allocate DMA descriptors\n"); + return ret; +} + +int mxs_nand_init_spl(struct nand_chip *nand) +{ + struct mxs_nand_info *nand_info; + int err; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return -ENOMEM; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + err = mxs_nand_alloc_buffers(nand_info); + if (err) + return err; + + err = mxs_nand_init_dma(nand_info); + if (err) + return err; + + nand_set_controller_data(nand, nand_info); + + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->cmd_ctrl = mxs_nand_cmd_ctrl; + nand->dev_ready = mxs_nand_device_ready; + nand->select_chip = mxs_nand_select_chip; + + nand->read_byte = mxs_nand_read_byte; + nand->read_buf = mxs_nand_read_buf; + + nand->ecc.read_page = mxs_nand_ecc_read_page; + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.bytes = 9; + nand->ecc.size = 512; + nand->ecc.strength = 8; + + return 0; +} + +int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) +{ + struct mtd_info *mtd; + struct nand_chip *nand; + int err; + + nand = &nand_info->chip; + mtd = nand_to_mtd(nand); + err = mxs_nand_alloc_buffers(nand_info); + if (err) + return err; + + err = mxs_nand_init_dma(nand_info); + if (err) + goto err_free_buffers; + + memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; +#endif + + nand_set_controller_data(nand, nand_info); + nand->options |= NAND_NO_SUBPAGE_WRITE; + + if (nand_info->dev) + nand->flash_node = dev_of_offset(nand_info->dev); + + nand->cmd_ctrl = mxs_nand_cmd_ctrl; + + nand->dev_ready = mxs_nand_device_ready; + nand->select_chip = mxs_nand_select_chip; + nand->block_bad = mxs_nand_block_bad; + + nand->read_byte = mxs_nand_read_byte; + + nand->read_buf = mxs_nand_read_buf; + nand->write_buf = mxs_nand_write_buf; + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + goto err_free_buffers; + + if (mxs_nand_setup_ecc(mtd)) + goto err_free_buffers; + + nand->ecc.read_page = mxs_nand_ecc_read_page; + nand->ecc.write_page = mxs_nand_ecc_write_page; + nand->ecc.read_oob = mxs_nand_ecc_read_oob; + nand->ecc.write_oob = mxs_nand_ecc_write_oob; + + nand->ecc.layout = &fake_ecc_layout; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size; + nand->ecc.strength = nand_info->bch_geometry.ecc_strength; + + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + goto err_free_buffers; + + err = nand_register(0, mtd); + if (err) + goto err_free_buffers; + + return 0; + +err_free_buffers: + free(nand_info->data_buf); + free(nand_info->cmd_buf); + + return err; +} + +#ifndef CONFIG_NAND_MXS_DT +void board_nand_init(void) +{ + struct mxs_nand_info *nand_info; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + + /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ + if (is_mx6sx() || is_mx7()) + nand_info->max_ecc_strength_supported = 62; + else + nand_info->max_ecc_strength_supported = 40; + +#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC + nand_info->use_minimum_ecc = true; +#endif + + if (mxs_nand_init_ctrl(nand_info) < 0) + goto err; + + return; + +err: + free(nand_info); +} +#endif diff --git a/drivers/mtd/nand/raw/mxs_nand.h b/drivers/mtd/nand/raw/mxs_nand.h new file mode 100644 index 0000000000..4bd65cded9 --- /dev/null +++ b/drivers/mtd/nand/raw/mxs_nand.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * NXP GPMI NAND flash driver + * + * Copyright (C) 2018 Toradex + * Authors: + * Stefan Agner + */ + +#include +#include +#include +#include + +/** + * @gf_len: The length of Galois Field. (e.g., 13 or 14) + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ +struct bch_geometry { + unsigned int gf_len; + unsigned int ecc_strength; + unsigned int ecc_chunk_size; + unsigned int ecc_chunk_count; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + +struct mxs_nand_info { + struct nand_chip chip; + struct udevice *dev; + unsigned int max_ecc_strength_supported; + bool use_minimum_ecc; + int cur_chip; + + uint32_t cmd_queue_len; + uint32_t data_buf_size; + struct bch_geometry bch_geometry; + + uint8_t *cmd_buf; + uint8_t *data_buf; + uint8_t *oob_buf; + + uint8_t marking_block_bad; + uint8_t raw_oob_mode; + + struct mxs_gpmi_regs *gpmi_regs; + struct mxs_bch_regs *bch_regs; + + /* Functions with altered behaviour */ + int (*hooked_read_oob)(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops); + int (*hooked_write_oob)(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops); + int (*hooked_block_markbad)(struct mtd_info *mtd, + loff_t ofs); + + /* DMA descriptors */ + struct mxs_dma_desc **desc; + uint32_t desc_index; +}; + +int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info); +int mxs_nand_init_spl(struct nand_chip *nand); +int mxs_nand_setup_ecc(struct mtd_info *mtd); diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c new file mode 100644 index 0000000000..44dec5dedf --- /dev/null +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -0,0 +1,94 @@ +/* + * NXP GPMI NAND flash driver (DT initialization) + * + * Copyright (C) 2018 Toradex + * Authors: + * Stefan Agner + * + * Based on denali_dt.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include "mxs_nand.h" + +struct mxs_nand_dt_data { + unsigned int max_ecc_strength_supported; +}; + +static const struct mxs_nand_dt_data mxs_nand_imx6q_data = { + .max_ecc_strength_supported = 40, +}; + +static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { + .max_ecc_strength_supported = 62, +}; + +static const struct udevice_id mxs_nand_dt_ids[] = { + { + .compatible = "fsl,imx6q-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx6q_data, + }, + { + .compatible = "fsl,imx7d-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx7d_data, + }, + { /* sentinel */ } +}; + +static int mxs_nand_dt_probe(struct udevice *dev) +{ + struct mxs_nand_info *info = dev_get_priv(dev); + const struct mxs_nand_dt_data *data; + struct resource res; + int ret; + + data = (void *)dev_get_driver_data(dev); + if (data) + info->max_ecc_strength_supported = data->max_ecc_strength_supported; + + info->dev = dev; + + ret = dev_read_resource_byname(dev, "gpmi-nand", &res); + if (ret) + return ret; + + info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res)); + + + ret = dev_read_resource_byname(dev, "bch", &res); + if (ret) + return ret; + + info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res)); + + info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc"); + + return mxs_nand_init_ctrl(info); +} + +U_BOOT_DRIVER(mxs_nand_dt) = { + .name = "mxs-nand-dt", + .id = UCLASS_MTD, + .of_match = mxs_nand_dt_ids, + .probe = mxs_nand_dt_probe, + .priv_auto_alloc_size = sizeof(struct mxs_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(mxs_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize MXS NAND controller. (error %d)\n", + ret); +} diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c new file mode 100644 index 0000000000..2d7bbe83cc --- /dev/null +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Gateworks Corporation + * Author: Tim Harvey + */ +#include +#include +#include +#include "mxs_nand.h" + +static struct mtd_info *mtd; +static struct nand_chip nand_chip; + +static void mxs_nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + u32 timeo, time_start; + + /* write out the command to the device */ + chip->cmd_ctrl(mtd, command, NAND_CLE); + + /* Serially input address */ + if (column != -1) { + chip->cmd_ctrl(mtd, column, NAND_ALE); + chip->cmd_ctrl(mtd, column >> 8, NAND_ALE); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, NAND_ALE); + chip->cmd_ctrl(mtd, page_addr >> 8, NAND_ALE); + /* One more address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, NAND_ALE); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0); + + if (command == NAND_CMD_READ0) { + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0); + } + + /* wait for nand ready */ + ndelay(100); + timeo = (CONFIG_SYS_HZ * 20) / 1000; + time_start = get_timer(0); + while (get_timer(time_start) < timeo) { + if (chip->dev_ready(mtd)) + break; + } +} + +#if defined (CONFIG_SPL_NAND_IDENT) + +/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */ +static int mxs_flash_full_ident(struct mtd_info *mtd) +{ + int nand_maf_id, nand_dev_id; + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_flash_dev *type; + + type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL); + + if (IS_ERR(type)) { + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + return 0; +} + +#else + +/* Trying to detect the NAND flash using ONFi only */ +static int mxs_flash_onfi_ident(struct mtd_info *mtd) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + int i; + u8 mfg_id, dev_id; + u8 id_data[8]; + struct nand_onfi_params *p = &chip->onfi_params; + + /* Reset the chip */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + mfg_id = chip->read_byte(mtd); + dev_id = chip->read_byte(mtd); + + /* Try again to make sure */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); + if (id_data[0] != mfg_id || id_data[1] != dev_id) { + printf("second ID read did not match"); + return -1; + } + debug("0x%02x:0x%02x ", mfg_id, dev_id); + + /* read ONFI */ + chip->onfi_version = 0; + chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); + if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || + chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') { + return -2; + } + + /* we have ONFI, probe it */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); + mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); + mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + chip->chipsize = le32_to_cpu(p->blocks_per_lun); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + chip->phys_erase_shift = ffs(mtd->erasesize) - 1; + /* Convert chipsize to number of pages per chip -1 */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + chip->badblockbits = 8; + + debug("erasesize=%d (>>%d)\n", mtd->erasesize, chip->phys_erase_shift); + debug("writesize=%d (>>%d)\n", mtd->writesize, chip->page_shift); + debug("oobsize=%d\n", mtd->oobsize); + debug("chipsize=%lld\n", chip->chipsize); + + return 0; +} + +#endif /* CONFIG_SPL_NAND_IDENT */ + +static int mxs_flash_ident(struct mtd_info *mtd) +{ + int ret; +#if defined (CONFIG_SPL_NAND_IDENT) + ret = mxs_flash_full_ident(mtd); +#else + ret = mxs_flash_onfi_ident(mtd); +#endif + return ret; +} + +static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page); + ret = nand_chip.ecc.read_page(mtd, chip, buf, 1, page); + if (ret < 0) { + printf("read_page failed %d\n", ret); + return -1; + } + return 0; +} + +static int is_badblock(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + unsigned int block = offs >> chip->phys_erase_shift; + unsigned int page = offs >> chip->page_shift; + + debug("%s offs=0x%08x block:%d page:%d\n", __func__, (int)offs, block, + page); + chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + memset(chip->oob_poi, 0, mtd->oobsize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + return chip->oob_poi[0] != 0xff; +} + +/* setup mtd and nand structs and init mxs_nand driver */ +static int mxs_nand_init(void) +{ + /* return if already initalized */ + if (nand_chip.numchips) + return 0; + + /* init mxs nand driver */ + mxs_nand_init_spl(&nand_chip); + mtd = nand_to_mtd(&nand_chip); + /* set mtd functions */ + nand_chip.cmdfunc = mxs_nand_command; + nand_chip.numchips = 1; + + /* identify flash device */ + if (mxs_flash_ident(mtd)) { + printf("Failed to identify\n"); + return -1; + } + + /* allocate and initialize buffers */ + nand_chip.buffers = memalign(ARCH_DMA_MINALIGN, + sizeof(*nand_chip.buffers)); + nand_chip.oob_poi = nand_chip.buffers->databuf + mtd->writesize; + /* setup flash layout (does not scan as we override that) */ + mtd->size = nand_chip.chipsize; + nand_chip.scan_bbt(mtd); + + return 0; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) +{ + struct nand_chip *chip; + unsigned int page; + unsigned int nand_page_per_block; + unsigned int sz = 0; + + if (mxs_nand_init()) + return -ENODEV; + chip = mtd_to_nand(mtd); + page = offs >> chip->page_shift; + nand_page_per_block = mtd->erasesize / mtd->writesize; + + debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page); + + size = roundup(size, mtd->writesize); + while (sz < size) { + if (mxs_read_page_ecc(mtd, buf, page) < 0) + return -1; + sz += mtd->writesize; + offs += mtd->writesize; + page++; + buf += mtd->writesize; + + /* + * Check if we have crossed a block boundary, and if so + * check for bad block. + */ + if (!(page % nand_page_per_block)) { + /* + * Yes, new block. See if this block is good. If not, + * loop until we find a good block. + */ + while (is_badblock(mtd, offs, 1)) { + page = page + nand_page_per_block; + /* Check i we've reached the end of flash. */ + if (page >= mtd->size >> chip->page_shift) + return -ENOMEM; + } + } + } + + return 0; +} + +int nand_default_bbt(struct mtd_info *mtd) +{ + return 0; +} + +void nand_init(void) +{ +} + +void nand_deselect(void) +{ +} + diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c new file mode 100644 index 0000000000..bca51ffbf2 --- /dev/null +++ b/drivers/mtd/nand/raw/nand.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2005 + * 2N Telekomunikace, a.s. + * Ladislav Michl + */ + +#include +#include +#include +#include + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +int nand_curr_device = -1; + +static struct mtd_info *nand_info[CONFIG_SYS_MAX_NAND_DEVICE]; + +#ifndef CONFIG_SYS_NAND_SELF_INIT +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; +static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST; +#endif + +static char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8]; + +static unsigned long total_nand_size; /* in kiB */ + +struct mtd_info *get_nand_dev_by_index(int dev) +{ + if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || !nand_info[dev] || + !nand_info[dev]->name) + return NULL; + + return nand_info[dev]; +} + +int nand_mtd_to_devnum(struct mtd_info *mtd) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + if (mtd && get_nand_dev_by_index(i) == mtd) + return i; + } + + return -ENODEV; +} + +/* Register an initialized NAND mtd device with the U-Boot NAND command. */ +int nand_register(int devnum, struct mtd_info *mtd) +{ + if (devnum >= CONFIG_SYS_MAX_NAND_DEVICE) + return -EINVAL; + + nand_info[devnum] = mtd; + + sprintf(dev_name[devnum], "nand%d", devnum); + mtd->name = dev_name[devnum]; + +#ifdef CONFIG_MTD_DEVICE + /* + * Add MTD device so that we can reference it later + * via the mtdcore infrastructure (e.g. ubi). + */ + add_mtd_device(mtd); +#endif + + total_nand_size += mtd->size / 1024; + + if (nand_curr_device == -1) + nand_curr_device = devnum; + + return 0; +} + +#ifndef CONFIG_SYS_NAND_SELF_INIT +static void nand_init_chip(int i) +{ + struct nand_chip *nand = &nand_chip[i]; + struct mtd_info *mtd = nand_to_mtd(nand); + ulong base_addr = base_address[i]; + int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; + + if (maxchips < 1) + maxchips = 1; + + nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; + + if (board_nand_init(nand)) + return; + + if (nand_scan(mtd, maxchips)) + return; + + nand_register(i, mtd); +} +#endif + +#ifdef CONFIG_MTD_CONCAT +static void create_mtd_concat(void) +{ + struct mtd_info *nand_info_list[CONFIG_SYS_MAX_NAND_DEVICE]; + int nand_devices_found = 0; + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { + struct mtd_info *mtd = get_nand_dev_by_index(i); + if (mtd != NULL) { + nand_info_list[nand_devices_found] = mtd; + nand_devices_found++; + } + } + if (nand_devices_found > 1) { + struct mtd_info *mtd; + char c_mtd_name[16]; + + /* + * We detected multiple devices. Concatenate them together. + */ + sprintf(c_mtd_name, "nand%d", nand_devices_found); + mtd = mtd_concat_create(nand_info_list, nand_devices_found, + c_mtd_name); + + if (mtd == NULL) + return; + + nand_register(nand_devices_found, mtd); + } + + return; +} +#else +static void create_mtd_concat(void) +{ +} +#endif + +unsigned long nand_size(void) +{ + return total_nand_size; +} + +void nand_init(void) +{ + static int initialized; + + /* + * Avoid initializing NAND Flash multiple times, + * otherwise it will calculate a wrong total size. + */ + if (initialized) + return; + initialized = 1; + +#ifdef CONFIG_SYS_NAND_SELF_INIT + board_nand_init(); +#else + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + nand_init_chip(i); +#endif + +#ifdef CONFIG_SYS_NAND_SELECT_DEVICE + /* + * Select the chip in the board/cpu specific driver + */ + board_nand_select_device(mtd_to_nand(get_nand_dev_by_index(nand_curr_device)), + nand_curr_device); +#endif + + create_mtd_concat(); +} diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c new file mode 100644 index 0000000000..92daebe120 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_base.c @@ -0,0 +1,4619 @@ +/* + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/doc/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002-2006 Thomas Gleixner (tglx@linutronix.de) + * + * Credits: + * David Woodhouse for adding multichip support + * + * Aleph One Ltd. and Toby Churchill Ltd. for supporting the + * rework for 2K page size chips + * + * TODO: + * Enable cached programming for 2k page size chips + * Check, if mtd->ecctype should be set to MTD_ECC_HW + * if we have HW ECC support. + * BBT table is not serialized, has to be fixed + * + * This program 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#if CONFIG_IS_ENABLED(OF_CONTROL) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MTD_PARTITIONS +#include +#endif +#include +#include + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout nand_oob_8 = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3, + .length = 2}, + {.offset = 6, + .length = 2} } +}; + +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 6, + .eccpos = {0, 1, 2, 3, 6, 7}, + .oobfree = { + {.offset = 8, + . length = 8} } +}; + +static struct nand_ecclayout nand_oob_64 = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 38} } +}; + +static struct nand_ecclayout nand_oob_128 = { + .eccbytes = 48, + .eccpos = { + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + .length = 78} } +}; + +static int nand_get_device(struct mtd_info *mtd, int new_state); + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +/* + * For devices which display every fart in the system on a separate LED. Is + * compiled away when LED support is disabled. + */ +DEFINE_LED_TRIGGER(nand_led_trigger); + +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { + pr_debug("%s: length not block aligned\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +/** + * nand_release_device - [GENERIC] release chip + * @mtd: MTD device structure + * + * Release chip lock and wake up anyone waiting on the device. + */ +static void nand_release_device(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + /* De-select the NAND device */ + chip->select_chip(mtd, -1); +} + +/** + * nand_read_byte - [DEFAULT] read one byte from the chip + * @mtd: MTD device structure + * + * Default read function for 8bit buswidth + */ +uint8_t nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return readb(chip->IO_ADDR_R); +} + +/** + * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth with endianness conversion. + * + */ +static uint8_t nand_read_byte16(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); +} + +/** + * nand_read_word - [DEFAULT] read one word from the chip + * @mtd: MTD device structure + * + * Default read function for 16bit buswidth without endianness conversion. + */ +static u16 nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + return readw(chip->IO_ADDR_R); +} + +/** + * nand_select_chip - [DEFAULT] control CE line + * @mtd: MTD device structure + * @chipnr: chipnumber to select, -1 for deselect + * + * Default select function for 1 chip devices. + */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + switch (chipnr) { + case -1: + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + break; + case 0: + break; + + default: + BUG(); + } +} + +/** + * nand_write_byte - [DEFAULT] write single byte to chip + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] + */ +static void nand_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + chip->write_buf(mtd, &byte, 1); +} + +/** + * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16 + * @mtd: MTD device structure + * @byte: value to write + * + * Default function to write a byte to I/O[7:0] on a 16-bit wide chip. + */ +static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + uint16_t word = byte; + + /* + * It's not entirely clear what should happen to I/O[15:8] when writing + * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads: + * + * When the host supports a 16-bit bus width, only data is + * transferred at the 16-bit width. All address and command line + * transfers shall use only the lower 8-bits of the data bus. During + * command transfers, the host may place any value on the upper + * 8-bits of the data bus. During address transfers, the host shall + * set the upper 8-bits of the data bus to 00h. + * + * One user of the write_byte callback is nand_onfi_set_features. The + * four parameters are specified to be written to I/O[7:0], but this is + * neither an address nor a command transfer. Let's assume a 0 on the + * upper I/O lines is OK. + */ + chip->write_buf(mtd, (uint8_t *)&word, 2); +} + +static void iowrite8_rep(void *addr, const uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + writeb(buf[i], addr); +} +static void ioread8_rep(void *addr, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = readb(addr); +} + +static void ioread16_rep(void *addr, void *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + + for (i = 0; i < len; i++) + p[i] = readw(addr); +} + +static void iowrite16_rep(void *addr, void *buf, int len) +{ + int i; + u16 *p = (u16 *) buf; + + for (i = 0; i < len; i++) + writew(p[i], addr); +} + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswidth. + */ +void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + iowrite8_rep(chip->IO_ADDR_W, buf, len); +} + +/** + * nand_read_buf - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 8bit buswidth. + */ +void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + ioread8_rep(chip->IO_ADDR_R, buf, len); +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswidth. + */ +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); +} + +/** + * nand_read_buf16 - [DEFAULT] read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + * + * Default read function for 16bit buswidth. + */ +void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u16 *p = (u16 *) buf; + + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); +} + +/** + * nand_block_bad - [DEFAULT] Read bad block marker from the chip + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check, if the block is bad. + */ +static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) +{ + int page, res = 0, i = 0; + struct nand_chip *chip = mtd_to_nand(mtd); + u16 bad; + + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + + do { + if (chip->options & NAND_BUSWIDTH_16) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, + chip->badblockpos & 0xFE, page); + bad = cpu_to_le16(chip->read_word(mtd)); + if (chip->badblockpos & 0x1) + bad >>= 8; + else + bad &= 0xFF; + } else { + chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, + page); + bad = chip->read_byte(mtd); + } + + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + ofs += mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; + i++; + } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); + + return res; +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by a hardware + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + memset(&ops, 0, sizeof(ops)); + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order: + * (1) erase the affected block, to allow OOB marker to be written cleanly + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the + * procedures, and dump the error in the end. +*/ +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int res, ret = 0; + + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { + struct erase_info einfo; + + /* Attempt erase before marking OOB */ + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = ofs; + einfo.len = 1ULL << chip->phys_erase_shift; + nand_erase_nand(mtd, &einfo, 0); + + /* Write bad block marker to OOB */ + nand_get_device(mtd, FL_WRITING); + ret = chip->block_markbad(mtd, ofs); + nand_release_device(mtd); + } + + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); + if (!ret) + ret = res; + } + + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * + * Check, if the device is write protected. The function expects, that the + * device is already selected. + */ +static int nand_check_wp(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + /* Broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + + /* Check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} + +/** + * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. + * @mtd: MTD device structure + * @ofs: offset from device start + * + * Check if the block is marked as reserved. + */ +static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->bbt) + return 0; + /* Return info from the table */ + return nand_isreserved_bbt(mtd, ofs); +} + +/** + * nand_block_checkbad - [GENERIC] Check if a block is marked bad + * @mtd: MTD device structure + * @ofs: offset from device start + * @allowbbt: 1, if its allowed to access the bbt area + * + * Check, if the block is bad. Either by reading the bad block table or + * calling of the scan function. + */ +static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!(chip->options & NAND_SKIP_BBTSCAN) && + !(chip->options & NAND_BBT_SCANNED)) { + chip->options |= NAND_BBT_SCANNED; + chip->scan_bbt(mtd); + } + + if (!chip->bbt) + return chip->block_bad(mtd, ofs); + + /* Return info from the table */ + return nand_isbad_bbt(mtd, ofs, allowbbt); +} + +/** + * nand_wait_ready - [GENERIC] Wait for the ready pin after commands. + * @mtd: MTD device structure + * + * Wait for the ready pin after a command, and warn if a timeout occurs. + */ +void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u32 timeo = (CONFIG_SYS_HZ * 400) / 1000; + u32 time_start; + + time_start = get_timer(0); + /* Wait until command is processed or timeout occurs */ + while (get_timer(time_start) < timeo) { + if (chip->dev_ready) + if (chip->dev_ready(mtd)) + break; + } + + if (!chip->dev_ready(mtd)) + pr_warn("timeout while waiting for chip to become ready\n"); +} +EXPORT_SYMBOL_GPL(nand_wait_ready); + +/** + * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands. + * @mtd: MTD device structure + * @timeo: Timeout in ms + * + * Wait for status ready (i.e. command done) or timeout. + */ +static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + u32 time_start; + + timeo = (CONFIG_SYS_HZ * timeo) / 1000; + time_start = get_timer(0); + while (get_timer(time_start) < timeo) { + if ((chip->read_byte(mtd) & NAND_STATUS_READY)) + break; + WATCHDOG_RESET(); + } +}; + +/** + * nand_command - [DEFAULT] Send command to NAND device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This function is used for small page devices + * (512 Bytes per page). + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; + + /* Write out the command to the device */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->writesize) { + /* OOB area */ + column -= mtd->writesize; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + chip->cmd_ctrl(mtd, readcmd, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + chip->cmd_ctrl(mtd, command, ctrl); + + /* Address cycle, when necessary */ + ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); + if (chip->options & NAND_ROW_ADDR_3) + chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status and sequential + * in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + case NAND_CMD_READID: + case NAND_CMD_SET_FEATURES: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, + NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + nand_wait_status_ready(mtd, 250); + return; + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * nand_command_lp - [DEFAULT] Send command to NAND large page device + * @mtd: MTD device structure + * @command: the command to be sent + * @column: the column address for this command, -1 if none + * @page_addr: the page address for this command, -1 if none + * + * Send command to NAND device. This is the version for the new large page + * devices. We don't have the separate regions as we have in the small page + * devices. We must emulate NAND_CMD_READOOB to keep the code compatible. + */ +static void nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd_to_nand(mtd); + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Command latch cycle */ + chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + + if (column != -1 || page_addr != -1) { + int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; + + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, + NAND_NCE | NAND_ALE); + if (chip->options & NAND_ROW_ADDR_3) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } + } + chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Program and erase have their own busy handlers status, sequential + * in and status need no delay. + */ + switch (command) { + + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_READID: + case NAND_CMD_SET_FEATURES: + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + chip->cmd_ctrl(mtd, NAND_CMD_STATUS, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + /* EZ-NAND can take upto 250ms as per ONFi v4.0 */ + nand_wait_status_ready(mtd, 250); + return; + + case NAND_CMD_RNDOUT: + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + return; + + case NAND_CMD_READ0: + chip->cmd_ctrl(mtd, NAND_CMD_READSTART, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE | NAND_CTRL_CHANGE); + + /* This applies to read commands */ + default: + /* + * If we don't have access to the busy pin, we apply the given + * command delay. + */ + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* + * Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. + */ + ndelay(100); + + nand_wait_ready(mtd); +} + +/** + * panic_nand_get_device - [GENERIC] Get chip for selected access + * @chip: the nand chip descriptor + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Used when in panic, no locks are taken. + */ +static void panic_nand_get_device(struct nand_chip *chip, + struct mtd_info *mtd, int new_state) +{ + /* Hardware controller shared among independent devices */ + chip->controller->active = chip; + chip->state = new_state; +} + +/** + * nand_get_device - [GENERIC] Get chip for selected access + * @mtd: MTD device structure + * @new_state: the state which is requested + * + * Get the device and lock it for exclusive access + */ +static int +nand_get_device(struct mtd_info *mtd, int new_state) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + chip->state = new_state; + return 0; +} + +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * @timeo: timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops through mtdoops. + */ +static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, + unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + mdelay(1); + } +} + +/** + * nand_wait - [DEFAULT] wait until the command is done + * @mtd: MTD device structure + * @chip: NAND chip structure + * + * Wait for command done. This applies to erase and program only. + */ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + int status; + unsigned long timeo = 400; + + led_trigger_event(nand_led_trigger, LED_FULL); + + /* + * Apply this short delay always to ensure that we do wait tWB in any + * case on any machine. + */ + ndelay(100); + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + + u32 timer = (CONFIG_SYS_HZ * timeo) / 1000; + u32 time_start; + + time_start = get_timer(0); + while (get_timer(time_start) < timer) { + if (chip->dev_ready) { + if (chip->dev_ready(mtd)) + break; + } else { + if (chip->read_byte(mtd) & NAND_STATUS_READY) + break; + } + } + led_trigger_event(nand_led_trigger, LED_OFF); + + status = (int)chip->read_byte(mtd); + /* This can happen if in case of timeout or buggy dev_ready */ + WARN_ON(!(status & NAND_STATUS_READY)); + return status; +} + +/** + * nand_reset_data_interface - Reset data interface and timings + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Reset the Data interface and timings to ONFI mode 0. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_data_interface *conf; + int ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * The ONFI specification says: + * " + * To transition from NV-DDR or NV-DDR2 to the SDR data + * interface, the host shall use the Reset (FFh) command + * using SDR timing mode 0. A device in any timing mode is + * required to recognize Reset (FFh) command issued in SDR + * timing mode 0. + * " + * + * Configure the data interface in SDR mode and set the + * timings to timing mode 0. + */ + + conf = nand_get_default_data_interface(); + ret = chip->setup_data_interface(mtd, chipnr, conf); + if (ret) + pr_err("Failed to configure data interface to SDR timing mode 0\n"); + + return ret; +} + +/** + * nand_setup_data_interface - Setup the best data interface and timings + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Find and configure the best data interface and NAND timings supported by + * the chip and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (!chip->setup_data_interface || !chip->data_interface) + return 0; + + /* + * Ensure the timing mode has been changed on the chip side + * before changing timings on the controller side. + */ + if (chip->onfi_version) { + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { + chip->onfi_timing_mode_default, + }; + + ret = chip->onfi_set_features(mtd, chip, + ONFI_FEATURE_ADDR_TIMING_MODE, + tmode_param); + if (ret) + goto err; + } + + ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface); +err: + return ret; +} + +/** + * nand_init_data_interface - find the best data interface and timings + * @chip: The NAND chip + * + * Find the best data interface and NAND timings supported by the chip + * and the driver. + * First tries to retrieve supported timing modes from ONFI information, + * and if the NAND chip does not support ONFI, relies on the + * ->onfi_timing_mode_default specified in the nand_ids table. After this + * function nand_chip->data_interface is initialized with the best timing mode + * available. + * + * Returns 0 for success or negative error code otherwise. + */ +static int nand_init_data_interface(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int modes, mode, ret; + + if (!chip->setup_data_interface) + return 0; + + /* + * First try to identify the best timings from ONFI parameters and + * if the NAND does not support ONFI, fallback to the default ONFI + * timing mode. + */ + modes = onfi_get_async_timing_mode(chip); + if (modes == ONFI_TIMING_MODE_UNKNOWN) { + if (!chip->onfi_timing_mode_default) + return 0; + + modes = GENMASK(chip->onfi_timing_mode_default, 0); + } + + chip->data_interface = kzalloc(sizeof(*chip->data_interface), + GFP_KERNEL); + if (!chip->data_interface) + return -ENOMEM; + + for (mode = fls(modes) - 1; mode >= 0; mode--) { + ret = onfi_init_data_interface(chip, chip->data_interface, + NAND_SDR_IFACE, mode); + if (ret) + continue; + + /* Pass -1 to only */ + ret = chip->setup_data_interface(mtd, + NAND_DATA_IFACE_CHECK_ONLY, + chip->data_interface); + if (!ret) { + chip->onfi_timing_mode_default = mode; + break; + } + } + + return 0; +} + +static void __maybe_unused nand_release_data_interface(struct nand_chip *chip) +{ + kfree(chip->data_interface); +} + +/** + * nand_reset - Reset and initialize a NAND device + * @chip: The NAND chip + * @chipnr: Internal die id + * + * Returns 0 for success or negative error code otherwise + */ +int nand_reset(struct nand_chip *chip, int chipnr) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = nand_reset_data_interface(chip, chipnr); + if (ret) + return ret; + + /* + * The CS line has to be released before we can apply the new NAND + * interface settings, hence this weird ->select_chip() dance. + */ + chip->select_chip(mtd, chipnr); + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + chip->select_chip(mtd, -1); + + chip->select_chip(mtd, chipnr); + ret = nand_setup_data_interface(chip, chipnr); + chip->select_chip(mtd, -1); + if (ret) + return ret; + + return 0; +} + +/** + * nand_check_erased_buf - check if a buffer contains (almost) only 0xff data + * @buf: buffer to test + * @len: buffer length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a buffer contains only 0xff, which means the underlying region + * has been erased and is ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region is not erased. + * Note: The logic of this function has been extracted from the memweight + * implementation, except that nand_check_erased_buf function exit before + * testing the whole buffer if the number of bitflips exceed the + * bitflips_threshold value. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. + */ +static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold) +{ + const unsigned char *bitmap = buf; + int bitflips = 0; + int weight; + + for (; len && ((uintptr_t)bitmap) % sizeof(long); + len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len >= 4; len -= 4, bitmap += 4) { + weight = hweight32(*((u32 *)bitmap)); + bitflips += 32 - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + for (; len > 0; len--, bitmap++) { + weight = hweight8(*bitmap); + bitflips += BITS_PER_BYTE - weight; + if (unlikely(bitflips > bitflips_threshold)) + return -EBADMSG; + } + + return bitflips; +} + +/** + * nand_check_erased_ecc_chunk - check if an ECC chunk contains (almost) only + * 0xff data + * @data: data buffer to test + * @datalen: data length + * @ecc: ECC buffer + * @ecclen: ECC length + * @extraoob: extra OOB buffer + * @extraooblen: extra OOB length + * @bitflips_threshold: maximum number of bitflips + * + * Check if a data buffer and its associated ECC and OOB data contains only + * 0xff pattern, which means the underlying region has been erased and is + * ready to be programmed. + * The bitflips_threshold specify the maximum number of bitflips before + * considering the region as not erased. + * + * Note: + * 1/ ECC algorithms are working on pre-defined block sizes which are usually + * different from the NAND page size. When fixing bitflips, ECC engines will + * report the number of errors per chunk, and the NAND core infrastructure + * expect you to return the maximum number of bitflips for the whole page. + * This is why you should always use this function on a single chunk and + * not on the whole page. After checking each chunk you should update your + * max_bitflips value accordingly. + * 2/ When checking for bitflips in erased pages you should not only check + * the payload data but also their associated ECC data, because a user might + * have programmed almost all bits to 1 but a few. In this case, we + * shouldn't consider the chunk as erased, and checking ECC bytes prevent + * this case. + * 3/ The extraoob argument is optional, and should be used if some of your OOB + * data are protected by the ECC engine. + * It could also be used if you support subpages and want to attach some + * extra OOB data to an ECC chunk. + * + * Returns a positive number of bitflips less than or equal to + * bitflips_threshold, or -ERROR_CODE for bitflips in excess of the + * threshold. In case of success, the passed buffers are filled with 0xff. + */ +int nand_check_erased_ecc_chunk(void *data, int datalen, + void *ecc, int ecclen, + void *extraoob, int extraooblen, + int bitflips_threshold) +{ + int data_bitflips = 0, ecc_bitflips = 0, extraoob_bitflips = 0; + + data_bitflips = nand_check_erased_buf(data, datalen, + bitflips_threshold); + if (data_bitflips < 0) + return data_bitflips; + + bitflips_threshold -= data_bitflips; + + ecc_bitflips = nand_check_erased_buf(ecc, ecclen, bitflips_threshold); + if (ecc_bitflips < 0) + return ecc_bitflips; + + bitflips_threshold -= ecc_bitflips; + + extraoob_bitflips = nand_check_erased_buf(extraoob, extraooblen, + bitflips_threshold); + if (extraoob_bitflips < 0) + return extraoob_bitflips; + + if (data_bitflips) + memset(data, 0xff, datalen); + + if (ecc_bitflips) + memset(ecc, 0xff, ecclen); + + if (extraoob_bitflips) + memset(extraoob, 0xff, extraooblen); + + return data_bitflips + ecc_bitflips + extraoob_bitflips; +} +EXPORT_SYMBOL(nand_check_erased_ecc_chunk); + +/** + * nand_read_page_raw - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + + return 0; +} + +/** + * nand_read_page_swecc - [REPLACEABLE] software ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; + + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_subpage - [REPLACEABLE] ECC based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data + * @page: page number to read + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, + int page) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + int index; + unsigned int max_bitflips = 0; + + /* Column address within the page aligned to ECC size (256bytes) */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + index = start_step * chip->ecc.bytes; + + /* Data size aligned to ECC ecc.size */ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* + * The performance is faster if we position offsets according to + * ecc.pos. Let's make sure that there are no gaps in ECC positions. + */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + index] + 1 != eccpos[i + index + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* + * Send the command to read the particular ECC bytes take care + * about buswidth alignment in read_buf. + */ + aligned_pos = eccpos[index] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[index] & (busw - 1)) + aligned_len++; + if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, + &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + &chip->buffers->ecccode[i], + chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Not for syndrome calculating ECC controllers which need a special oob layout. + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_hwecc_oob_first - [REPLACEABLE] hw ecc, read oob first + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Hardware ECC for large page chips, require OOB to be read first. For this + * ECC mode, the write_page method is re-used from ECC_HW. These methods + * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with + * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from + * the data area, by overwriting the NAND manufacturer bad block markings. + */ +static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; + + /* Read the OOB area first */ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} + +/** + * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); + + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + oob - eccpadbytes, + eccpadbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->read_buf(mtd, oob, i); + + return max_bitflips; +} + +/** + * nand_transfer_oob - [INTERN] Transfer oob to client buffer + * @chip: nand chip structure + * @oob: oob destination address + * @ops: oob ops structure + * @len: size of oob to transfer + */ +static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops, size_t len) +{ + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(oob, chip->oob_poi + ops->ooboffs, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, roffs = ops->ooboffs; + size_t bytes = 0; + + for (; free->length && len; free++, len -= bytes) { + /* Read request not from offset 0? */ + if (unlikely(roffs)) { + if (roffs >= free->length) { + roffs -= free->length; + continue; + } + boffs = free->offset + roffs; + bytes = min_t(size_t, len, + (free->length - roffs)); + roffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(oob, chip->oob_poi + boffs, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +/** + * nand_setup_read_retry - [INTERN] Set the READ RETRY mode + * @mtd: MTD device structure + * @retry_mode: the retry mode to use + * + * Some vendors supply a special command to shift the Vt threshold, to be used + * when there are too many bitflips in a page (i.e., ECC error). After setting + * a new threshold, the host should retry reading the page. + */ +static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + pr_debug("setting READ RETRY mode %d\n", retry_mode); + + if (retry_mode >= chip->read_retries) + return -EINVAL; + + if (!chip->setup_read_retry) + return -EOPNOTSUPP; + + return chip->setup_read_retry(mtd, retry_mode); +} + +/** + * nand_do_read_ops - [INTERN] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob ops structure + * + * Internal function. Called with chip held. + */ +static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int chipnr, page, realpage, col, bytes, aligned, oob_required; + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + uint32_t readlen = ops->len; + uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = mtd_oobavail(mtd, ops); + + uint8_t *bufpoi, *oob, *buf; + int use_bufpoi; + unsigned int max_bitflips = 0; + int retry_mode = 0; + bool ecc_fail = false; + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + col = (int)(from & (mtd->writesize - 1)); + + buf = ops->datbuf; + oob = ops->oobbuf; + oob_required = oob ? 1 : 0; + + while (1) { + unsigned int ecc_failures = mtd->ecc_stats.failed; + + WATCHDOG_RESET(); + bytes = min(mtd->writesize - col, readlen); + aligned = (bytes == mtd->writesize); + + if (!aligned) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !IS_ALIGNED((unsigned long)buf, + chip->buf_align); + else + use_bufpoi = 0; + + /* Is the current page in the buffer? */ + if (realpage != chip->pagebuf || oob) { + bufpoi = use_bufpoi ? chip->buffers->databuf : buf; + + if (use_bufpoi && aligned) + pr_debug("%s: using read bounce buffer for buf@%p\n", + __func__, buf); + +read_retry: + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ + if (unlikely(ops->mode == MTD_OPS_RAW)) + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + oob_required, + page); + else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && + !oob) + ret = chip->ecc.read_subpage(mtd, chip, + col, bytes, bufpoi, + page); + else + ret = chip->ecc.read_page(mtd, chip, bufpoi, + oob_required, page); + if (ret < 0) { + if (use_bufpoi) + /* Invalidate page cache */ + chip->pagebuf = -1; + break; + } + + max_bitflips = max_t(unsigned int, max_bitflips, ret); + + /* Transfer not aligned data */ + if (use_bufpoi) { + if (!NAND_HAS_SUBPAGE_READ(chip) && !oob && + !(mtd->ecc_stats.failed - ecc_failures) && + (ops->mode != MTD_OPS_RAW)) { + chip->pagebuf = realpage; + chip->pagebuf_bitflips = ret; + } else { + /* Invalidate page cache */ + chip->pagebuf = -1; + } + memcpy(buf, chip->buffers->databuf + col, bytes); + } + + if (unlikely(oob)) { + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } + } + + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + if (mtd->ecc_stats.failed - ecc_failures) { + if (retry_mode + 1 < chip->read_retries) { + retry_mode++; + ret = nand_setup_read_retry(mtd, + retry_mode); + if (ret < 0) + break; + + /* Reset failures; retry */ + mtd->ecc_stats.failed = ecc_failures; + goto read_retry; + } else { + /* No more retry modes; real failure */ + ecc_fail = true; + } + } + + buf += bytes; + } else { + memcpy(buf, chip->buffers->databuf + col, bytes); + buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); + } + + readlen -= bytes; + + /* Reset to retry mode 0 */ + if (retry_mode) { + ret = nand_setup_read_retry(mtd, 0); + if (ret < 0) + break; + retry_mode = 0; + } + + if (!readlen) + break; + + /* For subsequent reads align to page boundary */ + col = 0; + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + chip->select_chip(mtd, -1); + + ops->retlen = ops->len - (size_t) readlen; + if (oob) + ops->oobretlen = ops->ooblen - oobreadlen; + + if (ret < 0) + return ret; + + if (ecc_fail) + return -EBADMSG; + + return max_bitflips; +} + +/** + * nand_read_oob_std - [REPLACEABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return 0; +} + +/** + * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = chip->oob_poi; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + return 0; +} + +/** + * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_do_read_oob - [INTERN] NAND read out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operations description structure + * + * NAND read out-of-band data from the spare area. + */ +static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int page, realpage, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_ecc_stats stats; + int readlen = ops->ooblen; + int len; + uint8_t *buf = ops->oobbuf; + int ret = 0; + + pr_debug("%s: from = 0x%08Lx, len = %i\n", + __func__, (unsigned long long)from, readlen); + + stats = mtd->ecc_stats; + + len = mtd_oobavail(mtd, ops); + + if (unlikely(ops->ooboffs >= len)) { + pr_debug("%s: attempt to start read outside oob\n", + __func__); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + chipnr = (int)(from >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + realpage = (int)(from >> chip->page_shift); + page = realpage & chip->pagemask; + + while (1) { + WATCHDOG_RESET(); + + if (ops->mode == MTD_OPS_RAW) + ret = chip->ecc.read_oob_raw(mtd, chip, page); + else + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; + + len = min(len, readlen); + buf = nand_transfer_oob(chip, buf, ops, len); + + if (chip->options & NAND_NEED_READRDY) { + /* Apply delay or wait for ready/busy pin */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + nand_wait_ready(mtd); + } + + readlen -= len; + if (!readlen) + break; + + /* Increment page address */ + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + chip->select_chip(mtd, -1); + + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + +/** + * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band + * @mtd: MTD device structure + * @from: offset to read from + * @ops: oob operation description structure + * + * NAND read data and/or out-of-band data. + */ +static int nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow reads past end of device */ + if (ops->datbuf && (from + ops->len) > mtd->size) { + pr_debug("%s: attempt to read beyond end of device\n", + __func__); + return -EINVAL; + } + + nand_get_device(mtd, FL_READING); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_read_oob(mtd, from, ops); + else + ret = nand_do_read_ops(mtd, from, ops); + +out: + nand_release_device(mtd); + return ret; +} + + +/** + * nand_write_page_raw - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * Not for syndrome calculating ECC controllers, which use a special oob layout. + */ +static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + chip->write_buf(mtd, buf, mtd->writesize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +/** + * nand_write_page_raw_syndrome - [INTERN] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static int nand_write_page_raw_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); + + return 0; +} +/** + * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* Software ECC calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); +} + +/** + * nand_write_page_hwecc - [REPLACEABLE] hardware ECC based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + + +/** + * nand_write_subpage_hwecc - [REPLACEABLE] hardware ECC based subpage write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @offset: column address of subpage within the page + * @data_len: data length + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + */ +static int nand_write_subpage_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint32_t offset, + uint32_t data_len, const uint8_t *buf, + int oob_required, int page) +{ + uint8_t *oob_buf = chip->oob_poi; + uint8_t *ecc_calc = chip->buffers->ecccalc; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + int ecc_steps = chip->ecc.steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint32_t start_step = offset / ecc_size; + uint32_t end_step = (offset + data_len - 1) / ecc_size; + int oob_bytes = mtd->oobsize / ecc_steps; + int step, i; + + for (step = 0; step < ecc_steps; step++) { + /* configure controller for WRITE access */ + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + + /* write data (untouched subpages already masked by 0xFF) */ + chip->write_buf(mtd, buf, ecc_size); + + /* mask ECC of un-touched subpages by padding 0xFF */ + if ((step < start_step) || (step > end_step)) + memset(ecc_calc, 0xff, ecc_bytes); + else + chip->ecc.calculate(mtd, buf, ecc_calc); + + /* mask OOB of un-touched subpages by padding 0xFF */ + /* if oob_required, preserve OOB metadata of written subpage */ + if (!oob_required || (step < start_step) || (step > end_step)) + memset(oob_buf, 0xff, oob_bytes); + + buf += ecc_size; + ecc_calc += ecc_bytes; + oob_buf += oob_bytes; + } + + /* copy calculated ECC for whole page to chip->buffer->oob */ + /* this include masked-value(0xFF) for unwritten subpages */ + ecc_calc = chip->buffers->ecccalc; + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + /* write OOB buffer to NAND device */ + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + + +/** + * nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * + * The hw generator calculates the error syndrome automatically. Therefore we + * need a special oob layout and handling. + */ +static int nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); + + return 0; +} + +/** + * nand_write_page - [REPLACEABLE] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @offset: address offset within the page + * @data_len: length of actual data to be written + * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to write + * @raw: use _raw version of write_page + */ +static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int raw) +{ + int status, subpage; + + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && + chip->ecc.write_subpage) + subpage = offset || (data_len < mtd->writesize); + else + subpage = 0; + + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + status = chip->ecc.write_page_raw(mtd, chip, buf, + oob_required, page); + else if (subpage) + status = chip->ecc.write_subpage(mtd, chip, offset, data_len, + buf, oob_required, page); + else + status = chip->ecc.write_page(mtd, chip, buf, oob_required, + page); + + if (status < 0) + return status; + + if (nand_standard_page_accessors(&chip->ecc)) { + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + } + + return 0; +} + +/** + * nand_fill_oob - [INTERN] Transfer client buffer to oob + * @mtd: MTD device structure + * @oob: oob data buffer + * @len: oob data write length + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + /* + * Initialise to all 0xFF, to avoid the possibility of left over OOB + * data from a previous OOB read. + */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + + switch (ops->mode) { + + case MTD_OPS_PLACE_OOB: + case MTD_OPS_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OPS_AUTO_OOB: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for (; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0) + +/** + * nand_do_write_ops - [INTERN] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * NAND write with ECC. + */ +static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, realpage, page, column; + struct nand_chip *chip = mtd_to_nand(mtd); + uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = mtd_oobavail(mtd, ops); + + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret; + int oob_required = oob ? 1 : 0; + + ops->retlen = 0; + if (!writelen) + return 0; + + /* Reject writes, which are not page aligned */ + if (NOTALIGNED(to)) { + pr_notice("%s: attempt to write non page aligned data\n", + __func__); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + ret = -EIO; + goto err_out; + } + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= ((loff_t)chip->pagebuf << chip->page_shift) && + ((loff_t)chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* Don't allow multipage oob writes with offset */ + if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) { + ret = -EINVAL; + goto err_out; + } + + while (1) { + int bytes = mtd->writesize; + uint8_t *wbuf = buf; + int use_bufpoi; + int part_pagewr = (column || writelen < mtd->writesize); + + if (part_pagewr) + use_bufpoi = 1; + else if (chip->options & NAND_USE_BOUNCE_BUFFER) + use_bufpoi = !IS_ALIGNED((unsigned long)buf, + chip->buf_align); + else + use_bufpoi = 0; + + WATCHDOG_RESET(); + /* Partial page write?, or need to use bounce buffer */ + if (use_bufpoi) { + pr_debug("%s: using write bounce buffer for buf@%p\n", + __func__, buf); + if (part_pagewr) + bytes = min_t(int, bytes - column, writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(mtd, oob, len, ops); + oobwritelen -= len; + } else { + /* We still need to erase leftover OOB data */ + memset(chip->oob_poi, 0xff, mtd->oobsize); + } + ret = chip->write_page(mtd, chip, column, bytes, wbuf, + oob_required, page, + (ops->mode == MTD_OPS_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + +err_out: + chip->select_chip(mtd, -1); + return ret; +} + +/** + * panic_nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC. Used when performing writes in interrupt context, this + * may for example be called by mtdoops when writing an oops while in panic. + */ +static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_oob_ops ops; + int ret; + + /* Wait for the device to get ready */ + panic_nand_wait(mtd, chip, 400); + + /* Grab the device */ + panic_nand_get_device(chip, mtd, FL_WRITING); + + memset(&ops, 0, sizeof(ops)); + ops.len = len; + ops.datbuf = (uint8_t *)buf; + ops.mode = MTD_OPS_PLACE_OOB; + + ret = nand_do_write_ops(mtd, to, &ops); + + *retlen = ops.retlen; + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band. + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd_to_nand(mtd); + + pr_debug("%s: to = 0x%08x, len = %i\n", + __func__, (unsigned int)to, (int)ops->ooblen); + + len = mtd_oobavail(mtd, ops); + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + pr_debug("%s: attempt to write past end of page\n", + __func__); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= len)) { + pr_debug("%s: attempt to start write outside oob\n", + __func__); + return -EINVAL; + } + + /* Do not allow write past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + nand_reset(chip, chipnr); + + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + chip->select_chip(mtd, -1); + return -EROFS; + } + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops); + + if (ops->mode == MTD_OPS_RAW) + status = chip->ecc.write_oob_raw(mtd, chip, page & chip->pagemask); + else + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + + chip->select_chip(mtd, -1); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +static int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret = -ENOTSUPP; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + pr_debug("%s: attempt to write beyond end of device\n", + __func__); + return -EINVAL; + } + + nand_get_device(mtd, FL_WRITING); + + switch (ops->mode) { + case MTD_OPS_PLACE_OOB: + case MTD_OPS_AUTO_OOB: + case MTD_OPS_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + +out: + nand_release_device(mtd); + return ret; +} + +/** + * single_erase - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips. Returns NAND status. + */ +static int single_erase(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + return chip->waitfunc(mtd, chip); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks. + */ +static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand(mtd, instr, 0); +} + +/** + * nand_erase_nand - [INTERN] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks. + */ +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) +{ + int page, status, pages_per_block, ret, chipnr; + struct nand_chip *chip = mtd_to_nand(mtd); + loff_t len; + + pr_debug("%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)instr->addr, + (unsigned long long)instr->len); + + if (check_offs_len(mtd, instr->addr, instr->len)) + return -EINVAL; + + /* Grab the lock and see if the device is available */ + nand_get_device(mtd, FL_ERASING); + + /* Shift to get first page */ + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + pr_debug("%s: device is write protected!\n", + __func__); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + WATCHDOG_RESET(); + + /* Check if we have a bad block, we do not erase bad blocks! */ + if (!instr->scrub && nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, allowbbt)) { + pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", + __func__, page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page. + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + status = chip->erase(mtd, page & chip->pagemask); + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + pr_debug("%s: failed erase, page 0x%08x\n", + __func__, page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = + ((loff_t)page << chip->page_shift); + goto erase_exit; + } + + /* Increment page address and decrement length */ + len -= (1ULL << chip->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & chip->pagemask)) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + instr->state = MTD_ERASE_DONE; + +erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + + /* Deselect and wake up anyone waiting on the device */ + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* Return more or less happy */ + return ret; +} + +/** + * nand_sync - [MTD Interface] sync + * @mtd: MTD device structure + * + * Sync is actually a wait for chip ready function. + */ +static void nand_sync(struct mtd_info *mtd) +{ + pr_debug("%s: called\n", __func__); + + /* Grab the lock and see if the device is available */ + nand_get_device(mtd, FL_SYNCING); + /* Release it and go back */ + nand_release_device(mtd); +} + +/** + * nand_block_isbad - [MTD Interface] Check if block at offset is bad + * @mtd: MTD device structure + * @offs: offset relative to mtd start + */ +static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int chipnr = (int)(offs >> chip->chip_shift); + int ret; + + /* Select the NAND device */ + nand_get_device(mtd, FL_READING); + chip->select_chip(mtd, chipnr); + + ret = nand_block_checkbad(mtd, offs, 0); + + chip->select_chip(mtd, -1); + nand_release_device(mtd); + + return ret; +} + +/** + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + int ret; + + ret = nand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return nand_block_markbad_lowlevel(mtd, ofs); +} + +/** + * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + int status; + int i; + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -ENOTSUPP; +#endif + + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, subfeature_param[i]); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + return 0; +} + +/** + * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand + * @mtd: MTD device structure + * @chip: nand chip info structure + * @addr: feature address. + * @subfeature_param: the subfeature parameters, a four bytes array. + */ +static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, + int addr, uint8_t *subfeature_param) +{ + int i; + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) + return -ENOTSUPP; +#endif + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + *subfeature_param++ = chip->read_byte(mtd); + return 0; +} + +/* Set default functions */ +static void nand_set_defaults(struct nand_chip *chip, int busw) +{ + /* check for proper chip_delay setup, set 20us if not */ + if (!chip->chip_delay) + chip->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (chip->cmdfunc == NULL) + chip->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (chip->waitfunc == NULL) + chip->waitfunc = nand_wait; + + if (!chip->select_chip) + chip->select_chip = nand_select_chip; + + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + + /* If called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) + chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; + if (!chip->read_word) + chip->read_word = nand_read_word; + if (!chip->block_bad) + chip->block_bad = nand_block_bad; + if (!chip->block_markbad) + chip->block_markbad = nand_default_block_markbad; + if (!chip->write_buf || chip->write_buf == nand_write_buf) + chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; + if (!chip->write_byte || chip->write_byte == nand_write_byte) + chip->write_byte = busw ? nand_write_byte16 : nand_write_byte; + if (!chip->read_buf || chip->read_buf == nand_read_buf) + chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; + if (!chip->scan_bbt) + chip->scan_bbt = nand_default_bbt; + + if (!chip->controller) { + chip->controller = &chip->hwcontrol; + spin_lock_init(&chip->controller->lock); + init_waitqueue_head(&chip->controller->wq); + } + + if (!chip->buf_align) + chip->buf_align = 1; +} + +/* Sanitize ONFI strings so we can safely print them */ +static void sanitize_string(char *s, size_t len) +{ + ssize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* Remove trailing spaces */ + strim(s); +} + +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, + struct nand_chip *chip, struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret = -EINVAL; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + + /* Use the Change Read Column command to skip the ONFI param pages. */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + sizeof(*p) * p->num_of_param_pages , -1); + + /* Read out the Extended Parameter Page. */ + chip->read_buf(mtd, (uint8_t *)ep, len); + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp((char *)ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + +static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode}; + + return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY, + feature); +} + +/* + * Configure chip properties from Micron vendor-specific ONFI table + */ +static void nand_onfi_detect_micron(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + struct nand_onfi_vendor_micron *micron = (void *)p->vendor; + + if (le16_to_cpu(p->vendor_revision) < 1) + return; + + chip->read_retries = micron->read_retry_options; + chip->setup_read_retry = nand_setup_read_retry_micron; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_onfi_params *p = &chip->onfi_params; + int i, j; + int val; + + /* Try ONFI for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); + if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || + chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + for (i = 0; i < 3; i++) { + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + le16_to_cpu(p->crc)) { + break; + } + } + + if (i == 3) { + pr_err("Could not find valid ONFI parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 5)) + chip->onfi_version = 23; + else if (val & (1 << 4)) + chip->onfi_version = 22; + else if (val & (1 << 3)) + chip->onfi_version = 21; + else if (val & (1 << 2)) + chip->onfi_version = 20; + else if (val & (1 << 1)) + chip->onfi_version = 10; + + if (!chip->onfi_version) { + pr_info("unsupported ONFI version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* See erasesize comment */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + if (p->ecc_bits != 0xff) { + chip->ecc_strength_ds = p->ecc_bits; + chip->ecc_step_ds = 512; + } else if (chip->onfi_version >= 21 && + (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(mtd, chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } else { + pr_warn("Could not retrieve ONFI ECC requirements\n"); + } + + if (p->jedec_id == NAND_MFR_MICRON) + nand_onfi_detect_micron(chip, p); + + return 1; +} +#else +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + return 0; +} +#endif + +/* + * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_jedec_params *p = &chip->jedec_params; + struct jedec_ecc_info *ecc; + int val; + int i, j; + + /* Try JEDEC for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); + if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'C') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); + for (i = 0; i < 3; i++) { + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == + le16_to_cpu(p->crc)) + break; + } + + if (i == 3) { + pr_err("Could not find valid JEDEC parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 2)) + chip->jedec_version = 10; + else if (val & (1 << 1)) + chip->jedec_version = 1; /* vendor specific version */ + + if (!chip->jedec_version) { + pr_info("unsupported JEDEC version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + /* ECC info */ + ecc = &p->ecc_info[0]; + + if (ecc->codeword_size >= 9) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } else { + pr_warn("Invalid codeword size\n"); + } + + return 1; +} + +/* + * nand_id_has_period - Check if an ID string has a given wraparound period + * @id_data: the ID string + * @arrlen: the length of the @id_data array + * @period: the period of repitition + * + * Check if an ID string is repeated within a given sequence of bytes at + * specific repetition interval period (e.g., {0x20,0x01,0x7F,0x20} has a + * period of 3). This is a helper function for nand_id_len(). Returns non-zero + * if the repetition has a period of @period; otherwise, returns zero. + */ +static int nand_id_has_period(u8 *id_data, int arrlen, int period) +{ + int i, j; + for (i = 0; i < period; i++) + for (j = i + period; j < arrlen; j += period) + if (id_data[i] != id_data[j]) + return 0; + return 1; +} + +/* + * nand_id_len - Get the length of an ID string returned by CMD_READID + * @id_data: the ID string + * @arrlen: the length of the @id_data array + + * Returns the length of the ID string, according to known wraparound/trailing + * zero patterns. If no pattern exists, returns the length of the array. + */ +static int nand_id_len(u8 *id_data, int arrlen) +{ + int last_nonzero, period; + + /* Find last non-zero byte */ + for (last_nonzero = arrlen - 1; last_nonzero >= 0; last_nonzero--) + if (id_data[last_nonzero]) + break; + + /* All zeros */ + if (last_nonzero < 0) + return 0; + + /* Calculate wraparound period */ + for (period = 1; period < arrlen; period++) + if (nand_id_has_period(id_data, arrlen, period)) + break; + + /* There's a repeated pattern */ + if (period < arrlen) + return period; + + /* There are trailing zeros */ + if (last_nonzero < arrlen - 1) + return last_nonzero + 1; + + /* No pattern detected */ + return arrlen; +} + +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + +/* + * Many new NAND share similar device ID codes, which represent the size of the + * chip. The rest of the parameters must be decoded according to generic or + * manufacturer-specific "extended ID" decoding patterns. + */ +static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, + u8 id_data[8], int *busw) +{ + int extid, id_len; + /* The 3rd id byte holds MLC / multichip data */ + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + /* The 4th id byte is the important one */ + extid = id_data[3]; + + id_len = nand_id_len(id_data, 8); + + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) + * Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22) + * + * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung + * ID to decide what to do. + */ + if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && + !nand_is_slc(chip) && id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 1: + mtd->oobsize = 128; + break; + case 2: + mtd->oobsize = 218; + break; + case 3: + mtd->oobsize = 400; + break; + case 4: + mtd->oobsize = 436; + break; + case 5: + mtd->oobsize = 512; + break; + case 6: + mtd->oobsize = 640; + break; + case 7: + default: /* Other cases are "reserved" (unknown) */ + mtd->oobsize = 1024; + break; + } + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + *busw = 0; + } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && + !nand_is_slc(chip)) { + unsigned int tmp; + + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + switch (((extid >> 2) & 0x04) | (extid & 0x03)) { + case 0: + mtd->oobsize = 128; + break; + case 1: + mtd->oobsize = 224; + break; + case 2: + mtd->oobsize = 448; + break; + case 3: + mtd->oobsize = 64; + break; + case 4: + mtd->oobsize = 32; + break; + case 5: + mtd->oobsize = 16; + break; + default: + mtd->oobsize = 640; + break; + } + extid >>= 2; + /* Calc blocksize */ + tmp = ((extid >> 1) & 0x04) | (extid & 0x03); + if (tmp < 0x03) + mtd->erasesize = (128 * 1024) << tmp; + else if (tmp == 0x03) + mtd->erasesize = 768 * 1024; + else + mtd->erasesize = (64 * 1024) << tmp; + *busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + nand_is_slc(chip) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } + + } +} + +/* + * Old devices have chip data hardcoded in the device ID table. nand_decode_id + * decodes a matching ID table entry and assigns the MTD size parameters for + * the chip. + */ +static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, + struct nand_flash_dev *type, u8 id_data[8], + int *busw) +{ + int maf_id = id_data[0]; + + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + *busw = type->options & NAND_BUSWIDTH_16; + + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; + + /* + * Check for Spansion/AMD ID + repeating 5th, 6th byte since + * some Spansion chips have erasesize that conflicts with size + * listed in nand_ids table. + * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39) + */ + if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00 + && id_data[6] == 0x00 && id_data[7] == 0x00 + && mtd->writesize == 512) { + mtd->erasesize = 128 * 1024; + mtd->erasesize <<= ((id_data[3] & 0x03) << 1); + } +} + +/* + * Set the bad block marker/indicator (BBM/BBI) patterns according to some + * heuristic patterns using various detected parameters (e.g., manufacturer, + * page size, cell-type information). + */ +static void nand_decode_bbm_options(struct mtd_info *mtd, + struct nand_chip *chip, u8 id_data[8]) +{ + int maf_id = id_data[0]; + + /* Set the bad block position */ + if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16)) + chip->badblockpos = NAND_LARGE_BADBLOCK_POS; + else + chip->badblockpos = NAND_SMALL_BADBLOCK_POS; + + /* + * Bad block marker is stored in the last page of each block on Samsung + * and Hynix MLC devices; stored in first two pages of each block on + * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, + * AMD/Spansion, and Macronix. All others scan only the first page. + */ + if (!nand_is_slc(chip) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX)) + chip->bbt_options |= NAND_BBT_SCANLASTPAGE; + else if ((nand_is_slc(chip) && + (maf_id == NAND_MFR_SAMSUNG || + maf_id == NAND_MFR_HYNIX || + maf_id == NAND_MFR_TOSHIBA || + maf_id == NAND_MFR_AMD || + maf_id == NAND_MFR_MACRONIX)) || + (mtd->writesize == 2048 && + maf_id == NAND_MFR_MICRON)) + chip->bbt_options |= NAND_BBT_SCAN2NDPAGE; +} + +static inline bool is_full_id_nand(struct nand_flash_dev *type) +{ + return type->id_len; +} + +static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, + struct nand_flash_dev *type, u8 *id_data, int *busw) +{ + if (!strncmp((char *)type->id, (char *)id_data, type->id_len)) { + mtd->writesize = type->pagesize; + mtd->erasesize = type->erasesize; + mtd->oobsize = type->oobsize; + + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); + chip->chipsize = (uint64_t)type->chipsize << 20; + chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); + chip->onfi_timing_mode_default = + type->onfi_timing_mode_default; + + *busw = type->options & NAND_BUSWIDTH_16; + + if (!mtd->name) + mtd->name = type->name; + + return true; + } + return false; +} + +/* + * Get the flash and manufacturer id and lookup if the type is supported. + */ +struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, + struct nand_chip *chip, + int *maf_id, int *dev_id, + struct nand_flash_dev *type) +{ + int busw; + int i, maf_idx; + u8 id_data[8]; + + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up. + */ + nand_reset(chip, 0); + + /* Select the device */ + chip->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + *maf_id = chip->read_byte(mtd); + *dev_id = chip->read_byte(mtd); + + /* + * Try again to make sure, as some systems the bus-hold or other + * interface concerns can cause random data which looks like a + * possibly credible NAND flash to appear. If the two results do + * not match, ignore the device completely. + */ + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read entire ID string */ + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); + + if (id_data[0] != *maf_id || id_data[1] != *dev_id) { + pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", + *maf_id, *dev_id, id_data[0], id_data[1]); + return ERR_PTR(-ENODEV); + } + + if (!type) + type = nand_flash_ids; + + for (; type->name != NULL; type++) { + if (is_full_id_nand(type)) { + if (find_full_id_nand(mtd, chip, type, id_data, &busw)) + goto ident_done; + } else if (*dev_id == type->dev_id) { + break; + } + } + + chip->onfi_version = 0; + if (!type->name || !type->pagesize) { + /* Check if the chip is ONFI compliant */ + if (nand_flash_detect_onfi(mtd, chip, &busw)) + goto ident_done; + + /* Check if the chip is JEDEC compliant */ + if (nand_flash_detect_jedec(mtd, chip, &busw)) + goto ident_done; + } + + if (!type->name) + return ERR_PTR(-ENODEV); + + if (!mtd->name) + mtd->name = type->name; + + chip->chipsize = (uint64_t)type->chipsize << 20; + + if (!type->pagesize) { + /* Decode parameters from extended ID */ + nand_decode_ext_id(mtd, chip, id_data, &busw); + } else { + nand_decode_id(mtd, chip, type, id_data, &busw); + } + /* Get chip options */ + chip->options |= type->options; + + /* + * Check if chip is not a Samsung device. Do not clear the + * options for chips which do not have an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; +ident_done: + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } + + if (chip->options & NAND_BUSWIDTH_AUTO) { + WARN_ON(chip->options & NAND_BUSWIDTH_16); + chip->options |= busw; + nand_set_defaults(chip, busw); + } else if (busw != (chip->options & NAND_BUSWIDTH_16)) { + /* + * Check, if buswidth is correct. Hardware drivers should set + * chip correct! + */ + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + *maf_id, *dev_id); + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name); + pr_warn("bus width %d instead %d bit\n", + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); + return ERR_PTR(-EINVAL); + } + + nand_decode_bbm_options(mtd, chip, id_data); + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + /* Convert chipsize to number of pages per chip -1 */ + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; + + chip->bbt_erase_shift = chip->phys_erase_shift = + ffs(mtd->erasesize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else { + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); + chip->chip_shift += 32 - 1; + } + + if (chip->chip_shift - chip->page_shift > 16) + chip->options |= NAND_ROW_ADDR_3; + + chip->badblockbits = 8; + chip->erase = single_erase; + + /* Do not replace user supplied command function! */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", + *maf_id, *dev_id); + +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + if (chip->onfi_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->onfi_params.model); + else if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); +#else + if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); + + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); +#endif + + pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", + mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); + return type; +} +EXPORT_SYMBOL(nand_get_flash_type); + +#if CONFIG_IS_ENABLED(OF_CONTROL) +DECLARE_GLOBAL_DATA_PTR; + +static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node) +{ + int ret, ecc_mode = -1, ecc_strength, ecc_step; + const void *blob = gd->fdt_blob; + const char *str; + + ret = fdtdec_get_int(blob, node, "nand-bus-width", -1); + if (ret == 16) + chip->options |= NAND_BUSWIDTH_16; + + if (fdtdec_get_bool(blob, node, "nand-on-flash-bbt")) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + str = fdt_getprop(blob, node, "nand-ecc-mode", NULL); + if (str) { + if (!strcmp(str, "none")) + ecc_mode = NAND_ECC_NONE; + else if (!strcmp(str, "soft")) + ecc_mode = NAND_ECC_SOFT; + else if (!strcmp(str, "hw")) + ecc_mode = NAND_ECC_HW; + else if (!strcmp(str, "hw_syndrome")) + ecc_mode = NAND_ECC_HW_SYNDROME; + else if (!strcmp(str, "hw_oob_first")) + ecc_mode = NAND_ECC_HW_OOB_FIRST; + else if (!strcmp(str, "soft_bch")) + ecc_mode = NAND_ECC_SOFT_BCH; + } + + + ecc_strength = fdtdec_get_int(blob, node, "nand-ecc-strength", -1); + ecc_step = fdtdec_get_int(blob, node, "nand-ecc-step-size", -1); + + if ((ecc_step >= 0 && !(ecc_strength >= 0)) || + (!(ecc_step >= 0) && ecc_strength >= 0)) { + pr_err("must set both strength and step size in DT\n"); + return -EINVAL; + } + + if (ecc_mode >= 0) + chip->ecc.mode = ecc_mode; + + if (ecc_strength >= 0) + chip->ecc.strength = ecc_strength; + + if (ecc_step > 0) + chip->ecc.size = ecc_step; + + if (fdt_getprop(blob, node, "nand-ecc-maximize", NULL)) + chip->ecc.options |= NAND_ECC_MAXIMIZE; + + return 0; +} +#else +static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, int node) +{ + return 0; +} +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ + +/** + * nand_scan_ident - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * @table: alternative NAND ID table + * + * This is the first phase of the normal nand_scan() function. It reads the + * flash ID and sets up MTD fields accordingly. + * + */ +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) +{ + int i, nand_maf_id, nand_dev_id; + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_flash_dev *type; + int ret; + + if (chip->flash_node) { + ret = nand_dt_init(mtd, chip, chip->flash_node); + if (ret) + return ret; + } + + /* Set the default functions */ + nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); + + /* Read the flash type */ + type = nand_get_flash_type(mtd, chip, &nand_maf_id, + &nand_dev_id, table); + + if (IS_ERR(type)) { + if (!(chip->options & NAND_SCAN_SILENT_NODEV)) + pr_warn("No NAND device found\n"); + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + /* Initialize the ->data_interface field. */ + ret = nand_init_data_interface(chip); + if (ret) + return ret; + + /* + * Setup the data interface correctly on the chip and controller side. + * This explicit call to nand_setup_data_interface() is only required + * for the first die, because nand_reset() has been called before + * ->data_interface and ->default_onfi_timing_mode were set. + * For the other dies, nand_reset() will automatically switch to the + * best mode for us. + */ + ret = nand_setup_data_interface(chip, 0); + if (ret) + return ret; + + chip->select_chip(mtd, -1); + + /* Check for a chip array */ + for (i = 1; i < maxchips; i++) { + /* See comment in nand_get_flash_type for reset */ + nand_reset(chip, i); + + chip->select_chip(mtd, i); + /* Send the command for reading device ID */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + /* Read manufacturer and device IDs */ + if (nand_maf_id != chip->read_byte(mtd) || + nand_dev_id != chip->read_byte(mtd)) { + chip->select_chip(mtd, -1); + break; + } + chip->select_chip(mtd, -1); + } + +#ifdef DEBUG + if (i > 1) + pr_info("%d chips detected\n", i); +#endif + + /* Store the number of chips and calc total size for mtd */ + chip->numchips = i; + mtd->size = i * chip->chipsize; + + return 0; +} +EXPORT_SYMBOL(nand_scan_ident); + +/** + * nand_check_ecc_caps - check the sanity of preset ECC settings + * @chip: nand chip info structure + * @caps: ECC caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * When ECC step size and strength are already set, check if they are supported + * by the controller and the calculated ECC bytes fit within the chip's OOB. + * On success, the calculated ECC bytes is set. + */ +int nand_check_ecc_caps(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int preset_step = chip->ecc.size; + int preset_strength = chip->ecc.strength; + int nsteps, ecc_bytes; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + if (!preset_step || !preset_strength) + return -ENODATA; + + nsteps = mtd->writesize / preset_step; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + + if (stepinfo->stepsize != preset_step) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + if (stepinfo->strengths[j] != preset_strength) + continue; + + ecc_bytes = caps->calc_ecc_bytes(preset_step, + preset_strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + return ecc_bytes; + + if (ecc_bytes * nsteps > oobavail) { + pr_err("ECC (step, strength) = (%d, %d) does not fit in OOB", + preset_step, preset_strength); + return -ENOSPC; + } + + chip->ecc.bytes = ecc_bytes; + + return 0; + } + } + + pr_err("ECC (step, strength) = (%d, %d) not supported on this controller", + preset_step, preset_strength); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(nand_check_ecc_caps); + +/** + * nand_match_ecc_req - meet the chip's requirement with least ECC bytes + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * If a chip's ECC requirement is provided, try to meet it with the least + * number of ECC bytes (i.e. with the largest number of OOB-free bytes). + * On success, the chosen ECC settings are set. + */ +int nand_match_ecc_req(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int req_step = chip->ecc_step_ds; + int req_strength = chip->ecc_strength_ds; + int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; + int best_step, best_strength, best_ecc_bytes; + int best_ecc_bytes_total = INT_MAX; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + /* No information provided by the NAND chip */ + if (!req_step || !req_strength) + return -ENOTSUPP; + + /* number of correctable bits the chip requires in a page */ + req_corr = mtd->writesize / req_step * req_strength; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + /* + * If both step size and strength are smaller than the + * chip's requirement, it is not easy to compare the + * resulted reliability. + */ + if (step_size < req_step && strength < req_strength) + continue; + + if (mtd->writesize % step_size) + continue; + + nsteps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + ecc_bytes_total = ecc_bytes * nsteps; + + if (ecc_bytes_total > oobavail || + strength * nsteps < req_corr) + continue; + + /* + * We assume the best is to meet the chip's requrement + * with the least number of ECC bytes. + */ + if (ecc_bytes_total < best_ecc_bytes_total) { + best_ecc_bytes_total = ecc_bytes_total; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (best_ecc_bytes_total == INT_MAX) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_match_ecc_req); + +/** + * nand_maximize_ecc - choose the max ECC strength available + * @chip: nand chip info structure + * @caps: ECC engine caps info structure + * @oobavail: OOB size that the ECC engine can use + * + * Choose the max ECC strength that is supported on the controller, and can fit + * within the chip's OOB. On success, the chosen ECC settings are set. + */ +int nand_maximize_ecc(struct nand_chip *chip, + const struct nand_ecc_caps *caps, int oobavail) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_step_info *stepinfo; + int step_size, strength, nsteps, ecc_bytes, corr; + int best_corr = 0; + int best_step = 0; + int best_strength, best_ecc_bytes; + int i, j; + + if (WARN_ON(oobavail < 0)) + return -EINVAL; + + for (i = 0; i < caps->nstepinfos; i++) { + stepinfo = &caps->stepinfos[i]; + step_size = stepinfo->stepsize; + + /* If chip->ecc.size is already set, respect it */ + if (chip->ecc.size && step_size != chip->ecc.size) + continue; + + for (j = 0; j < stepinfo->nstrengths; j++) { + strength = stepinfo->strengths[j]; + + if (mtd->writesize % step_size) + continue; + + nsteps = mtd->writesize / step_size; + + ecc_bytes = caps->calc_ecc_bytes(step_size, strength); + if (WARN_ON_ONCE(ecc_bytes < 0)) + continue; + + if (ecc_bytes * nsteps > oobavail) + continue; + + corr = strength * nsteps; + + /* + * If the number of correctable bits is the same, + * bigger step_size has more reliability. + */ + if (corr > best_corr || + (corr == best_corr && step_size > best_step)) { + best_corr = corr; + best_step = step_size; + best_strength = strength; + best_ecc_bytes = ecc_bytes; + } + } + } + + if (!best_corr) + return -ENOTSUPP; + + chip->ecc.size = best_step; + chip->ecc.strength = best_strength; + chip->ecc.bytes = best_ecc_bytes; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_maximize_ecc); + +/* + * Check if the chip configuration meet the datasheet requirements. + + * If our configuration corrects A bits per B bytes and the minimum + * required correction level is X bits per Y bytes, then we must ensure + * both of the following are true: + * + * (1) A / B >= X / Y + * (2) A >= X + * + * Requirement (1) ensures we can correct for the required bitflip density. + * Requirement (2) ensures we can correct even when all bitflips are clumped + * in the same sector. + */ +static bool nand_ecc_strength_good(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int corr, ds_corr; + + if (ecc->size == 0 || chip->ecc_step_ds == 0) + /* Not enough information */ + return true; + + /* + * We get the number of corrected bits per page to compare + * the correction density. + */ + corr = (mtd->writesize * ecc->strength) / ecc->size; + ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds; + + return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; +} + +static bool invalid_ecc_page_accessors(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (nand_standard_page_accessors(ecc)) + return false; + + /* + * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND + * controller driver implements all the page accessors because + * default helpers are not suitable when the core does not + * send the READ0/PAGEPROG commands. + */ + return (!ecc->read_page || !ecc->write_page || + !ecc->read_page_raw || !ecc->write_page_raw || + (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) || + (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage && + ecc->hwctl && ecc->calculate)); +} + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int i; + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (invalid_ecc_page_accessors(chip)) { + pr_err("Invalid ECC page accessors setup\n"); + return -EINVAL; + } + + if (!(chip->options & NAND_OWN_BUFFERS)) { + nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL); + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + /* + * If no default placement scheme is given, select an appropriate one. + */ + if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { + switch (mtd->oobsize) { + case 8: + ecc->layout = &nand_oob_8; + break; + case 16: + ecc->layout = &nand_oob_16; + break; + case 64: + ecc->layout = &nand_oob_64; + break; + case 128: + ecc->layout = &nand_oob_128; + break; + default: + pr_warn("No oob scheme defined for oobsize %d\n", + mtd->oobsize); + BUG(); + } + } + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* + * Check ECC mode, default to software if 3byte/512byte hardware ECC is + * selected and we have 256 byte pagesize fallback to software ECC + */ + + switch (ecc->mode) { + case NAND_ECC_HW_OOB_FIRST: + /* Similar to NAND_ECC_HW, but a separate read_page handle */ + if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { + pr_warn("No ECC functions supplied; hardware ECC not possible\n"); + BUG(); + } + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc_oob_first; + + case NAND_ECC_HW: + /* Use standard hwecc read page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) + ecc->write_subpage = nand_write_subpage_hwecc; + + case NAND_ECC_HW_SYNDROME: + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { + pr_warn("No ECC functions supplied; hardware ECC not possible\n"); + BUG(); + } + /* Use standard syndrome read/write page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= ecc->size) { + if (!ecc->strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } + break; + } + pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + ecc->size, mtd->writesize); + ecc->mode = NAND_ECC_SOFT; + + case NAND_ECC_SOFT: + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; + break; + + case NAND_ECC_SOFT_BCH: + if (!mtd_nand_has_bch()) { + pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + BUG(); + } + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.strength values + * to select how many bits are correctable. Otherwise, default + * to 4 bits for large page devices. + */ + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->strength = 4; + } + + /* See nand_bch_init() for details. */ + ecc->bytes = 0; + ecc->priv = nand_bch_init(mtd); + if (!ecc->priv) { + pr_warn("BCH ECC initialization failed!\n"); + BUG(); + } + break; + + case NAND_ECC_NONE: + pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->write_oob = nand_write_oob_std; + ecc->size = mtd->writesize; + ecc->bytes = 0; + ecc->strength = 0; + break; + + default: + pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); + BUG(); + } + + /* For many systems, the standard OOB write also works for raw */ + if (!ecc->read_oob_raw) + ecc->read_oob_raw = ecc->read_oob; + if (!ecc->write_oob_raw) + ecc->write_oob_raw = ecc->write_oob; + + /* + * The number of bytes available for a client to place data into + * the out of band area. + */ + mtd->oobavail = 0; + if (ecc->layout) { + for (i = 0; ecc->layout->oobfree[i].length; i++) + mtd->oobavail += ecc->layout->oobfree[i].length; + } + + /* ECC sanity check: warn if it's too weak */ + if (!nand_ecc_strength_good(mtd)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + mtd->name); + + /* + * Set the number of read / write steps for one page depending on ECC + * mode. + */ + ecc->steps = mtd->writesize / ecc->size; + if (ecc->steps * ecc->size != mtd->writesize) { + pr_warn("Invalid ECC parameters\n"); + BUG(); + } + ecc->total = ecc->steps * ecc->bytes; + + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { + switch (ecc->steps) { + case 2: + mtd->subpage_sft = 1; + break; + case 4: + case 8: + case 16: + mtd->subpage_sft = 2; + break; + } + } + chip->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* Initialize state */ + chip->state = FL_READY; + + /* Invalidate the pagebuffer reference */ + chip->pagebuf = -1; + + /* Large page NAND with SOFT_ECC should support subpage reads */ + switch (ecc->mode) { + case NAND_ECC_SOFT: + case NAND_ECC_SOFT_BCH: + if (chip->page_shift > 9) + chip->options |= NAND_SUBPAGE_READ; + break; + + default: + break; + } + + /* Fill in remaining MTD driver data */ + mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; + mtd->_erase = nand_erase; + mtd->_panic_write = panic_nand_write; + mtd->_read_oob = nand_read_oob; + mtd->_write_oob = nand_write_oob; + mtd->_sync = nand_sync; + mtd->_lock = NULL; + mtd->_unlock = NULL; + mtd->_block_isreserved = nand_block_isreserved; + mtd->_block_isbad = nand_block_isbad; + mtd->_block_markbad = nand_block_markbad; + mtd->writebufsize = mtd->writesize; + + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); + + return 0; +} +EXPORT_SYMBOL(nand_scan_tail); + +/** + * nand_scan - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * @maxchips: number of chips to scan for + * + * This fills out all the uninitialized function pointers with the defaults. + * The flash ID is read and the mtd/chip structures are filled with the + * appropriate values. + */ +int nand_scan(struct mtd_info *mtd, int maxchips) +{ + int ret; + + ret = nand_scan_ident(mtd, maxchips, NULL); + if (!ret) + ret = nand_scan_tail(mtd); + return ret; +} +EXPORT_SYMBOL(nand_scan); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steven J. Hill "); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c new file mode 100644 index 0000000000..ba785c5d53 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -0,0 +1,1373 @@ +/* + * Overview: + * Bad block table support for the NAND driver + * + * Copyright © 2004 Thomas Gleixner (tglx@linutronix.de) + * + * This program 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. + * + * Description: + * + * When nand_scan_bbt is called, then it tries to find the bad block table + * depending on the options in the BBT descriptor(s). If no flash based BBT + * (NAND_BBT_USE_FLASH) is specified then the device is scanned for factory + * marked good / bad blocks. This information is used to create a memory BBT. + * Once a new bad block is discovered then the "factory" information is updated + * on the device. + * If a flash based BBT is specified then the function first tries to find the + * BBT on flash. If a BBT is found then the contents are read and the memory + * based BBT is created. If a mirrored BBT is selected then the mirror is + * searched too and the versions are compared. If the mirror has a greater + * version number, then the mirror BBT is used to build the memory based BBT. + * If the tables are not versioned, then we "or" the bad block information. + * If one of the BBTs is out of date or does not exist it is (re)created. + * If no BBT exists at all then the device is scanned for factory marked + * good / bad blocks and the bad block tables are created. + * + * For manufacturer created BBTs like the one found on M-SYS DOC devices + * the BBT is searched and read but never created + * + * The auto generated bad block table is located in the last good blocks + * of the device. The table is mirrored, so it can be updated eventually. + * The table is marked in the OOB area with an ident pattern and a version + * number which indicates which of both tables is more up to date. If the NAND + * controller needs the complete OOB area for the ECC information then the + * option NAND_BBT_NO_OOB should be used (along with NAND_BBT_USE_FLASH, of + * course): it moves the ident pattern and the version byte into the data area + * and the OOB area will remain untouched. + * + * The table uses 2 bits per block + * 11b: block is good + * 00b: block is factory marked bad + * 01b, 10b: block is marked bad due to wear + * + * The memory bad block table uses the following scheme: + * 00b: block is good + * 01b: block is marked bad due to wear + * 10b: block is reserved (to protect the bbt area) + * 11b: block is factory marked bad + * + * Multichip devices like DOC store the bad block info per floor. + * + * Following assumptions are made: + * - bbts start at a page boundary, if autolocated on a block boundary + * - the space necessary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 + +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) +{ + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} + +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} + +static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) +{ + if (memcmp(buf, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * check_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. + */ +static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return check_pattern_no_oob(buf, td); + + /* Compare the pattern */ + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) + return -1; + + return 0; +} + +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block tables and + * good / bad block identifiers. Same as check_pattern, but no optional empty + * check. + */ +static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) +{ + /* Compare the pattern */ + if (memcmp(buf + td->offs, td->pattern, td->len)) + return -1; + return 0; +} + +/** + * add_marker_len - compute the length of the marker in data area + * @td: BBT descriptor used for computation + * + * The length will be 0 if the marker is located in OOB area. + */ +static u32 add_marker_len(struct nand_bbt_descr *td) +{ + u32 len; + + if (!(td->options & NAND_BBT_NO_OOB)) + return 0; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + return len; +} + +/** + * read_bbt - [GENERIC] Read the bad block table starting from page + * @mtd: MTD device structure + * @buf: temporary buffer + * @page: the starting page + * @num: the number of bbt descriptors to read + * @td: the bbt describtion table + * @offs: block number offset in the table + * + * Read the bad block table starting from page. + */ +static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, + struct nand_bbt_descr *td, int offs) +{ + int res, ret = 0, i, j, act = 0; + struct nand_chip *this = mtd_to_nand(mtd); + size_t retlen, len, totlen; + loff_t from; + int bits = td->options & NAND_BBT_NRBITS_MSK; + uint8_t msk = (uint8_t)((1 << bits) - 1); + u32 marker_len; + int reserved_block_code = td->reserved_block_code; + + totlen = (num * bits) >> 3; + marker_len = add_marker_len(td); + from = ((loff_t)page) << this->page_shift; + + while (totlen) { + len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); + if (marker_len) { + /* + * In case the BBT marker is not in the OOB area it + * will be just in the first page. + */ + len -= marker_len; + from += marker_len; + marker_len = 0; + } + res = mtd_read(mtd, from, len, &retlen, buf); + if (res < 0) { + if (mtd_is_eccerr(res)) { + pr_info("nand_bbt: ECC error in BBT at 0x%012llx\n", + from & ~mtd->writesize); + return res; + } else if (mtd_is_bitflip(res)) { + pr_info("nand_bbt: corrected error in BBT at 0x%012llx\n", + from & ~mtd->writesize); + ret = res; + } else { + pr_info("nand_bbt: error reading BBT\n"); + return res; + } + } + + /* Analyse data */ + for (i = 0; i < len; i++) { + uint8_t dat = buf[i]; + for (j = 0; j < 8; j += bits, act++) { + uint8_t tmp = (dat >> j) & msk; + if (tmp == msk) + continue; + if (reserved_block_code && (tmp == reserved_block_code)) { + pr_info("nand_read_bbt: reserved block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_RESERVED); + mtd->ecc_stats.bbtblocks++; + continue; + } + /* + * Leave it for now, if it's matured we can + * move this message to pr_debug. + */ + pr_info("nand_read_bbt: bad block at 0x%012llx\n", + (loff_t)(offs + act) << + this->bbt_erase_shift); + /* Factory marked bad or worn out? */ + if (tmp == 0) + bbt_mark_entry(this, offs + act, + BBT_BLOCK_FACTORY_BAD); + else + bbt_mark_entry(this, offs + act, + BBT_BLOCK_WORN); + mtd->ecc_stats.badblocks++; + } + } + totlen -= len; + from += len; + } + return ret; +} + +/** + * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @chip: read the table for a specific chip, -1 read all chips; applies only if + * NAND_BBT_PERCHIP option is set + * + * Read the bad block table for all chips starting at a given page. We assume + * that the bbt bits are in consecutive order. + */ +static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int res = 0, i; + + if (td->options & NAND_BBT_PERCHIP) { + int offs = 0; + for (i = 0; i < this->numchips; i++) { + if (chip == -1 || chip == i) + res = read_bbt(mtd, buf, td->pages[i], + this->chipsize >> this->bbt_erase_shift, + td, offs); + if (res) + return res; + offs += this->chipsize >> this->bbt_erase_shift; + } + } else { + res = read_bbt(mtd, buf, td->pages[0], + mtd->size >> this->bbt_erase_shift, td, 0); + if (res) + return res; + } + return 0; +} + +/* BBT marker is in the first page, no OOB */ +static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + struct nand_bbt_descr *td) +{ + size_t retlen; + size_t len; + + len = td->len; + if (td->options & NAND_BBT_VERSION) + len++; + + return mtd_read(mtd, offs, len, &retlen, buf); +} + +/** + * scan_read_oob - [GENERIC] Scan data+OOB region to buffer + * @mtd: MTD device structure + * @buf: temporary buffer + * @offs: offset at which to scan + * @len: length of data region to read + * + * Scan read data from data+OOB. May traverse multiple pages, interleaving + * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest" + * ECC condition (error or bitflip). May quit on the first (non-ECC) error. + */ +static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len) +{ + struct mtd_oob_ops ops; + int res, ret = 0; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + + while (len > 0) { + ops.datbuf = buf; + ops.len = min(len, (size_t)mtd->writesize); + ops.oobbuf = buf + ops.len; + + res = mtd_read_oob(mtd, offs, &ops); + if (res) { + if (!mtd_is_bitflip_or_eccerr(res)) + return res; + else if (mtd_is_eccerr(res) || !ret) + ret = res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + offs += mtd->writesize; + } + return ret; +} + +static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, + size_t len, struct nand_bbt_descr *td) +{ + if (td->options & NAND_BBT_NO_OOB) + return scan_read_data(mtd, buf, offs, td); + else + return scan_read_oob(mtd, buf, offs, len); +} + +/* Scan write data with oob to flash */ +static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, + uint8_t *buf, uint8_t *oob) +{ + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.datbuf = buf; + ops.oobbuf = oob; + ops.len = len; + + return mtd_write_oob(mtd, offs, &ops); +} + +static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + u32 ver_offs = td->veroffs; + + if (!(td->options & NAND_BBT_NO_OOB)) + ver_offs += mtd->writesize; + return ver_offs; +} + +/** + * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Read the bad block table(s) for all chips starting at a given page. We + * assume that the bbt bits are in consecutive order. + */ +static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + /* Read the primary version, if available */ + if (td->options & NAND_BBT_VERSION) { + scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift, + mtd->writesize, td); + td->version[0] = buf[bbt_get_ver_offs(mtd, td)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + td->pages[0], td->version[0]); + } + + /* Read the mirror version, if available */ + if (md && (md->options & NAND_BBT_VERSION)) { + scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift, + mtd->writesize, md); + md->version[0] = buf[bbt_get_ver_offs(mtd, md)]; + pr_info("Bad block table at page %d, version 0x%02X\n", + md->pages[0], md->version[0]); + } +} + +/* Scan a given block partially */ +static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, + loff_t offs, uint8_t *buf, int numpages) +{ + struct mtd_oob_ops ops; + int j, ret; + + ops.ooblen = mtd->oobsize; + ops.oobbuf = buf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + for (j = 0; j < numpages; j++) { + /* + * Read the full oob until read_oob is fixed to handle single + * byte reads for 16 bit buswidth. + */ + ret = mtd_read_oob(mtd, offs, &ops); + /* Ignore ECC errors when checking for BBM */ + if (ret && !mtd_is_bitflip_or_eccerr(ret)) + return ret; + + if (check_short_pattern(buf, bd)) + return 1; + + offs += mtd->writesize; + } + return 0; +} + +/** + * create_bbt - [GENERIC] Create a bad block table by scanning the device + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * @chip: create the table for a specific chip, -1 read all chips; applies only + * if NAND_BBT_PERCHIP option is set + * + * Create a bad block table by scanning the device for the given good/bad block + * identify pattern. + */ +static int create_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *bd, int chip) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, numblocks, numpages; + int startblock; + loff_t from; + + pr_info("Scanning device for bad blocks\n"); + + if (bd->options & NAND_BBT_SCAN2NDPAGE) + numpages = 2; + else + numpages = 1; + + if (chip == -1) { + numblocks = mtd->size >> this->bbt_erase_shift; + startblock = 0; + from = 0; + } else { + if (chip >= this->numchips) { + pr_warn("create_bbt(): chipnr (%d) > available chips (%d)\n", + chip + 1, this->numchips); + return -EINVAL; + } + numblocks = this->chipsize >> this->bbt_erase_shift; + startblock = chip * numblocks; + numblocks += startblock; + from = (loff_t)startblock << this->bbt_erase_shift; + } + + if (this->bbt_options & NAND_BBT_SCANLASTPAGE) + from += mtd->erasesize - (mtd->writesize * numpages); + + for (i = startblock; i < numblocks; i++) { + int ret; + + BUG_ON(bd->options & NAND_BBT_NO_OOB); + + ret = scan_block_fast(mtd, bd, from, buf, numpages); + if (ret < 0) + return ret; + + if (ret) { + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); + pr_warn("Bad eraseblock %d at 0x%012llx\n", + i, (unsigned long long)from); + mtd->ecc_stats.badblocks++; + } + + from += (1 << this->bbt_erase_shift); + } + return 0; +} + +/** + * search_bbt - [GENERIC] scan the device for a specific bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * + * Read the bad block table by searching for a given ident pattern. Search is + * preformed either from the beginning up or from the end of the device + * downwards. The search starts always at the start of a block. If the option + * NAND_BBT_PERCHIP is given, each chip is searched for a bbt, which contains + * the bad block information of this chip. This is necessary to provide support + * for certain DOC devices. + * + * The bbt ident pattern resides in the oob area of the first page in a block. + */ +static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, chips; + int startblock, block, dir; + int scanlen = mtd->writesize + mtd->oobsize; + int bbtblocks; + int blocktopage = this->bbt_erase_shift - this->page_shift; + + /* Search direction top -> down? */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = (mtd->size >> this->bbt_erase_shift) - 1; + dir = -1; + } else { + startblock = 0; + dir = 1; + } + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + bbtblocks = this->chipsize >> this->bbt_erase_shift; + startblock &= bbtblocks - 1; + } else { + chips = 1; + bbtblocks = mtd->size >> this->bbt_erase_shift; + } + + for (i = 0; i < chips; i++) { + /* Reset version information */ + td->version[i] = 0; + td->pages[i] = -1; + /* Scan the maximum number of blocks */ + for (block = 0; block < td->maxblocks; block++) { + + int actblock = startblock + dir * block; + loff_t offs = (loff_t)actblock << this->bbt_erase_shift; + + /* Read first page */ + scan_read(mtd, buf, offs, mtd->writesize, td); + if (!check_pattern(buf, scanlen, mtd->writesize, td)) { + td->pages[i] = actblock << blocktopage; + if (td->options & NAND_BBT_VERSION) { + offs = bbt_get_ver_offs(mtd, td); + td->version[i] = buf[offs]; + } + break; + } + } + startblock += this->chipsize >> this->bbt_erase_shift; + } + /* Check, if we found a bbt for each requested chip */ + for (i = 0; i < chips; i++) { + if (td->pages[i] == -1) + pr_warn("Bad block table not found for chip %d\n", i); + else + pr_info("Bad block table found at page %d, version 0x%02X\n", + td->pages[i], td->version[i]); + } + return 0; +} + +/** + * search_read_bbts - [GENERIC] scan the device for bad block table(s) + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * + * Search and read the bad block table(s). + */ +static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, + struct nand_bbt_descr *md) +{ + /* Search the primary table */ + search_bbt(mtd, buf, td); + + /* Search the mirror table */ + if (md) + search_bbt(mtd, buf, md); +} + +/** + * write_bbt - [GENERIC] (Re)write the bad block table + * @mtd: MTD device structure + * @buf: temporary buffer + * @td: descriptor for the bad block table + * @md: descriptor for the bad block table mirror + * @chipsel: selector for a specific chip, -1 for all + * + * (Re)write the bad block table. + */ +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct erase_info einfo; + int i, res, chip = 0; + int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; + int nrchips, pageoffs, ooboffs; + uint8_t msk[4]; + uint8_t rcode = td->reserved_block_code; + size_t retlen, len = 0; + loff_t to; + struct mtd_oob_ops ops; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OPS_PLACE_OOB; + + if (!rcode) + rcode = 0xff; + /* Write bad block table per chip rather than per device? */ + if (td->options & NAND_BBT_PERCHIP) { + numblocks = (int)(this->chipsize >> this->bbt_erase_shift); + /* Full device write or specific chip? */ + if (chipsel == -1) { + nrchips = this->numchips; + } else { + nrchips = chipsel + 1; + chip = chipsel; + } + } else { + numblocks = (int)(mtd->size >> this->bbt_erase_shift); + nrchips = 1; + } + + /* Loop through the chips */ + for (; chip < nrchips; chip++) { + /* + * There was already a version of the table, reuse the page + * This applies for absolute placement too, as we have the + * page nr. in td->pages. + */ + if (td->pages[chip] != -1) { + page = td->pages[chip]; + goto write; + } + + /* + * Automatic placement of the bad block table. Search direction + * top -> down? + */ + if (td->options & NAND_BBT_LASTBLOCK) { + startblock = numblocks * (chip + 1) - 1; + dir = -1; + } else { + startblock = chip * numblocks; + dir = 1; + } + + for (i = 0; i < td->maxblocks; i++) { + int block = startblock + dir * i; + /* Check, if the block is bad */ + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: + continue; + } + page = block << + (this->bbt_erase_shift - this->page_shift); + /* Check, if the block is used by the mirror table */ + if (!md || md->pages[chip] != page) + goto write; + } + pr_err("No space left to write bad block table\n"); + return -ENOSPC; + write: + + /* Set up shift count and masks for the flash table */ + bits = td->options & NAND_BBT_NRBITS_MSK; + msk[2] = ~rcode; + switch (bits) { + case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x01; + break; + case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; + msk[3] = 0x03; + break; + case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; + msk[3] = 0x0f; + break; + case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; + msk[3] = 0xff; + break; + default: return -EINVAL; + } + + to = ((loff_t)page) << this->page_shift; + + /* Must we save the block contents? */ + if (td->options & NAND_BBT_SAVECONTENT) { + /* Make it block aligned */ + to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1); + len = 1 << this->bbt_erase_shift; + res = mtd_read(mtd, to, len, &retlen, buf); + if (res < 0) { + if (retlen != len) { + pr_info("nand_bbt: error reading block for writing the bad block table\n"); + return res; + } + pr_warn("nand_bbt: ECC error while reading block for writing bad block table\n"); + } + /* Read oob data */ + ops.ooblen = (len >> this->page_shift) * mtd->oobsize; + ops.oobbuf = &buf[len]; + res = mtd_read_oob(mtd, to + mtd->writesize, &ops); + if (res < 0 || ops.oobretlen != ops.ooblen) + goto outerr; + + /* Calc the byte offset in the buffer */ + pageoffs = page - (int)(to >> this->page_shift); + offs = pageoffs << this->page_shift; + /* Preset the bbt area with 0xff */ + memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); + ooboffs = len + (pageoffs * mtd->oobsize); + + } else if (td->options & NAND_BBT_NO_OOB) { + ooboffs = 0; + offs = td->len; + /* The version byte */ + if (td->options & NAND_BBT_VERSION) + offs++; + /* Calc length */ + len = (size_t)(numblocks >> sft); + len += offs; + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len); + /* Pattern is located at the begin of first page */ + memcpy(buf, td->pattern, td->len); + } else { + /* Calc length */ + len = (size_t)(numblocks >> sft); + /* Make it page aligned! */ + len = ALIGN(len, mtd->writesize); + /* Preset the buffer with 0xff */ + memset(buf, 0xff, len + + (len >> this->page_shift)* mtd->oobsize); + offs = 0; + ooboffs = len; + /* Pattern is located in oob area of first page */ + memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); + } + + if (td->options & NAND_BBT_VERSION) + buf[ooboffs + td->veroffs] = td->version[chip]; + + /* Walk through the memory table */ + for (i = 0; i < numblocks; i++) { + uint8_t dat; + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); + } + + memset(&einfo, 0, sizeof(einfo)); + einfo.mtd = mtd; + einfo.addr = to; + einfo.len = 1 << this->bbt_erase_shift; + res = nand_erase_nand(mtd, &einfo, 1); + if (res < 0) + goto outerr; + + res = scan_write_bbt(mtd, to, len, buf, + td->options & NAND_BBT_NO_OOB ? NULL : + &buf[len]); + if (res < 0) + goto outerr; + + pr_info("Bad block table written to 0x%012llx, version 0x%02X\n", + (unsigned long long)to, td->version[chip]); + + /* Mark it as used */ + td->pages[chip] = page; + } + return 0; + + outerr: + pr_warn("nand_bbt: error while writing bad block table %d\n", res); + return res; +} + +/** + * nand_memory_bbt - [GENERIC] create a memory based bad block table + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function creates a memory based bbt by scanning the device for + * manufacturer / software marked good / bad blocks. + */ +static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + return create_bbt(mtd, this->buffers->databuf, bd, -1); +} + +/** + * check_create - [GENERIC] create and write bbt(s) if necessary + * @mtd: MTD device structure + * @buf: temporary buffer + * @bd: descriptor for the good/bad block search pattern + * + * The function checks the results of the previous call to read_bbt and creates + * / updates the bbt(s) if necessary. Creation is necessary if no bbt was found + * for the chip/device. Update is necessary if one of the tables is missing or + * the version nr. of one table is less than the other. + */ +static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) +{ + int i, chips, writeops, create, chipsel, res, res2; + struct nand_chip *this = mtd_to_nand(mtd); + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + struct nand_bbt_descr *rd, *rd2; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) + chips = this->numchips; + else + chips = 1; + + for (i = 0; i < chips; i++) { + writeops = 0; + create = 0; + rd = NULL; + rd2 = NULL; + res = res2 = 0; + /* Per chip or per device? */ + chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; + /* Mirrored table available? */ + if (md) { + if (td->pages[i] == -1 && md->pages[i] == -1) { + create = 1; + writeops = 0x03; + } else if (td->pages[i] == -1) { + rd = md; + writeops = 0x01; + } else if (md->pages[i] == -1) { + rd = td; + writeops = 0x02; + } else if (td->version[i] == md->version[i]) { + rd = td; + if (!(td->options & NAND_BBT_VERSION)) + rd2 = md; + } else if (((int8_t)(td->version[i] - md->version[i])) > 0) { + rd = td; + writeops = 0x02; + } else { + rd = md; + writeops = 0x01; + } + } else { + if (td->pages[i] == -1) { + create = 1; + writeops = 0x01; + } else { + rd = td; + } + } + + if (create) { + /* Create the bad block table by scanning the device? */ + if (!(td->options & NAND_BBT_CREATE)) + continue; + + /* Create the table in memory by scanning the chip(s) */ + if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY)) + create_bbt(mtd, buf, bd, chipsel); + + td->version[i] = 1; + if (md) + md->version[i] = 1; + } + + /* Read back first? */ + if (rd) { + res = read_abs_bbt(mtd, buf, rd, chipsel); + if (mtd_is_eccerr(res)) { + /* Mark table as invalid */ + rd->pages[i] = -1; + rd->version[i] = 0; + i--; + continue; + } + } + /* If they weren't versioned, read both */ + if (rd2) { + res2 = read_abs_bbt(mtd, buf, rd2, chipsel); + if (mtd_is_eccerr(res2)) { + /* Mark table as invalid */ + rd2->pages[i] = -1; + rd2->version[i] = 0; + i--; + continue; + } + } + + /* Scrub the flash table(s)? */ + if (mtd_is_bitflip(res) || mtd_is_bitflip(res2)) + writeops = 0x03; + + /* Update version numbers before writing */ + if (md) { + td->version[i] = max(td->version[i], md->version[i]); + md->version[i] = td->version[i]; + } + + /* Write the bad block table to the device? */ + if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + return res; + } + + /* Write the mirror bad block table to the device? */ + if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + if (res < 0) + return res; + } + } + return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd: MTD device structure + * @td: bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent accidental + * erasures / writes. The regions are identified by the mark 0x02. + */ +static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int i, j, chips, block, nrblocks, update; + uint8_t oldval; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chips = this->numchips; + nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); + } else { + chips = 1; + nrblocks = (int)(mtd->size >> this->bbt_erase_shift); + } + + for (i = 0; i < chips; i++) { + if ((td->options & NAND_BBT_ABSPAGE) || + !(td->options & NAND_BBT_WRITE)) { + if (td->pages[i] == -1) + continue; + block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); + continue; + } + update = 0; + if (td->options & NAND_BBT_LASTBLOCK) + block = ((i + 1) * nrblocks) - td->maxblocks; + else + block = i * nrblocks; + for (j = 0; j < td->maxblocks; j++) { + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) + update = 1; + block++; + } + /* + * If we want reserved blocks to be recorded to flash, and some + * new ones have been marked, then we need to update the stored + * bbts. This should only happen once. + */ + if (update && td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); + } +} + +/** + * verify_bbt_descr - verify the bad block description + * @mtd: MTD device structure + * @bd: the table to verify + * + * This functions performs a few sanity checks on the bad block description + * table. + */ +static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u32 pattern_len; + u32 bits; + u32 table_size; + + if (!bd) + return; + + pattern_len = bd->len; + bits = bd->options & NAND_BBT_NRBITS_MSK; + + BUG_ON((this->bbt_options & NAND_BBT_NO_OOB) && + !(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!bits); + + if (bd->options & NAND_BBT_VERSION) + pattern_len++; + + if (bd->options & NAND_BBT_NO_OOB) { + BUG_ON(!(this->bbt_options & NAND_BBT_USE_FLASH)); + BUG_ON(!(this->bbt_options & NAND_BBT_NO_OOB)); + BUG_ON(bd->offs); + if (bd->options & NAND_BBT_VERSION) + BUG_ON(bd->veroffs != bd->len); + BUG_ON(bd->options & NAND_BBT_SAVECONTENT); + } + + if (bd->options & NAND_BBT_PERCHIP) + table_size = this->chipsize >> this->bbt_erase_shift; + else + table_size = mtd->size >> this->bbt_erase_shift; + table_size >>= 3; + table_size *= bits; + if (bd->options & NAND_BBT_NO_OOB) + table_size += pattern_len; + BUG_ON(table_size > (1 << this->bbt_erase_shift)); +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd: MTD device structure + * @bd: descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already available. If + * not it scans the device for manufacturer marked good / bad blocks and writes + * the bad block table(s) to the selected place. + * + * The bad block table memory is allocated here. It must be freed by calling + * the nand_free_bbt function. + */ +static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int len, res; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + len = (mtd->size >> (this->bbt_erase_shift + 2)) ? : 1; + /* + * Allocate memory (2bit per block) and clear the memory bad block + * table. + */ + this->bbt = kzalloc(len, GFP_KERNEL); + if (!this->bbt) + return -ENOMEM; + + /* + * If no primary table decriptor is given, scan the device to build a + * memory based bad block table. + */ + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); + goto err; + } + return 0; + } + verify_bbt_descr(mtd, td); + verify_bbt_descr(mtd, md); + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = vmalloc(len); + if (!buf) { + res = -ENOMEM; + goto err; + } + + /* Is the bbt at a given page? */ + if (td->options & NAND_BBT_ABSPAGE) { + read_abs_bbts(mtd, buf, td, md); + } else { + /* Search the bad block table using a pattern in oob */ + search_read_bbts(mtd, buf, td, md); + } + + res = check_create(mtd, buf, bd); + if (res) + goto err; + + /* Prevent the bbt regions from erasing / writing */ + mark_bbt_region(mtd, td); + if (md) + mark_bbt_region(mtd, md); + + vfree(buf); + return 0; + +err: + kfree(this->bbt); + this->bbt = NULL; + return res; +} + +/** + * nand_update_bbt - update bad block table(s) + * @mtd: MTD device structure + * @offs: the offset of the newly marked block + * + * The function updates the bad block table(s). + */ +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int len, res = 0; + int chip, chipsel; + uint8_t *buf; + struct nand_bbt_descr *td = this->bbt_td; + struct nand_bbt_descr *md = this->bbt_md; + + if (!this->bbt || !td) + return -EINVAL; + + /* Allocate a temporary buffer for one eraseblock incl. oob */ + len = (1 << this->bbt_erase_shift); + len += (len >> this->page_shift) * mtd->oobsize; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Do we have a bbt per chip? */ + if (td->options & NAND_BBT_PERCHIP) { + chip = (int)(offs >> this->chip_shift); + chipsel = chip; + } else { + chip = 0; + chipsel = -1; + } + + td->version[chip]++; + if (md) + md->version[chip]++; + + /* Write the bad block table to the device? */ + if (td->options & NAND_BBT_WRITE) { + res = write_bbt(mtd, buf, td, md, chipsel); + if (res < 0) + goto out; + } + /* Write the mirror bad block table to the device? */ + if (md && (md->options & NAND_BBT_WRITE)) { + res = write_bbt(mtd, buf, md, td, chipsel); + } + + out: + kfree(buf); + return res; +} + +/* + * Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +/* Generic flash bbt descriptors */ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +static struct nand_bbt_descr bbt_main_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_no_oob_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP + | NAND_BBT_NO_OOB, + .len = 4, + .veroffs = 4, + .maxblocks = NAND_BBT_SCAN_MAXBLOCKS, + .pattern = mirror_pattern +}; + +#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB) +/** + * nand_create_badblock_pattern - [INTERN] Creates a BBT descriptor structure + * @this: NAND chip to create descriptor for + * + * This function allocates and initializes a nand_bbt_descr for BBM detection + * based on the properties of @this. The new descriptor is stored in + * this->badblock_pattern. Thus, this->badblock_pattern should be NULL when + * passed to this function. + */ +static int nand_create_badblock_pattern(struct nand_chip *this) +{ + struct nand_bbt_descr *bd; + if (this->badblock_pattern) { + pr_warn("Bad block pattern already allocated; not replacing\n"); + return -EINVAL; + } + bd = kzalloc(sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + bd->options = this->bbt_options & BADBLOCK_SCAN_MASK; + bd->offs = this->badblockpos; + bd->len = (this->options & NAND_BUSWIDTH_16) ? 2 : 1; + bd->pattern = scan_ff_pattern; + bd->options |= NAND_BBT_DYNAMICSTRUCT; + this->badblock_pattern = bd; + return 0; +} + +/** + * nand_default_bbt - [NAND Interface] Select a default bad block table for the device + * @mtd: MTD device structure + * + * This function selects the default bad block table support for the device and + * calls the nand_scan_bbt function. + */ +int nand_default_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int ret; + + /* Is a flash based bad block table requested? */ + if (this->bbt_options & NAND_BBT_USE_FLASH) { + /* Use the default pattern descriptors */ + if (!this->bbt_td) { + if (this->bbt_options & NAND_BBT_NO_OOB) { + this->bbt_td = &bbt_main_no_oob_descr; + this->bbt_md = &bbt_mirror_no_oob_descr; + } else { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + } + } else { + this->bbt_td = NULL; + this->bbt_md = NULL; + } + + if (!this->badblock_pattern) { + ret = nand_create_badblock_pattern(this); + if (ret) + return ret; + } + + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +/** + * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved + * @mtd: MTD device structure + * @offs: offset in the device + */ +int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block; + + block = (int)(offs >> this->bbt_erase_shift); + return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED; +} + +/** + * nand_isbad_bbt - [NAND Interface] Check if a block is bad + * @mtd: MTD device structure + * @offs: offset in the device + * @allowbbt: allow access to bad block table region + */ +int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block, res; + + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); + + pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", + (unsigned int)offs, block, res); + + switch (res) { + case BBT_BLOCK_GOOD: + return 0; + case BBT_BLOCK_WORN: + return 1; + case BBT_BLOCK_RESERVED: + return allowbbt ? 0 : 1; + } + return 1; +} + +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c new file mode 100644 index 0000000000..afa0418168 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_bch.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic + * + */ + +#include +/*#include */ +#include + +#include +#include +#include +#include +#include +#include + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @ecclayout: private ecc layout for this BCH configuration + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + struct nand_ecclayout ecclayout; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @mtd: MTD block structure + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) +{ + const struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + encode_bch(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + const struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + pr_debug("%s: corrected bitflip %u\n", + __func__, errloc[i]); + } + } else if (count < 0) { + printk(KERN_ERR "ecc unrecoverable error\n"); + count = -EBADMSG; + } + return count; +} + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + unsigned int m, t, eccsteps, i; + struct nand_ecclayout *layout = nand->ecc.layout; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccstrength = nand->ecc.strength; + + if (!eccbytes && eccstrength) { + eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); + nand->ecc.bytes = eccbytes; + } + + if (!eccsize || !eccbytes) { + printk(KERN_WARNING "ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = init_bch(m, t, 0); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* if no ecc placement scheme was provided, build one */ + if (!layout) { + + /* handle large page devices only */ + if (mtd->oobsize < 64) { + printk(KERN_WARNING "must provide an oob scheme for " + "oobsize %d\n", mtd->oobsize); + goto fail; + } + + layout = &nbc->ecclayout; + layout->eccbytes = eccsteps*eccbytes; + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + printk(KERN_WARNING "no suitable oob scheme available " + "for oobsize %d eccbytes %u\n", mtd->oobsize, + eccbytes); + goto fail; + } + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + + nand->ecc.layout = layout; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + printk(KERN_WARNING "eccsize %u is too large\n", eccsize); + goto fail; + } + if (layout->eccbytes != (eccsteps*eccbytes)) { + printk(KERN_WARNING "invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + memset(nbc->eccmask, 0, eccbytes); + encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + if (!eccstrength) + nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + free_bch(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c new file mode 100644 index 0000000000..2bc329be1a --- /dev/null +++ b/drivers/mtd/nand/raw/nand_ecc.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/raw/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * + * Copyright (C) 2006 Thomas Gleixner + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include + +#include +#include +#include + +/* + * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), + * only nand_correct_data() is needed + */ + +#if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC) +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const u_char nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * @mtd: MTD block structure + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; + int i; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + + /* Build up column parity */ + for(i = 0; i < 256; i++) { + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[*dat++]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) i; + reg2 ^= ~((uint8_t) i); + } + } + + /* Create non-inverted ECC code from line parity */ + tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ + tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ + tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ + tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ + tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ + tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ + tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ + tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ + + tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ + tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ + tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ + tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ + tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ + tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ + tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ + tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + + /* Calculate final ECC code */ + ecc_code[0] = ~tmp1; + ecc_code[1] = ~tmp2; + ecc_code[2] = ((~reg1) << 2) | 0x03; + + return 0; +} +#endif /* CONFIG_NAND_SPL */ + +static inline int countbits(uint32_t byte) +{ + int res = 0; + + for (;byte; byte >>= 1) + res += byte & 0x01; + return res; +} + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @mtd: MTD block structure + * @dat: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256 byte block + */ +int nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + uint8_t s0, s1, s2; + + s1 = calc_ecc[0] ^ read_ecc[0]; + s0 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; + if ((s0 | s1 | s2) == 0) + return 0; + + /* Check for a single bit error */ + if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && + ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && + ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + + uint32_t byteoffs, bitnum; + + byteoffs = (s1 << 0) & 0x80; + byteoffs |= (s1 << 1) & 0x40; + byteoffs |= (s1 << 2) & 0x20; + byteoffs |= (s1 << 3) & 0x10; + + byteoffs |= (s0 >> 4) & 0x08; + byteoffs |= (s0 >> 3) & 0x04; + byteoffs |= (s0 >> 2) & 0x02; + byteoffs |= (s0 >> 1) & 0x01; + + bitnum = (s2 >> 5) & 0x04; + bitnum |= (s2 >> 4) & 0x02; + bitnum |= (s2 >> 3) & 0x01; + + dat[byteoffs] ^= (1 << bitnum); + + return 1; + } + + if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) + return 1; + + return -EBADMSG; +} diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c new file mode 100644 index 0000000000..4009d64123 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * This program 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 +#include +#include + +#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS +#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) + +#define SP_OPTIONS NAND_NEED_READRDY +#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16) + +/* + * The chip ID list: + * name, device ID, page size, chip size in MiB, eraseblock size, options + * + * If page size and eraseblock size are 0, the sizes are taken from the + * extended chip ID. + */ +struct nand_flash_dev nand_flash_ids[] = { +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS + LEGACY_ID_NAND("NAND 1MiB 5V 8-bit", 0x6e, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 2MiB 5V 8-bit", 0x64, 2, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xe8, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit", 0xec, 1, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit", 0xea, 2, SZ_4K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xd5, 4, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xe6, 8, SZ_8K, SP_OPTIONS), +#endif + /* + * Some incompatible NAND chips share device ID's and so must be + * listed by full ID. We list them first so that we can easily identify + * the most specific match. + */ + {"TC58NVG0S3E 1G 3.3V 8-bit", + { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, + SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), + 2 }, + {"TC58NVG2S0F 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG2S0H 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, + {"TC58NVG3S0F 8G 3.3V 8-bit", + { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, + {"TC58NVG5D2 32G 3.3V 8-bit", + { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"TC58NVG6D2 64G 3.3V 8-bit", + { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNRGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, + {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", + { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, + SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, + NAND_ECC_INFO(40, SZ_1K), 4 }, + {"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit", + { .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} }, + SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664, + NAND_ECC_INFO(56, SZ_1K), 1 }, + + LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS), + + LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16), + LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16), + + LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS), + + /* + * These are the new chips with large page size. Their page size and + * eraseblock size are determined from the extended ID bytes. + */ + + /* 512 Megabit */ + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16), + + /* 1 Gigabit */ + EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16), + + /* 2 Gigabit */ + EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16), + + /* 4 Gigabit */ + EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16), + + /* 8 Gigabit */ + EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16), + + /* 16 Gigabit */ + EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16), + + /* 32 Gigabit */ + EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16), + + /* 64 Gigabit */ + EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16), + + /* 128 Gigabit */ + EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16), + + /* 256 Gigabit */ + EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16), + + /* 512 Gigabit */ + EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS), + EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16), + EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16), + + {NULL} +}; + +/* Manufacturer IDs */ +struct nand_manufacturers nand_manuf_ids[] = { + {NAND_MFR_TOSHIBA, "Toshiba"}, + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {NAND_MFR_RENESAS, "Renesas"}, + {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_MICRON, "Micron"}, + {NAND_MFR_AMD, "AMD/Spansion"}, + {NAND_MFR_MACRONIX, "Macronix"}, + {NAND_MFR_EON, "Eon"}, + {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_INTEL, "Intel"}, + {NAND_MFR_ATO, "ATO"}, + {0x0, "Unknown"} +}; + +EXPORT_SYMBOL(nand_manuf_ids); +EXPORT_SYMBOL(nand_flash_ids); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner "); +MODULE_DESCRIPTION("Nand device & manufacturer IDs"); diff --git a/drivers/mtd/nand/raw/nand_plat.c b/drivers/mtd/nand/raw/nand_plat.c new file mode 100644 index 0000000000..335c3e3471 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_plat.c @@ -0,0 +1,64 @@ +/* + * Genericish driver for memory mapped NAND devices + * + * Copyright (c) 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +/* Your board must implement the following macros: + * NAND_PLAT_WRITE_CMD(chip, cmd) + * NAND_PLAT_WRITE_ADR(chip, cmd) + * NAND_PLAT_INIT() + * + * It may also implement the following: + * NAND_PLAT_DEV_READY(chip) + */ + +#include +#include +#ifdef NAND_PLAT_GPIO_DEV_READY +# include +# define NAND_PLAT_DEV_READY(chip) gpio_get_value(NAND_PLAT_GPIO_DEV_READY) +#endif + +#include + +static void plat_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd_to_nand(mtd); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + NAND_PLAT_WRITE_CMD(this, cmd); + else + NAND_PLAT_WRITE_ADR(this, cmd); +} + +#ifdef NAND_PLAT_DEV_READY +static int plat_dev_ready(struct mtd_info *mtd) +{ + return NAND_PLAT_DEV_READY((struct nand_chip *)mtd_to_nand(mtd)); +} +#else +# define plat_dev_ready NULL +#endif + +int board_nand_init(struct nand_chip *nand) +{ +#ifdef NAND_PLAT_GPIO_DEV_READY + gpio_request(NAND_PLAT_GPIO_DEV_READY, "nand-plat"); + gpio_direction_input(NAND_PLAT_GPIO_DEV_READY); +#endif + +#ifdef NAND_PLAT_INIT + NAND_PLAT_INIT(); +#endif + + nand->cmd_ctrl = plat_cmd_ctrl; + nand->dev_ready = plat_dev_ready; + nand->ecc.mode = NAND_ECC_SOFT; + + return 0; +} diff --git a/drivers/mtd/nand/raw/nand_spl_load.c b/drivers/mtd/nand/raw/nand_spl_load.c new file mode 100644 index 0000000000..ecd373e054 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_spl_load.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + */ + +#include +#include + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* + * Load U-Boot image from NAND into RAM + */ + nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +} diff --git a/drivers/mtd/nand/raw/nand_spl_loaders.c b/drivers/mtd/nand/raw/nand_spl_loaders.c new file mode 100644 index 0000000000..177c12b581 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_spl_loaders.c @@ -0,0 +1,104 @@ +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page, page_offset; + + /* offs has to be aligned to a page address! */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + page_offset = offs % CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* Skip bad blocks */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + /* + * When offs is not aligned to page address the + * extra offset is copied to dst as well. Copy + * the image such that its first byte will be + * at the dst. + */ + if (unlikely(page_offset)) { + memmove(dst, dst + page_offset, + CONFIG_SYS_NAND_PAGE_SIZE); + dst = (void *)((int)dst - page_offset); + page_offset = 0; + } + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +#ifdef CONFIG_SPL_UBI +/* + * Temporary storage for non NAND page aligned and non NAND page sized + * reads. Note: This does not support runtime detected FLASH yet, but + * that should be reasonably easy to fix by making the buffer large + * enough :) + */ +static u8 scratch_buf[CONFIG_SYS_NAND_PAGE_SIZE]; + +/** + * nand_spl_read_block - Read data from physical eraseblock into a buffer + * @block: Number of the physical eraseblock + * @offset: Data offset from the start of @peb + * @len: Data size to read + * @dst: Address of the destination buffer + * + * This could be further optimized if we'd have a subpage read + * function in the simple code. On NAND which allows subpage reads + * this would spare quite some time to readout e.g. the VID header of + * UBI. + * + * Notes: + * @offset + @len are not allowed to be larger than a physical + * erase block. No sanity check done for simplicity reasons. + * + * To support runtime detected flash this needs to be extended by + * information about the actual flash geometry, but thats beyond the + * scope of this effort and for most applications where fast boot is + * required it is not an issue anyway. + */ +int nand_spl_read_block(int block, int offset, int len, void *dst) +{ + int page, read; + + /* Calculate the page number */ + page = offset / CONFIG_SYS_NAND_PAGE_SIZE; + + /* Offset to the start of a flash page */ + offset = offset % CONFIG_SYS_NAND_PAGE_SIZE; + + while (len) { + /* + * Non page aligned reads go to the scratch buffer. + * Page aligned reads go directly to the destination. + */ + if (offset || len < CONFIG_SYS_NAND_PAGE_SIZE) { + nand_read_page(block, page, scratch_buf); + read = min(len, CONFIG_SYS_NAND_PAGE_SIZE - offset); + memcpy(dst, scratch_buf + offset, read); + offset = 0; + } else { + nand_read_page(block, page, dst); + read = CONFIG_SYS_NAND_PAGE_SIZE; + } + page++; + len -= read; + dst += read; + } + return 0; +} +#endif diff --git a/drivers/mtd/nand/raw/nand_spl_simple.c b/drivers/mtd/nand/raw/nand_spl_simple.c new file mode 100644 index 0000000000..09e053541a --- /dev/null +++ b/drivers/mtd/nand/raw/nand_spl_simple.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + */ + +#include +#include +#include +#include + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static struct mtd_info *mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +#if (CONFIG_SYS_NAND_PAGE_SIZE <= 512) +/* + * NAND command for small page NAND devices (512) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + + while (!this->dev_ready(mtd)) + ; + + /* Begin command latch cycle */ + this->cmd_ctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + this->cmd_ctrl(mtd, offs, NAND_CTRL_ALE | NAND_CTRL_CHANGE); + this->cmd_ctrl(mtd, page_addr & 0xff, NAND_CTRL_ALE); /* A[16:9] */ + this->cmd_ctrl(mtd, (page_addr >> 8) & 0xff, + NAND_CTRL_ALE); /* A[24:17] */ +#ifdef CONFIG_SYS_NAND_4_ADDR_CYCLE + /* One more address cycle for devices > 32MiB */ + this->cmd_ctrl(mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[28:25] */ +#endif + /* Latch in address */ + this->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(mtd)) + ; + + return 0; +} +#else +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd_to_nand(mtd); + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if ((this->options & NAND_BUSWIDTH_16) && !nand_opcode_8bits(cmd)) + offs >>= 1; + + /* Begin command latch cycle */ + hwctrl(mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + /* Latch in address */ + hwctrl(mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(mtd)) + ; + + return 0; +} +#endif + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char bb_data[2]; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + this->read_buf(mtd, bb_data, 2); + if (bb_data[0] != 0xff || bb_data[1] != 0xff) + return 1; + } else { + this->read_buf(mtd, bb_data, 1); + if (bb_data[0] != 0xff) + return 1; + } + + return 0; +} + +#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST) +static int nand_read_page(int block, int page, uchar *dst) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READOOB); + this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + nand_command(block, page, 0, NAND_CMD_READ0); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(mtd, NAND_ECC_READ); + this->read_buf(mtd, p, eccsize); + this->ecc.calculate(mtd, p, &ecc_calc[i]); + this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd_to_nand(mtd); + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (this->ecc.mode != NAND_ECC_SOFT) + this->ecc.hwctl(mtd, NAND_ECC_READ); + this->read_buf(mtd, p, eccsize); + this->ecc.calculate(mtd, p, &ecc_calc[i]); + } + this->read_buf(mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#endif + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd = nand_to_mtd(&nand_chip); + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_SOFTECC + if (nand_chip.ecc.mode == NAND_ECC_SOFT) { + nand_chip.ecc.calculate = nand_calculate_ecc; + nand_chip.ecc.correct = nand_correct_data; + } +#endif + + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, 0); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(mtd, -1); +} + +#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/raw/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c new file mode 100644 index 0000000000..c0545a4fb1 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This program 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 +#include +#include + +static const struct nand_data_interface onfi_sdr_timings[] = { + /* Mode 0 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 20000, + .tALS_min = 50000, + .tAR_min = 25000, + .tCEA_max = 100000, + .tCEH_min = 20000, + .tCH_min = 20000, + .tCHZ_max = 100000, + .tCLH_min = 20000, + .tCLR_min = 20000, + .tCLS_min = 50000, + .tCOH_min = 0, + .tCS_min = 70000, + .tDH_min = 20000, + .tDS_min = 40000, + .tFEAT_max = 1000000, + .tIR_min = 10000, + .tITC_max = 1000000, + .tRC_min = 100000, + .tREA_max = 40000, + .tREH_min = 30000, + .tRHOH_min = 0, + .tRHW_min = 200000, + .tRHZ_max = 200000, + .tRLOH_min = 0, + .tRP_min = 50000, + .tRR_min = 40000, + .tRST_max = 250000000000ULL, + .tWB_max = 200000, + .tWC_min = 100000, + .tWH_min = 30000, + .tWHR_min = 120000, + .tWP_min = 50000, + .tWW_min = 100000, + }, + }, + /* Mode 1 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 25000, + .tAR_min = 10000, + .tCEA_max = 45000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 25000, + .tCOH_min = 15000, + .tCS_min = 35000, + .tDH_min = 10000, + .tDS_min = 20000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 50000, + .tREA_max = 30000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 25000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 45000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 25000, + .tWW_min = 100000, + }, + }, + /* Mode 2 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 15000, + .tAR_min = 10000, + .tCEA_max = 30000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 15000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 15000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 35000, + .tREA_max = 25000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tRP_min = 17000, + .tWC_min = 35000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 17000, + .tWW_min = 100000, + }, + }, + /* Mode 3 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 50000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 30000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 15000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 30000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 15000, + .tWW_min = 100000, + }, + }, + /* Mode 4 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 20000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 25000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 12000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 25000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 12000, + .tWW_min = 100000, + }, + }, + /* Mode 5 */ + { + .type = NAND_SDR_IFACE, + .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 15000, + .tDH_min = 5000, + .tDS_min = 7000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 20000, + .tREA_max = 16000, + .tREH_min = 7000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 10000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 20000, + .tWH_min = 7000, + .tWHR_min = 80000, + .tWP_min = 10000, + .tWW_min = 100000, + }, + }, +}; + +/** + * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND + * timings according to the given ONFI timing mode + * @mode: ONFI timing mode + */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) +{ + if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) + return ERR_PTR(-EINVAL); + + return &onfi_sdr_timings[mode].timings.sdr; +} +EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); + +/** + * onfi_init_data_interface - [NAND Interface] Initialize a data interface from + * given ONFI mode + * @iface: The data interface to be initialized + * @mode: The ONFI timing mode + */ +int onfi_init_data_interface(struct nand_chip *chip, + struct nand_data_interface *iface, + enum nand_data_interface_type type, + int timing_mode) +{ + if (type != NAND_SDR_IFACE) + return -EINVAL; + + if (timing_mode < 0 || timing_mode >= ARRAY_SIZE(onfi_sdr_timings)) + return -EINVAL; + + *iface = onfi_sdr_timings[timing_mode]; + + /* + * Initialize timings that cannot be deduced from timing mode: + * tR, tPROG, tCCS, ... + * These information are part of the ONFI parameter page. + */ + if (chip->onfi_version) { + struct nand_onfi_params *params = &chip->onfi_params; + struct nand_sdr_timings *timings = &iface->timings.sdr; + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); + timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); + timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + } + + return 0; +} +EXPORT_SYMBOL(onfi_init_data_interface); + +/** + * nand_get_default_data_interface - [NAND Interface] Retrieve NAND + * data interface for mode 0. This is used as default timing after + * reset. + */ +const struct nand_data_interface *nand_get_default_data_interface(void) +{ + return &onfi_sdr_timings[0]; +} +EXPORT_SYMBOL(nand_get_default_data_interface); diff --git a/drivers/mtd/nand/raw/nand_util.c b/drivers/mtd/nand/raw/nand_util.c new file mode 100644 index 0000000000..fc2235c1a0 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_util.c @@ -0,0 +1,904 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/mtd/nand/raw/nand_util.c + * + * Copyright (C) 2006 by Weiss-Electronic GmbH. + * All rights reserved. + * + * @author: Guido Classen + * @descr: NAND Flash support + * @references: borrowed heavily from Linux mtd-utils code: + * flash_eraseall.c by Arcom Control System Ltd + * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) + * and Thomas Gleixner (tglx@linutronix.de) + * + * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by + * Artem Bityutskiy from mtd-utils + * + * Copyright 2010 Freescale Semiconductor + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +typedef struct erase_info erase_info_t; +typedef struct mtd_info mtd_info_t; + +/* support only for native endian JFFS2 */ +#define cpu_to_je16(x) (x) +#define cpu_to_je32(x) (x) + +/** + * nand_erase_opts: - erase NAND flash with support for various options + * (jffs2 formatting) + * + * @param mtd nand mtd instance to erase + * @param opts options, @see struct nand_erase_options + * @return 0 in case of success + * + * This code is ported from flash_eraseall.c from Linux mtd utils by + * Arcom Control System Ltd. + */ +int nand_erase_opts(struct mtd_info *mtd, + const nand_erase_options_t *opts) +{ + struct jffs2_unknown_node cleanmarker; + erase_info_t erase; + unsigned long erase_length, erased_length; /* in blocks */ + int result; + int percent_complete = -1; + const char *mtd_device = mtd->name; + struct mtd_oob_ops oob_opts; + struct nand_chip *chip = mtd_to_nand(mtd); + + if ((opts->offset & (mtd->erasesize - 1)) != 0) { + printf("Attempt to erase non block-aligned data\n"); + return -1; + } + + memset(&erase, 0, sizeof(erase)); + memset(&oob_opts, 0, sizeof(oob_opts)); + + erase.mtd = mtd; + erase.len = mtd->erasesize; + erase.addr = opts->offset; + erase_length = lldiv(opts->length + mtd->erasesize - 1, + mtd->erasesize); + + cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); + cleanmarker.totlen = cpu_to_je32(8); + + /* scrub option allows to erase badblock. To prevent internal + * check from erase() method, set block check method to dummy + * and disable bad block table while erasing. + */ + if (opts->scrub) { + erase.scrub = opts->scrub; + /* + * We don't need the bad block table anymore... + * after scrub, there are no bad blocks left! + */ + if (chip->bbt) { + kfree(chip->bbt); + } + chip->bbt = NULL; + chip->options &= ~NAND_BBT_SCANNED; + } + + for (erased_length = 0; + erased_length < erase_length; + erase.addr += mtd->erasesize) { + + WATCHDOG_RESET(); + + if (opts->lim && (erase.addr >= (opts->offset + opts->lim))) { + puts("Size of erase exceeds limit\n"); + return -EFBIG; + } + if (!opts->scrub) { + int ret = mtd_block_isbad(mtd, erase.addr); + if (ret > 0) { + if (!opts->quiet) + printf("\rSkipping bad block at " + "0x%08llx " + " \n", + erase.addr); + + if (!opts->spread) + erased_length++; + + continue; + + } else if (ret < 0) { + printf("\n%s: MTD get bad block failed: %d\n", + mtd_device, + ret); + return -1; + } + } + + erased_length++; + + result = mtd_erase(mtd, &erase); + if (result != 0) { + printf("\n%s: MTD Erase failure: %d\n", + mtd_device, result); + continue; + } + + /* format for JFFS2 ? */ + if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) { + struct mtd_oob_ops ops; + ops.ooblen = 8; + ops.datbuf = NULL; + ops.oobbuf = (uint8_t *)&cleanmarker; + ops.ooboffs = 0; + ops.mode = MTD_OPS_AUTO_OOB; + + result = mtd_write_oob(mtd, erase.addr, &ops); + if (result != 0) { + printf("\n%s: MTD writeoob failure: %d\n", + mtd_device, result); + continue; + } + } + + if (!opts->quiet) { + unsigned long long n = erased_length * 100ULL; + int percent; + + do_div(n, erase_length); + percent = (int)n; + + /* output progress message only at whole percent + * steps to reduce the number of messages printed + * on (slow) serial consoles + */ + if (percent != percent_complete) { + percent_complete = percent; + + printf("\rErasing at 0x%llx -- %3d%% complete.", + erase.addr, percent); + + if (opts->jffs2 && result == 0) + printf(" Cleanmarker written at 0x%llx.", + erase.addr); + } + } + } + if (!opts->quiet) + printf("\n"); + + return 0; +} + +#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK + +#define NAND_CMD_LOCK_TIGHT 0x2c +#define NAND_CMD_LOCK_STATUS 0x7a + +/****************************************************************************** + * Support for locking / unlocking operations of some NAND devices + *****************************************************************************/ + +/** + * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT + * state + * + * @param mtd nand mtd instance + * @param tight bring device in lock tight mode + * + * @return 0 on success, -1 in case of error + * + * The lock / lock-tight command only applies to the whole chip. To get some + * parts of the chip lock and others unlocked use the following sequence: + * + * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) + * - Call nand_unlock() once for each consecutive area to be unlocked + * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) + * + * If the device is in lock-tight state software can't change the + * current active lock/unlock state of all pages. nand_lock() / nand_unlock() + * calls will fail. It is only posible to leave lock-tight state by + * an hardware signal (low pulse on _WP pin) or by power down. + */ +int nand_lock(struct mtd_info *mtd, int tight) +{ + int ret = 0; + int status; + struct nand_chip *chip = mtd_to_nand(mtd); + + /* select the NAND device */ + chip->select_chip(mtd, 0); + + /* check the Lock Tight Status */ + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0); + if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { + printf("nand_lock: Device is locked tight!\n"); + ret = -1; + goto out; + } + + chip->cmdfunc(mtd, + (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), + -1, -1); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + + /* see if device thinks it succeeded */ + if (status & 0x01) { + ret = -1; + } + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_get_lock_status: - query current lock state from one page of NAND + * flash + * + * @param mtd nand mtd instance + * @param offset page address to query (must be page-aligned!) + * + * @return -1 in case of error + * >0 lock status: + * bitfield with the following combinations: + * NAND_LOCK_STATUS_TIGHT: page in tight state + * NAND_LOCK_STATUS_UNLOCK: page unlocked + * + */ +int nand_get_lock_status(struct mtd_info *mtd, loff_t offset) +{ + int ret = 0; + int chipnr; + int page; + struct nand_chip *chip = mtd_to_nand(mtd); + + /* select the NAND device */ + chipnr = (int)(offset >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + + if ((offset & (mtd->writesize - 1)) != 0) { + printf("nand_get_lock_status: " + "Start address must be beginning of " + "nand page!\n"); + ret = -1; + goto out; + } + + /* check the Lock Status */ + page = (int)(offset >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); + + ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT + | NAND_LOCK_STATUS_UNLOCK); + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} + +/** + * nand_unlock: - Unlock area of NAND pages + * only one consecutive area can be unlocked at one time! + * + * @param mtd nand mtd instance + * @param start start byte address + * @param length number of bytes to unlock (must be a multiple of + * page size mtd->writesize) + * @param allexcept if set, unlock everything not selected + * + * @return 0 on success, -1 in case of error + */ +int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, + int allexcept) +{ + int ret = 0; + int chipnr; + int status; + int page; + struct nand_chip *chip = mtd_to_nand(mtd); + + debug("nand_unlock%s: start: %08llx, length: %zd!\n", + allexcept ? " (allexcept)" : "", start, length); + + /* select the NAND device */ + chipnr = (int)(start >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) { + printf("nand_unlock: Device is write protected!\n"); + ret = -1; + goto out; + } + + /* check the Lock Tight Status */ + page = (int)(start >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); + if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { + printf("nand_unlock: Device is locked tight!\n"); + ret = -1; + goto out; + } + + if ((start & (mtd->erasesize - 1)) != 0) { + printf("nand_unlock: Start address must be beginning of " + "nand block!\n"); + ret = -1; + goto out; + } + + if (length == 0 || (length & (mtd->erasesize - 1)) != 0) { + printf("nand_unlock: Length must be a multiple of nand block " + "size %08x!\n", mtd->erasesize); + ret = -1; + goto out; + } + + /* + * Set length so that the last address is set to the + * starting address of the last block + */ + length -= mtd->erasesize; + + /* submit address of first page to unlock */ + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* submit ADDRESS of LAST page to unlock */ + page += (int)(length >> chip->page_shift); + + /* + * Page addresses for unlocking are supposed to be block-aligned. + * At least some NAND chips use the low bit to indicate that the + * page range should be inverted. + */ + if (allexcept) + page |= 1; + + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask); + + /* call wait ready function */ + status = chip->waitfunc(mtd, chip); + /* see if device thinks it succeeded */ + if (status & 0x01) { + /* there was an error */ + ret = -1; + goto out; + } + + out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + return ret; +} +#endif + +/** + * check_skip_len + * + * Check if there are any bad blocks, and whether length including bad + * blocks fits into device + * + * @param mtd nand mtd instance + * @param offset offset in flash + * @param length image length + * @param used length of flash needed for the requested length + * @return 0 if the image fits and there are no bad blocks + * 1 if the image fits, but there are bad blocks + * -1 if the image does not fit + */ +static int check_skip_len(struct mtd_info *mtd, loff_t offset, size_t length, + size_t *used) +{ + size_t len_excl_bad = 0; + int ret = 0; + + while (len_excl_bad < length) { + size_t block_len, block_off; + loff_t block_start; + + if (offset >= mtd->size) + return -1; + + block_start = offset & ~(loff_t)(mtd->erasesize - 1); + block_off = offset & (mtd->erasesize - 1); + block_len = mtd->erasesize - block_off; + + if (!nand_block_isbad(mtd, block_start)) + len_excl_bad += block_len; + else + ret = 1; + + offset += block_len; + *used += block_len; + } + + /* If the length is not a multiple of block_len, adjust. */ + if (len_excl_bad > length) + *used -= (len_excl_bad - length); + + return ret; +} + +#ifdef CONFIG_CMD_NAND_TRIMFFS +static size_t drop_ffs(const struct mtd_info *mtd, const u_char *buf, + const size_t *len) +{ + size_t l = *len; + ssize_t i; + + for (i = l - 1; i >= 0; i--) + if (buf[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + l = i + 1; + l = (l + mtd->writesize - 1) / mtd->writesize; + l *= mtd->writesize; + + /* + * since the input length may be unaligned, prevent access past the end + * of the buffer + */ + return min(l, *len); +} +#endif + +/** + * nand_verify_page_oob: + * + * Verify a page of NAND flash, including the OOB. + * Reads page of NAND and verifies the contents and OOB against the + * values in ops. + * + * @param mtd nand mtd instance + * @param ops MTD operations, including data to verify + * @param ofs offset in flash + * @return 0 in case of success + */ +int nand_verify_page_oob(struct mtd_info *mtd, struct mtd_oob_ops *ops, + loff_t ofs) +{ + int rval; + struct mtd_oob_ops vops; + size_t verlen = mtd->writesize + mtd->oobsize; + + memcpy(&vops, ops, sizeof(vops)); + + vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen); + + if (!vops.datbuf) + return -ENOMEM; + + vops.oobbuf = vops.datbuf + mtd->writesize; + + rval = mtd_read_oob(mtd, ofs, &vops); + if (!rval) + rval = memcmp(ops->datbuf, vops.datbuf, vops.len); + if (!rval) + rval = memcmp(ops->oobbuf, vops.oobbuf, vops.ooblen); + + free(vops.datbuf); + + return rval ? -EIO : 0; +} + +/** + * nand_verify: + * + * Verify a region of NAND flash. + * Reads NAND in page-sized chunks and verifies the contents against + * the contents of a buffer. The offset into the NAND must be + * page-aligned, and the function doesn't handle skipping bad blocks. + * + * @param mtd nand mtd instance + * @param ofs offset in flash + * @param len buffer length + * @param buf buffer to read from + * @return 0 in case of success + */ +int nand_verify(struct mtd_info *mtd, loff_t ofs, size_t len, u_char *buf) +{ + int rval = 0; + size_t verofs; + size_t verlen = mtd->writesize; + uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen); + + if (!verbuf) + return -ENOMEM; + + /* Read the NAND back in page-size groups to limit malloc size */ + for (verofs = ofs; verofs < ofs + len; + verofs += verlen, buf += verlen) { + verlen = min(mtd->writesize, (uint32_t)(ofs + len - verofs)); + rval = nand_read(mtd, verofs, &verlen, verbuf); + if (!rval || (rval == -EUCLEAN)) + rval = memcmp(buf, verbuf, verlen); + + if (rval) + break; + } + + free(verbuf); + + return rval ? -EIO : 0; +} + + + +/** + * nand_write_skip_bad: + * + * Write image to NAND flash. + * Blocks that are marked bad are skipped and the is written to the next + * block instead as long as the image is short enough to fit even after + * skipping the bad blocks. Due to bad blocks we may not be able to + * perform the requested write. In the case where the write would + * extend beyond the end of the NAND device, both length and actual (if + * not NULL) are set to 0. In the case where the write would extend + * beyond the limit we are passed, length is set to 0 and actual is set + * to the required length. + * + * @param mtd nand mtd instance + * @param offset offset in flash + * @param length buffer length + * @param actual set to size required to write length worth of + * buffer or 0 on error, if not NULL + * @param lim maximum size that actual may be in order to not + * exceed the buffer + * @param buffer buffer to read from + * @param flags flags modifying the behaviour of the write to NAND + * @return 0 in case of success + */ +int nand_write_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, + size_t *actual, loff_t lim, u_char *buffer, int flags) +{ + int rval = 0, blocksize; + size_t left_to_write = *length; + size_t used_for_write = 0; + u_char *p_buffer = buffer; + int need_skip; + + if (actual) + *actual = 0; + + blocksize = mtd->erasesize; + + /* + * nand_write() handles unaligned, partial page writes. + * + * We allow length to be unaligned, for convenience in + * using the $filesize variable. + * + * However, starting at an unaligned offset makes the + * semantics of bad block skipping ambiguous (really, + * you should only start a block skipping access at a + * partition boundary). So don't try to handle that. + */ + if ((offset & (mtd->writesize - 1)) != 0) { + printf("Attempt to write non page-aligned data\n"); + *length = 0; + return -EINVAL; + } + + need_skip = check_skip_len(mtd, offset, *length, &used_for_write); + + if (actual) + *actual = used_for_write; + + if (need_skip < 0) { + printf("Attempt to write outside the flash area\n"); + *length = 0; + return -EINVAL; + } + + if (used_for_write > lim) { + puts("Size of write exceeds partition or device limit\n"); + *length = 0; + return -EFBIG; + } + + if (!need_skip && !(flags & WITH_DROP_FFS)) { + rval = nand_write(mtd, offset, length, buffer); + + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify(mtd, offset, *length, buffer); + + if (rval == 0) + return 0; + + *length = 0; + printf("NAND write to offset %llx failed %d\n", + offset, rval); + return rval; + } + + while (left_to_write > 0) { + size_t block_offset = offset & (mtd->erasesize - 1); + size_t write_size, truncated_write_size; + + WATCHDOG_RESET(); + + if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) { + printf("Skip bad block 0x%08llx\n", + offset & ~(mtd->erasesize - 1)); + offset += mtd->erasesize - block_offset; + continue; + } + + if (left_to_write < (blocksize - block_offset)) + write_size = left_to_write; + else + write_size = blocksize - block_offset; + + truncated_write_size = write_size; +#ifdef CONFIG_CMD_NAND_TRIMFFS + if (flags & WITH_DROP_FFS) + truncated_write_size = drop_ffs(mtd, p_buffer, + &write_size); +#endif + + rval = nand_write(mtd, offset, &truncated_write_size, + p_buffer); + + if ((flags & WITH_WR_VERIFY) && !rval) + rval = nand_verify(mtd, offset, + truncated_write_size, p_buffer); + + offset += write_size; + p_buffer += write_size; + + if (rval != 0) { + printf("NAND write to offset %llx failed %d\n", + offset, rval); + *length -= left_to_write; + return rval; + } + + left_to_write -= write_size; + } + + return 0; +} + +/** + * nand_read_skip_bad: + * + * Read image from NAND flash. + * Blocks that are marked bad are skipped and the next block is read + * instead as long as the image is short enough to fit even after + * skipping the bad blocks. Due to bad blocks we may not be able to + * perform the requested read. In the case where the read would extend + * beyond the end of the NAND device, both length and actual (if not + * NULL) are set to 0. In the case where the read would extend beyond + * the limit we are passed, length is set to 0 and actual is set to the + * required length. + * + * @param mtd nand mtd instance + * @param offset offset in flash + * @param length buffer length, on return holds number of read bytes + * @param actual set to size required to read length worth of buffer or 0 + * on error, if not NULL + * @param lim maximum size that actual may be in order to not exceed the + * buffer + * @param buffer buffer to write to + * @return 0 in case of success + */ +int nand_read_skip_bad(struct mtd_info *mtd, loff_t offset, size_t *length, + size_t *actual, loff_t lim, u_char *buffer) +{ + int rval; + size_t left_to_read = *length; + size_t used_for_read = 0; + u_char *p_buffer = buffer; + int need_skip; + + if ((offset & (mtd->writesize - 1)) != 0) { + printf("Attempt to read non page-aligned data\n"); + *length = 0; + if (actual) + *actual = 0; + return -EINVAL; + } + + need_skip = check_skip_len(mtd, offset, *length, &used_for_read); + + if (actual) + *actual = used_for_read; + + if (need_skip < 0) { + printf("Attempt to read outside the flash area\n"); + *length = 0; + return -EINVAL; + } + + if (used_for_read > lim) { + puts("Size of read exceeds partition or device limit\n"); + *length = 0; + return -EFBIG; + } + + if (!need_skip) { + rval = nand_read(mtd, offset, length, buffer); + if (!rval || rval == -EUCLEAN) + return 0; + + *length = 0; + printf("NAND read from offset %llx failed %d\n", + offset, rval); + return rval; + } + + while (left_to_read > 0) { + size_t block_offset = offset & (mtd->erasesize - 1); + size_t read_length; + + WATCHDOG_RESET(); + + if (nand_block_isbad(mtd, offset & ~(mtd->erasesize - 1))) { + printf("Skipping bad block 0x%08llx\n", + offset & ~(mtd->erasesize - 1)); + offset += mtd->erasesize - block_offset; + continue; + } + + if (left_to_read < (mtd->erasesize - block_offset)) + read_length = left_to_read; + else + read_length = mtd->erasesize - block_offset; + + rval = nand_read(mtd, offset, &read_length, p_buffer); + if (rval && rval != -EUCLEAN) { + printf("NAND read from offset %llx failed %d\n", + offset, rval); + *length -= left_to_read; + return rval; + } + + left_to_read -= read_length; + offset += read_length; + p_buffer += read_length; + } + + return 0; +} + +#ifdef CONFIG_CMD_NAND_TORTURE + +/** + * check_pattern: + * + * Check if buffer contains only a certain byte pattern. + * + * @param buf buffer to check + * @param patt the pattern to check + * @param size buffer size in bytes + * @return 1 if there are only patt bytes in buf + * 0 if something else was found + */ +static int check_pattern(const u_char *buf, u_char patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (buf[i] != patt) + return 0; + return 1; +} + +/** + * nand_torture: + * + * Torture a block of NAND flash. + * This is useful to determine if a block that caused a write error is still + * good or should be marked as bad. + * + * @param mtd nand mtd instance + * @param offset offset in flash + * @return 0 if the block is still good + */ +int nand_torture(struct mtd_info *mtd, loff_t offset) +{ + u_char patterns[] = {0xa5, 0x5a, 0x00}; + struct erase_info instr = { + .mtd = mtd, + .addr = offset, + .len = mtd->erasesize, + }; + size_t retlen; + int err, ret = -1, i, patt_count; + u_char *buf; + + if ((offset & (mtd->erasesize - 1)) != 0) { + puts("Attempt to torture a block at a non block-aligned offset\n"); + return -EINVAL; + } + + if (offset + mtd->erasesize > mtd->size) { + puts("Attempt to torture a block outside the flash area\n"); + return -EINVAL; + } + + patt_count = ARRAY_SIZE(patterns); + + buf = malloc_cache_aligned(mtd->erasesize); + if (buf == NULL) { + puts("Out of memory for erase block buffer\n"); + return -ENOMEM; + } + + for (i = 0; i < patt_count; i++) { + err = mtd_erase(mtd, &instr); + if (err) { + printf("%s: erase() failed for block at 0x%llx: %d\n", + mtd->name, instr.addr, err); + goto out; + } + + /* Make sure the block contains only 0xff bytes */ + err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf); + if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) { + printf("%s: read() failed for block at 0x%llx: %d\n", + mtd->name, instr.addr, err); + goto out; + } + + err = check_pattern(buf, 0xff, mtd->erasesize); + if (!err) { + printf("Erased block at 0x%llx, but a non-0xff byte was found\n", + offset); + ret = -EIO; + goto out; + } + + /* Write a pattern and check it */ + memset(buf, patterns[i], mtd->erasesize); + err = mtd_write(mtd, offset, mtd->erasesize, &retlen, buf); + if (err || retlen != mtd->erasesize) { + printf("%s: write() failed for block at 0x%llx: %d\n", + mtd->name, instr.addr, err); + goto out; + } + + err = mtd_read(mtd, offset, mtd->erasesize, &retlen, buf); + if ((err && err != -EUCLEAN) || retlen != mtd->erasesize) { + printf("%s: read() failed for block at 0x%llx: %d\n", + mtd->name, instr.addr, err); + goto out; + } + + err = check_pattern(buf, patterns[i], mtd->erasesize); + if (!err) { + printf("Pattern 0x%.2x checking failed for block at " + "0x%llx\n", patterns[i], offset); + ret = -EIO; + goto out; + } + } + + ret = 0; + +out: + free(buf); + return ret; +} + +#endif diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c new file mode 100644 index 0000000000..35c6dd1f1b --- /dev/null +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010-2011 Texas Instruments, + * Mansoor Ahamed + * + * BCH Error Location Module (ELM) support. + * + * NOTE: + * 1. Supports only continuous mode. Dont see need for page mode in uboot + * 2. Supports only syndrome polynomial 0. i.e. poly local variable is + * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial + * sets in uboot + */ + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "omap-elm" +#define ELM_DEFAULT_POLY (0) + +struct elm *elm_cfg; + +/** + * elm_load_syndromes - Load BCH syndromes based on bch_type selection + * @syndrome: BCH syndrome + * @bch_type: BCH4/BCH8/BCH16 + * @poly: Syndrome Polynomial set to use + */ +static void elm_load_syndromes(u8 *syndrome, enum bch_level bch_type, u8 poly) +{ + u32 *ptr; + u32 val; + + /* reg 0 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; + val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | + (syndrome[3] << 24); + writel(val, ptr); + /* reg 1 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; + val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | + (syndrome[7] << 24); + writel(val, ptr); + + if (bch_type == BCH_8_BIT || bch_type == BCH_16_BIT) { + /* reg 2 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; + val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | + (syndrome[11] << 24); + writel(val, ptr); + /* reg 3 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; + val = syndrome[12] | (syndrome[13] << 8) | + (syndrome[14] << 16) | (syndrome[15] << 24); + writel(val, ptr); + } + + if (bch_type == BCH_16_BIT) { + /* reg 4 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; + val = syndrome[16] | (syndrome[17] << 8) | + (syndrome[18] << 16) | (syndrome[19] << 24); + writel(val, ptr); + + /* reg 5 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; + val = syndrome[20] | (syndrome[21] << 8) | + (syndrome[22] << 16) | (syndrome[23] << 24); + writel(val, ptr); + + /* reg 6 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; + val = syndrome[24] | (syndrome[25] << 8) | + (syndrome[26] << 16) | (syndrome[27] << 24); + writel(val, ptr); + } +} + +/** + * elm_check_errors - Check for BCH errors and return error locations + * @syndrome: BCH syndrome + * @bch_type: BCH4/BCH8/BCH16 + * @error_count: Returns number of errrors in the syndrome + * @error_locations: Returns error locations (in decimal) in this array + * + * Check the provided syndrome for BCH errors and return error count + * and locations in the array passed. Returns -1 if error is not correctable, + * else returns 0 + */ +int elm_check_error(u8 *syndrome, enum bch_level bch_type, u32 *error_count, + u32 *error_locations) +{ + u8 poly = ELM_DEFAULT_POLY; + s8 i; + u32 location_status; + + elm_load_syndromes(syndrome, bch_type, poly); + + /* start processing */ + writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) + | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), + &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); + + /* wait for processing to complete */ + while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) + ; + /* clear status */ + writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), + &elm_cfg->irqstatus); + + /* check if correctable */ + location_status = readl(&elm_cfg->error_location[poly].location_status); + if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) { + printf("%s: uncorrectable ECC errors\n", DRIVER_NAME); + return -EBADMSG; + } + + /* get error count */ + *error_count = readl(&elm_cfg->error_location[poly].location_status) & + ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; + + for (i = 0; i < *error_count; i++) { + error_locations[i] = + readl(&elm_cfg->error_location[poly].error_location_x[i]); + } + + return 0; +} + + +/** + * elm_config - Configure ELM module + * @level: 4 / 8 / 16 bit BCH + * + * Configure ELM module based on BCH level. + * Set mode as continuous mode. + * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. + * Also, the mode is set only for syndrome 0 + */ +int elm_config(enum bch_level level) +{ + u32 val; + u8 poly = ELM_DEFAULT_POLY; + u32 buffer_size = 0x7FF; + + /* config size and level */ + val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; + val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & + ELM_LOCATION_CONFIG_ECC_SIZE_MASK); + writel(val, &elm_cfg->location_config); + + /* config continous mode */ + /* enable interrupt generation for syndrome polynomial set */ + writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), + &elm_cfg->irqenable); + /* set continuous mode for the syndrome polynomial set */ + writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), + &elm_cfg->page_ctrl); + + return 0; +} + +/** + * elm_reset - Do a soft reset of ELM + * + * Perform a soft reset of ELM and return after reset is done. + */ +void elm_reset(void) +{ + /* initiate reset */ + writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), + &elm_cfg->sysconfig); + + /* wait for reset complete and normal operation */ + while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != + ELM_SYSSTATUS_RESETDONE) + ; +} + +/** + * elm_init - Initialize ELM module + * + * Initialize ELM support. Currently it does only base address init + * and ELM reset. + */ +void elm_init(void) +{ + elm_cfg = (struct elm *)ELM_BASE; + elm_reset(); +} diff --git a/drivers/mtd/nand/raw/omap_gpmc.c b/drivers/mtd/nand/raw/omap_gpmc.c new file mode 100644 index 0000000000..6a050501b0 --- /dev/null +++ b/drivers/mtd/nand/raw/omap_gpmc.c @@ -0,0 +1,1037 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2004-2008 Texas Instruments, + * Rohit Choraria + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BADBLOCK_MARKER_LENGTH 2 +#define SECTOR_BYTES 512 +#define ECCCLEAR (0x1 << 8) +#define ECCRESULTREG1 (0x1 << 0) +/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ +#define BCH4_BIT_PAD 4 + +#ifdef CONFIG_BCH +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; +#endif +static uint8_t cs_next; +static __maybe_unused struct nand_ecclayout omap_ecclayout; + +#if defined(CONFIG_NAND_OMAP_GPMC_WSCFG) +static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE] = + { CONFIG_NAND_OMAP_GPMC_WSCFG }; +#else +/* wscfg is preset to zero since its a static variable */ +static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE]; +#endif + +/* + * Driver configurations + */ +struct omap_nand_info { + struct bch_control *control; + enum omap_ecc ecc_scheme; + uint8_t cs; + uint8_t ws; /* wait status pin (0,1) */ +}; + +/* We are wasting a bit of memory but al least we are safe */ +static struct omap_nand_info omap_nand_info[GPMC_MAX_CS]; + +/* + * omap_nand_hwcontrol - Set the address pointers corretly for the + * following address/data/command operation + */ +static void omap_nand_hwcontrol(struct mtd_info *mtd, int32_t cmd, + uint32_t ctrl) +{ + register struct nand_chip *this = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(this); + int cs = info->cs; + + /* + * Point the IO_ADDR to DATA and ADDRESS registers instead + * of chip address + */ + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_adr; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + this->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + break; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Check wait pin as dev ready indicator */ +static int omap_dev_ready(struct mtd_info *mtd) +{ + register struct nand_chip *this = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(this); + return gpmc_cfg->status & (1 << (8 + info->ws)); +} + +/* + * gen_true_ecc - This function will generate true ECC value, which + * can be used when correcting data read from NAND flash memory core + * + * @ecc_buf: buffer to store ecc code + * + * @return: re-formatted ECC value + */ +static uint32_t gen_true_ecc(uint8_t *ecc_buf) +{ + return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | + ((ecc_buf[2] & 0x0F) << 8); +} + +/* + * omap_correct_data - Compares the ecc read from nand spare area with ECC + * registers values and corrects one bit error if it has occurred + * Further details can be had from OMAP TRM and the following selected links: + * http://en.wikipedia.org/wiki/Hamming_code + * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pdf + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint32_t orig_ecc, new_ecc, res, hm; + uint16_t parity_bits, byte; + uint8_t bit; + + /* Regenerate the orginal ECC */ + orig_ecc = gen_true_ecc(read_ecc); + new_ecc = gen_true_ecc(calc_ecc); + /* Get the XOR of real ecc */ + res = orig_ecc ^ new_ecc; + if (res) { + /* Get the hamming width */ + hm = hweight32(res); + /* Single bit errors can be corrected! */ + if (hm == 12) { + /* Correctable data! */ + parity_bits = res >> 16; + bit = (parity_bits & 0x7); + byte = (parity_bits >> 3) & 0x1FF; + /* Flip the bit to correct */ + dat[byte] ^= (0x1 << bit); + } else if (hm == 1) { + printf("Error: Ecc is wrong\n"); + /* ECC itself is corrupted */ + return 2; + } else { + /* + * hm distance != parity pairs OR one, could mean 2 bit + * error OR potentially be on a blank page.. + * orig_ecc: contains spare area data from nand flash. + * new_ecc: generated ecc while reading data area. + * Note: if the ecc = 0, all data bits from which it was + * generated are 0xFF. + * The 3 byte(24 bits) ecc is generated per 512byte + * chunk of a page. If orig_ecc(from spare area) + * is 0xFF && new_ecc(computed now from data area)=0x0, + * this means that data area is 0xFF and spare area is + * 0xFF. A sure sign of a erased page! + */ + if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) + return 0; + printf("Error: Bad compare! failed\n"); + /* detected 2 bit error */ + return -EBADMSG; + } + } + return 0; +} + +/* + * omap_enable_hwecc - configures GPMC as per ECC scheme before read/write + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +__maybe_unused +static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(nand); + unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0; + unsigned int ecc_algo = 0; + unsigned int bch_type = 0; + unsigned int eccsize1 = 0x00, eccsize0 = 0x00, bch_wrapmode = 0x00; + u32 ecc_size_config_val = 0; + u32 ecc_config_val = 0; + int cs = info->cs; + + /* configure GPMC for specific ecc-scheme */ + switch (info->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_SW: + return; + case OMAP_ECC_HAM1_CODE_HW: + ecc_algo = 0x0; + bch_type = 0x0; + bch_wrapmode = 0x00; + eccsize0 = 0xFF; + eccsize1 = 0xFF; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + ecc_algo = 0x1; + bch_type = 0x1; + if (mode == NAND_ECC_WRITE) { + bch_wrapmode = 0x01; + eccsize0 = 0; /* extra bits in nibbles per sector */ + eccsize1 = 28; /* OOB bits in nibbles per sector */ + } else { + bch_wrapmode = 0x01; + eccsize0 = 26; /* ECC bits in nibbles per sector */ + eccsize1 = 2; /* non-ECC bits in nibbles per sector */ + } + break; + case OMAP_ECC_BCH16_CODE_HW: + ecc_algo = 0x1; + bch_type = 0x2; + if (mode == NAND_ECC_WRITE) { + bch_wrapmode = 0x01; + eccsize0 = 0; /* extra bits in nibbles per sector */ + eccsize1 = 52; /* OOB bits in nibbles per sector */ + } else { + bch_wrapmode = 0x01; + eccsize0 = 52; /* ECC bits in nibbles per sector */ + eccsize1 = 0; /* non-ECC bits in nibbles per sector */ + } + break; + default: + return; + } + /* Clear ecc and enable bits */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + /* Configure ecc size for BCH */ + ecc_size_config_val = (eccsize1 << 22) | (eccsize0 << 12); + writel(ecc_size_config_val, &gpmc_cfg->ecc_size_config); + + /* Configure device details for BCH engine */ + ecc_config_val = ((ecc_algo << 16) | /* HAM1 | BCHx */ + (bch_type << 12) | /* BCH4/BCH8/BCH16 */ + (bch_wrapmode << 8) | /* wrap mode */ + (dev_width << 7) | /* bus width */ + (0x0 << 4) | /* number of sectors */ + (cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + writel(ecc_config_val, &gpmc_cfg->ecc_config); +} + +/* + * omap_calculate_ecc - Read ECC result + * @mtd: MTD structure + * @dat: unused + * @ecc_code: ecc_code buffer + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as + * long nobody is trying to write data on the seemingly unused page. + * Reading an erased page will produce an ECC mismatch between + * generated and read ECC bytes that has to be dealt with separately. + * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC + * is used, the result of read will be 0x0 while the ECC offsets of the + * spare area will be 0xFF which will result in an ECC mismatch. + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(chip); + const uint32_t *ptr; + uint32_t val = 0; + int8_t i = 0, j; + + switch (info->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_HW: + val = readl(&gpmc_cfg->ecc1_result); + ecc_code[0] = val & 0xFF; + ecc_code[1] = (val >> 16) & 0xFF; + ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0); + break; +#ifdef CONFIG_BCH + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#endif + case OMAP_ECC_BCH8_CODE_HW: + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; + val = readl(ptr); + ecc_code[i++] = (val >> 0) & 0xFF; + ptr--; + for (j = 0; j < 3; j++) { + val = readl(ptr); + ecc_code[i++] = (val >> 24) & 0xFF; + ecc_code[i++] = (val >> 16) & 0xFF; + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + ptr--; + } + break; + case OMAP_ECC_BCH16_CODE_HW: + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[2]); + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[1]); + ecc_code[i++] = (val >> 24) & 0xFF; + ecc_code[i++] = (val >> 16) & 0xFF; + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + val = readl(&gpmc_cfg->bch_result_4_6[0].bch_result_x[0]); + ecc_code[i++] = (val >> 24) & 0xFF; + ecc_code[i++] = (val >> 16) & 0xFF; + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + for (j = 3; j >= 0; j--) { + val = readl(&gpmc_cfg->bch_result_0_3[0].bch_result_x[j] + ); + ecc_code[i++] = (val >> 24) & 0xFF; + ecc_code[i++] = (val >> 16) & 0xFF; + ecc_code[i++] = (val >> 8) & 0xFF; + ecc_code[i++] = (val >> 0) & 0xFF; + } + break; + default: + return -EINVAL; + } + /* ECC scheme specific syndrome customizations */ + switch (info->ecc_scheme) { + case OMAP_ECC_HAM1_CODE_HW: + break; +#ifdef CONFIG_BCH + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + + for (i = 0; i < chip->ecc.bytes; i++) + *(ecc_code + i) = *(ecc_code + i) ^ + bch8_polynomial[i]; + break; +#endif + case OMAP_ECC_BCH8_CODE_HW: + ecc_code[chip->ecc.bytes - 1] = 0x00; + break; + case OMAP_ECC_BCH16_CODE_HW: + break; + default: + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH + +#define PREFETCH_CONFIG1_CS_SHIFT 24 +#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 +#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) +#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) +#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) +#define ENABLE_PREFETCH (1 << 7) + +/** + * omap_prefetch_enable - configures and starts prefetch transfer + * @fifo_th: fifo threshold to be used for read/ write + * @count: number of bytes to be transferred + * @is_write: prefetch read(0) or write post(1) mode + * @cs: chip select to use + */ +static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs) +{ + uint32_t val; + + if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) + return -EINVAL; + + if (readl(&gpmc_cfg->prefetch_control)) + return -EBUSY; + + /* Set the amount of bytes to be prefetched */ + writel(count, &gpmc_cfg->prefetch_config2); + + val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) | + PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH; + writel(val, &gpmc_cfg->prefetch_config1); + + /* Start the prefetch engine */ + writel(1, &gpmc_cfg->prefetch_control); + + return 0; +} + +/** + * omap_prefetch_reset - disables and stops the prefetch engine + */ +static void omap_prefetch_reset(void) +{ + writel(0, &gpmc_cfg->prefetch_control); + writel(0, &gpmc_cfg->prefetch_config1); +} + +static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len) +{ + int ret; + uint32_t cnt; + struct omap_nand_info *info = nand_get_controller_data(chip); + + ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs); + if (ret < 0) + return ret; + + do { + int i; + + cnt = readl(&gpmc_cfg->prefetch_status); + cnt = PREFETCH_STATUS_FIFO_CNT(cnt); + + for (i = 0; i < cnt / 4; i++) { + *buf++ = readl(CONFIG_SYS_NAND_BASE); + len -= 4; + } + } while (len); + + omap_prefetch_reset(); + + return 0; +} + +static inline void omap_nand_read(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (chip->options & NAND_BUSWIDTH_16) + nand_read_buf16(mtd, buf, len); + else + nand_read_buf(mtd, buf, len); +} + +static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int ret; + uint32_t head, tail; + struct nand_chip *chip = mtd_to_nand(mtd); + + /* + * If the destination buffer is unaligned, start with reading + * the overlap byte-wise. + */ + head = ((uint32_t) buf) % 4; + if (head) { + omap_nand_read(mtd, buf, head); + buf += head; + len -= head; + } + + /* + * Only transfer multiples of 4 bytes in a pre-fetched fashion. + * If there's a residue, care for it byte-wise afterwards. + */ + tail = len % 4; + + ret = __read_prefetch_aligned(chip, (uint32_t *)buf, len - tail); + if (ret < 0) { + /* fallback in case the prefetch engine is busy */ + omap_nand_read(mtd, buf, len); + } else if (tail) { + buf += len - tail; + omap_nand_read(mtd, buf, tail); + } +} +#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */ + +#ifdef CONFIG_NAND_OMAP_ELM +/* + * omap_reverse_list - re-orders list elements in reverse order [internal] + * @list: pointer to start of list + * @length: length of list +*/ +static void omap_reverse_list(u8 *list, unsigned int length) +{ + unsigned int i, j; + unsigned int half_length = length / 2; + u8 tmp; + for (i = 0, j = length - 1; i < half_length; i++, j--) { + tmp = list[i]; + list[i] = list[j]; + list[j] = tmp; + } +} + +/* + * omap_correct_data_bch - Compares the ecc read from nand spare area + * with ECC registers values and corrects one bit error if it has occurred + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash (ignored) + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + uint32_t error_count = 0, error_max; + uint32_t error_loc[ELM_MAX_ERROR_COUNT]; + enum bch_level bch_type; + uint32_t i, ecc_flag = 0; + uint8_t count; + uint32_t byte_pos, bit_pos; + int err = 0; + + /* check calculated ecc */ + for (i = 0; i < ecc->bytes && !ecc_flag; i++) { + if (calc_ecc[i] != 0x00) + ecc_flag = 1; + } + if (!ecc_flag) + return 0; + + /* check for whether its a erased-page */ + ecc_flag = 0; + for (i = 0; i < ecc->bytes && !ecc_flag; i++) { + if (read_ecc[i] != 0xff) + ecc_flag = 1; + } + if (!ecc_flag) + return 0; + + /* + * while reading ECC result we read it in big endian. + * Hence while loading to ELM we have rotate to get the right endian. + */ + switch (info->ecc_scheme) { + case OMAP_ECC_BCH8_CODE_HW: + bch_type = BCH_8_BIT; + omap_reverse_list(calc_ecc, ecc->bytes - 1); + break; + case OMAP_ECC_BCH16_CODE_HW: + bch_type = BCH_16_BIT; + omap_reverse_list(calc_ecc, ecc->bytes); + break; + default: + return -EINVAL; + } + /* use elm module to check for errors */ + elm_config(bch_type); + err = elm_check_error(calc_ecc, bch_type, &error_count, error_loc); + if (err) + return err; + + /* correct bch error */ + for (count = 0; count < error_count; count++) { + switch (info->ecc_scheme) { + case OMAP_ECC_BCH8_CODE_HW: + /* 14th byte in ECC is reserved to match ROM layout */ + error_max = SECTOR_BYTES + (ecc->bytes - 1); + break; + case OMAP_ECC_BCH16_CODE_HW: + error_max = SECTOR_BYTES + ecc->bytes; + break; + default: + return -EINVAL; + } + byte_pos = error_max - (error_loc[count] / 8) - 1; + bit_pos = error_loc[count] % 8; + if (byte_pos < SECTOR_BYTES) { + dat[byte_pos] ^= 1 << bit_pos; + debug("nand: bit-flip corrected @data=%d\n", byte_pos); + } else if (byte_pos < error_max) { + read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos; + debug("nand: bit-flip corrected @oob=%d\n", byte_pos - + SECTOR_BYTES); + } else { + err = -EBADMSG; + printf("nand: error: invalid bit-flip location\n"); + } + } + return (err) ? err : error_count; +} + +/** + * omap_read_page_bch - hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi + * @page: page number to read + * + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *oob = chip->oob_poi; + uint32_t data_pos; + uint32_t oob_pos; + + data_pos = 0; + /* oob area start */ + oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; + oob += chip->ecc.layout->eccpos[0]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, + oob += eccbytes) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + /* read data */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, -1); + chip->read_buf(mtd, p, eccsize); + + /* read respective ecc from oob area */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1); + chip->read_buf(mtd, oob, eccbytes); + /* read syndrome */ + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + } + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} +#endif /* CONFIG_NAND_OMAP_ELM */ + +/* + * OMAP3 BCH8 support (with BCH library) + */ +#ifdef CONFIG_BCH +/** + * omap_correct_data_bch_sw - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct nand_chip *chip = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(chip); + + count = decode_bch(info->control, NULL, SECTOR_BYTES, + read_ecc, calc_ecc, NULL, errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < SECTOR_BYTES << 3) + data[errloc[i] >> 3] ^= 1 << (errloc[i] & 7); + debug("corrected bitflip %u\n", errloc[i]); +#ifdef DEBUG + puts("read_ecc: "); + /* + * BCH8 have 13 bytes of ECC; BCH4 needs adoption + * here! + */ + for (i = 0; i < 13; i++) + printf("%02x ", read_ecc[i]); + puts("\n"); + puts("calc_ecc: "); + for (i = 0; i < 13; i++) + printf("%02x ", calc_ecc[i]); + puts("\n"); +#endif + } + } else if (count < 0) { + puts("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void __maybe_unused omap_free_bch(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct omap_nand_info *info = nand_get_controller_data(chip); + + if (info->control) { + free_bch(info->control); + info->control = NULL; + } +} +#endif /* CONFIG_BCH */ + +/** + * omap_select_ecc_scheme - configures driver for particular ecc-scheme + * @nand: NAND chip device structure + * @ecc_scheme: ecc scheme to configure + * @pagesize: number of main-area bytes per page of NAND device + * @oobsize: number of OOB/spare bytes per page of NAND device + */ +static int omap_select_ecc_scheme(struct nand_chip *nand, + enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { + struct omap_nand_info *info = nand_get_controller_data(nand); + struct nand_ecclayout *ecclayout = &omap_ecclayout; + int eccsteps = pagesize / SECTOR_BYTES; + int i; + + switch (ecc_scheme) { + case OMAP_ECC_HAM1_CODE_SW: + debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n"); + /* For this ecc-scheme, ecc.bytes, ecc.layout, ... are + * initialized in nand_scan_tail(), so just set ecc.mode */ + info->control = NULL; + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = NULL; + nand->ecc.size = 0; + break; + + case OMAP_ECC_HAM1_CODE_HW: + debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (3 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + info->control = NULL; + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 1; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 3; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + for (i = 0; i < ecclayout->eccbytes; i++) { + if (nand->options & NAND_BUSWIDTH_16) + ecclayout->eccpos[i] = i + 2; + else + ecclayout->eccpos[i] = i + 1; + } + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + break; + + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_BCH + debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (13 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + /* check if BCH S/W library can be used for error detection */ + info->control = init_bch(13, 8, 0x201b); + if (!info->control) { + printf("nand: error: could not init_bch()\n"); + return -ENODEV; + } + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 8; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 13; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data_bch_sw; + nand->ecc.calculate = omap_calculate_ecc; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + for (i = 1; i < ecclayout->eccbytes; i++) { + if (i % nand->ecc.bytes) + ecclayout->eccpos[i] = + ecclayout->eccpos[i - 1] + 1; + else + ecclayout->eccpos[i] = + ecclayout->eccpos[i - 1] + 2; + } + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + break; +#else + printf("nand: error: CONFIG_BCH required for ECC\n"); + return -EINVAL; +#endif + + case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_NAND_OMAP_ELM + debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (14 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + /* intialize ELM for ECC error detection */ + elm_init(); + info->control = NULL; + /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.strength = 8; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 14; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc; + nand->ecc.read_page = omap_read_page_bch; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + for (i = 0; i < ecclayout->eccbytes; i++) + ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - + BADBLOCK_MARKER_LENGTH; + break; +#else + printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); + return -EINVAL; +#endif + + case OMAP_ECC_BCH16_CODE_HW: +#ifdef CONFIG_NAND_OMAP_ELM + debug("nand: using OMAP_ECC_BCH16_CODE_HW\n"); + /* check ecc-scheme requirements before updating ecc info */ + if ((26 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { + printf("nand: error: insufficient OOB: require=%d\n", ( + (26 * eccsteps) + BADBLOCK_MARKER_LENGTH)); + return -EINVAL; + } + /* intialize ELM for ECC error detection */ + elm_init(); + /* populate ecc specific fields */ + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = SECTOR_BYTES; + nand->ecc.bytes = 26; + nand->ecc.strength = 16; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data_bch; + nand->ecc.calculate = omap_calculate_ecc; + nand->ecc.read_page = omap_read_page_bch; + /* define ecc-layout */ + ecclayout->eccbytes = nand->ecc.bytes * eccsteps; + for (i = 0; i < ecclayout->eccbytes; i++) + ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes - + BADBLOCK_MARKER_LENGTH; + break; +#else + printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); + return -EINVAL; +#endif + default: + debug("nand: error: ecc scheme not enabled or supported\n"); + return -EINVAL; + } + + /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */ + if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW) + nand->ecc.layout = ecclayout; + + info->ecc_scheme = ecc_scheme; + return 0; +} + +#ifndef CONFIG_SPL_BUILD +/* + * omap_nand_switch_ecc - switch the ECC operation between different engines + * (h/w and s/w) and different algorithms (hamming and BCHx) + * + * @hardware - true if one of the HW engines should be used + * @eccstrength - the number of bits that could be corrected + * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16) + */ +int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) +{ + struct nand_chip *nand; + struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); + int err = 0; + + if (!mtd) { + printf("nand: error: no NAND devices found\n"); + return -ENODEV; + } + + nand = mtd_to_nand(mtd); + nand->options |= NAND_OWN_BUFFERS; + nand->options &= ~NAND_SUBPAGE_READ; + /* Setup the ecc configurations again */ + if (hardware) { + if (eccstrength == 1) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_HAM1_CODE_HW, + mtd->writesize, mtd->oobsize); + } else if (eccstrength == 8) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_BCH8_CODE_HW, + mtd->writesize, mtd->oobsize); + } else if (eccstrength == 16) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_BCH16_CODE_HW, + mtd->writesize, mtd->oobsize); + } else { + printf("nand: error: unsupported ECC scheme\n"); + return -EINVAL; + } + } else { + if (eccstrength == 1) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_HAM1_CODE_SW, + mtd->writesize, mtd->oobsize); + } else if (eccstrength == 8) { + err = omap_select_ecc_scheme(nand, + OMAP_ECC_BCH8_CODE_HW_DETECTION_SW, + mtd->writesize, mtd->oobsize); + } else { + printf("nand: error: unsupported ECC scheme\n"); + return -EINVAL; + } + } + + /* Update NAND handling after ECC mode switch */ + if (!err) + err = nand_scan_tail(mtd); + return err; +} +#endif /* CONFIG_SPL_BUILD */ + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific: + * - 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 + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - waitfunc: hardwarespecific function for accesing device ready/busy line + * - ecc.hwctl: function to enable (reset) hardware ecc generator + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + */ +int board_nand_init(struct nand_chip *nand) +{ + int32_t gpmc_config = 0; + int cs = cs_next++; + int err = 0; + /* + * xloader/Uboot's gpmc configuration would have configured GPMC for + * nand type of memory. The following logic scans and latches on to the + * first CS with NAND type memory. + * TBD: need to make this logic generic to handle multiple CS NAND + * devices. + */ + while (cs < GPMC_MAX_CS) { + /* Check if NAND type is set */ + if ((readl(&gpmc_cfg->cs[cs].config1) & 0xC00) == 0x800) { + /* Found it!! */ + break; + } + cs++; + } + if (cs >= GPMC_MAX_CS) { + printf("nand: error: Unable to find NAND settings in " + "GPMC Configuration - quitting\n"); + return -ENODEV; + } + + gpmc_config = readl(&gpmc_cfg->config); + /* Disable Write protect */ + gpmc_config |= 0x10; + writel(gpmc_config, &gpmc_cfg->config); + + nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat; + nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; + omap_nand_info[cs].control = NULL; + omap_nand_info[cs].cs = cs; + omap_nand_info[cs].ws = wscfg[cs]; + nand_set_controller_data(nand, &omap_nand_info[cs]); + nand->cmd_ctrl = omap_nand_hwcontrol; + nand->options |= NAND_NO_PADDING | NAND_CACHEPRG; + nand->chip_delay = 100; + nand->ecc.layout = &omap_ecclayout; + + /* configure driver and controller based on NAND device bus-width */ + gpmc_config = readl(&gpmc_cfg->cs[cs].config1); +#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) + nand->options |= NAND_BUSWIDTH_16; + writel(gpmc_config | (0x1 << 12), &gpmc_cfg->cs[cs].config1); +#else + nand->options &= ~NAND_BUSWIDTH_16; + writel(gpmc_config & ~(0x1 << 12), &gpmc_cfg->cs[cs].config1); +#endif + /* select ECC scheme */ +#if defined(CONFIG_NAND_OMAP_ECCSCHEME) + err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME, + CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE); +#else + /* pagesize and oobsize are not required to configure sw ecc-scheme */ + err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, + 0, 0); +#endif + if (err) + return err; + +#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH + nand->read_buf = omap_nand_read_prefetch; +#else + if (nand->options & NAND_BUSWIDTH_16) + nand->read_buf = nand_read_buf16; + else + nand->read_buf = nand_read_buf; +#endif + + nand->dev_ready = omap_dev_ready; + + return 0; +} diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.c b/drivers/mtd/nand/raw/pxa3xx_nand.c new file mode 100644 index 0000000000..4c783f1e1e --- /dev/null +++ b/drivers/mtd/nand/raw/pxa3xx_nand.c @@ -0,0 +1,1828 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/mtd/nand/raw/pxa3xx_nand.c + * + * Copyright © 2005 Intel Corporation + * Copyright © 2006 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pxa3xx_nand.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define TIMEOUT_DRAIN_FIFO 5 /* in ms */ +#define CHIP_DELAY_TIMEOUT 200 +#define NAND_STOP_DELAY 40 + +/* + * Define a buffer size for the initial command that detects the flash device: + * STATUS, READID and PARAM. + * ONFI param page is 256 bytes, and there are three redundant copies + * to be read. JEDEC param page is 512 bytes, and there are also three + * redundant copies to be read. + * Hence this buffer should be at least 512 x 3. Let's pick 2048. + */ +#define INIT_BUFFER_SIZE 2048 + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBDR0 (0x1C) /* Bad Block Register 0 */ +#define NDBDR1 (0x20) /* Bad Block Register 1 */ +#define NDECCCTRL (0x28) /* ECC control */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ + +#define NDCR_SPARE_EN (0x1 << 31) +#define NDCR_ECC_EN (0x1 << 30) +#define NDCR_DMA_EN (0x1 << 29) +#define NDCR_ND_RUN (0x1 << 28) +#define NDCR_DWIDTH_C (0x1 << 27) +#define NDCR_DWIDTH_M (0x1 << 26) +#define NDCR_PAGE_SZ (0x1 << 24) +#define NDCR_NCSX (0x1 << 23) +#define NDCR_ND_MODE (0x3 << 21) +#define NDCR_NAND_MODE (0x0) +#define NDCR_CLR_PG_CNT (0x1 << 20) +#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) +#define NDCR_RD_ID_CNT_MASK (0x7 << 16) +#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) + +#define NDCR_RA_START (0x1 << 15) +#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_ND_ARB_EN (0x1 << 12) +#define NDCR_INT_MASK (0xFFF) + +#define NDSR_MASK (0xfff) +#define NDSR_ERR_CNT_OFF (16) +#define NDSR_ERR_CNT_MASK (0x1f) +#define NDSR_ERR_CNT(sr) ((sr >> NDSR_ERR_CNT_OFF) & NDSR_ERR_CNT_MASK) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) +#define NDSR_CS0_PAGED (0x1 << 10) +#define NDSR_CS1_PAGED (0x1 << 9) +#define NDSR_CS0_CMDD (0x1 << 8) +#define NDSR_CS1_CMDD (0x1 << 7) +#define NDSR_CS0_BBD (0x1 << 6) +#define NDSR_CS1_BBD (0x1 << 5) +#define NDSR_UNCORERR (0x1 << 4) +#define NDSR_CORERR (0x1 << 3) +#define NDSR_WRDREQ (0x1 << 2) +#define NDSR_RDDREQ (0x1 << 1) +#define NDSR_WRCMDREQ (0x1) + +#define NDCB0_LEN_OVRD (0x1 << 28) +#define NDCB0_ST_ROW_EN (0x1 << 26) +#define NDCB0_AUTO_RS (0x1 << 25) +#define NDCB0_CSEL (0x1 << 24) +#define NDCB0_EXT_CMD_TYPE_MASK (0x7 << 29) +#define NDCB0_EXT_CMD_TYPE(x) (((x) << 29) & NDCB0_EXT_CMD_TYPE_MASK) +#define NDCB0_CMD_TYPE_MASK (0x7 << 21) +#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK) +#define NDCB0_NC (0x1 << 20) +#define NDCB0_DBC (0x1 << 19) +#define NDCB0_ADDR_CYC_MASK (0x7 << 16) +#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) +#define NDCB0_CMD2_MASK (0xff << 8) +#define NDCB0_CMD1_MASK (0xff) +#define NDCB0_ADDR_CYC_SHIFT (16) + +#define EXT_CMD_TYPE_DISPATCH 6 /* Command dispatch */ +#define EXT_CMD_TYPE_NAKED_RW 5 /* Naked read or Naked write */ +#define EXT_CMD_TYPE_READ 4 /* Read */ +#define EXT_CMD_TYPE_DISP_WR 4 /* Command dispatch with write */ +#define EXT_CMD_TYPE_FINAL 3 /* Final command */ +#define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ +#define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */ + +/* + * This should be large enough to read 'ONFI' and 'JEDEC'. + * Let's use 7 bytes, which is the maximum ID count supported + * by the controller (see NDCR_RD_ID_CNT_MASK). + */ +#define READ_ID_BYTES 7 + +/* macros for registers read/write */ +#define nand_writel(info, off, val) \ + writel((val), (info)->mmio_base + (off)) + +#define nand_readl(info, off) \ + readl((info)->mmio_base + (off)) + +/* error code and state */ +enum { + ERR_NONE = 0, + ERR_DMABUSERR = -1, + ERR_SENDCMD = -2, + ERR_UNCORERR = -3, + ERR_BBERR = -4, + ERR_CORERR = -5, +}; + +enum { + STATE_IDLE = 0, + STATE_PREPARED, + STATE_CMD_HANDLE, + STATE_DMA_READING, + STATE_DMA_WRITING, + STATE_DMA_DONE, + STATE_PIO_READING, + STATE_PIO_WRITING, + STATE_CMD_DONE, + STATE_READY, +}; + +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, +}; + +struct pxa3xx_nand_host { + struct nand_chip chip; + void *info_data; + + /* page size of attached chip */ + int use_ecc; + int cs; + + /* calculated from pxa3xx_nand_flash data */ + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; +}; + +struct pxa3xx_nand_info { + struct nand_hw_control controller; + struct pxa3xx_nand_platform_data *pdata; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long mmio_phys; + int cmd_complete, dev_ready; + + unsigned int buf_start; + unsigned int buf_count; + unsigned int buf_size; + unsigned int data_buff_pos; + unsigned int oob_buff_pos; + + unsigned char *data_buff; + unsigned char *oob_buff; + + struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; + unsigned int state; + + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + + int cs; + int use_ecc; /* use HW ECC ? */ + int ecc_bch; /* using BCH ECC? */ + int use_spare; /* use spare ? */ + int need_wait; + + /* Amount of real data per full chunk */ + unsigned int chunk_size; + + /* Amount of spare data per full chunk */ + unsigned int spare_size; + + /* Number of full chunks (i.e chunk_size + spare_size) */ + unsigned int nfullchunks; + + /* + * Total number of chunks. If equal to nfullchunks, then there + * are only full chunks. Otherwise, there is one last chunk of + * size (last_chunk_size + last_spare_size) + */ + unsigned int ntotalchunks; + + /* Amount of real data in the last chunk */ + unsigned int last_chunk_size; + + /* Amount of spare data in the last chunk */ + unsigned int last_spare_size; + + unsigned int ecc_size; + unsigned int ecc_err_cnt; + unsigned int max_bitflips; + int retcode; + + /* + * Variables only valid during command + * execution. step_chunk_size and step_spare_size is the + * amount of real data and spare data in the current + * chunk. cur_chunk is the current chunk being + * read/programmed. + */ + unsigned int step_chunk_size; + unsigned int step_spare_size; + unsigned int cur_chunk; + + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; + + /* generated NDCBx register values */ + uint32_t ndcb0; + uint32_t ndcb1; + uint32_t ndcb2; + uint32_t ndcb3; +}; + +static struct pxa3xx_nand_timing timing[] = { + /* + * tCH Enable signal hold time + * tCS Enable signal setup time + * tWH ND_nWE high duration + * tWP ND_nWE pulse time + * tRH ND_nRE high duration + * tRP ND_nRE pulse width + * tR ND_nWE high to ND_nRE low for read + * tWHR ND_nWE high to ND_nRE low for status read + * tAR ND_ALE low to ND_nRE low delay + */ + /*ch cs wh wp rh rp r whr ar */ + { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, + { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, + { 10, 25, 15, 25, 15, 30, 25000, 60, 10, }, + { 10, 35, 15, 25, 15, 25, 25000, 60, 10, }, + { 5, 20, 10, 12, 10, 12, 25000, 60, 10, }, +}; + +static struct pxa3xx_nand_flash builtin_flash_types[] = { + /* + * chip_id + * flash_width Width of Flash memory (DWIDTH_M) + * dfc_width Width of flash controller(DWIDTH_C) + * *timing + * http://www.linux-mtd.infradead.org/nand-data/nanddata.html + */ + { 0x46ec, 16, 16, &timing[1] }, + { 0xdaec, 8, 8, &timing[1] }, + { 0xd7ec, 8, 8, &timing[1] }, + { 0xa12c, 8, 8, &timing[2] }, + { 0xb12c, 16, 16, &timing[2] }, + { 0xdc2c, 8, 8, &timing[2] }, + { 0xcc2c, 16, 16, &timing[2] }, + { 0xba20, 16, 16, &timing[3] }, + { 0xda98, 8, 8, &timing[4] }, +}; + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT +static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; +static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 8, + .len = 6, + .veroffs = 14, + .maxblocks = 8, /* Last 8 blocks in each chip */ + .pattern = bbt_mirror_pattern +}; +#endif + +static struct nand_ecclayout ecc_layout_2KB_bch4bit = { + .eccbytes = 32, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 30} } +}; + +static struct nand_ecclayout ecc_layout_2KB_bch8bit = { + .eccbytes = 64, + .eccpos = { + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { {1, 4}, {6, 26} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch4bit = { + .eccbytes = 64, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + /* Bootrom looks in bytes 0 & 5 for bad blocks */ + .oobfree = { {6, 26}, { 64, 32} } +}; + +static struct nand_ecclayout ecc_layout_8KB_bch4bit = { + .eccbytes = 128, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255}, + + /* Bootrom looks in bytes 0 & 5 for bad blocks */ + .oobfree = { {1, 4}, {6, 26}, { 64, 32}, {128, 32}, {192, 32} } +}; + +static struct nand_ecclayout ecc_layout_4KB_bch8bit = { + .eccbytes = 128, + .eccpos = { + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { } +}; + +static struct nand_ecclayout ecc_layout_8KB_bch8bit = { + .eccbytes = 256, + .eccpos = {}, + /* HW ECC handles all ECC data and all spare area is free for OOB */ + .oobfree = {{0, 160} } +}; + +#define NDTR0_tCH(c) (min((c), 7) << 19) +#define NDTR0_tCS(c) (min((c), 7) << 16) +#define NDTR0_tWH(c) (min((c), 7) << 11) +#define NDTR0_tWP(c) (min((c), 7) << 8) +#define NDTR0_tRH(c) (min((c), 7) << 3) +#define NDTR0_tRP(c) (min((c), 7) << 0) + +#define NDTR1_tR(c) (min((c), 65535) << 16) +#define NDTR1_tWHR(c) (min((c), 15) << 4) +#define NDTR1_tAR(c) (min((c), 15) << 0) + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) + +static enum pxa3xx_nand_variant pxa3xx_nand_get_variant(void) +{ + /* We only support the Armada 370/XP/38x for now */ + return PXA3XX_NAND_VARIANT_ARMADA370; +} + +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, + const struct pxa3xx_nand_timing *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + unsigned long nand_clk = mvebu_get_nand_clock(); + uint32_t ndtr0, ndtr1; + + ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | + NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | + NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | + NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | + NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | + NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | + NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, + const struct nand_sdr_timings *t) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct nand_chip *chip = &host->chip; + unsigned long nand_clk = mvebu_get_nand_clock(); + uint32_t ndtr0, ndtr1; + + u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); + u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); + u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); + u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); + u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); + u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); + u32 tR = chip->chip_delay * 1000; + u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); + u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000); + + /* fallback to a default value if tR = 0 */ + if (!tR) + tR = 20000; + + ndtr0 = NDTR0_tCH(ns2cycle(tCH_min, nand_clk)) | + NDTR0_tCS(ns2cycle(tCS_min, nand_clk)) | + NDTR0_tWH(ns2cycle(tWH_min, nand_clk)) | + NDTR0_tWP(ns2cycle(tWP_min, nand_clk)) | + NDTR0_tRH(ns2cycle(tREH_min, nand_clk)) | + NDTR0_tRP(ns2cycle(tRP_min, nand_clk)); + + ndtr1 = NDTR1_tR(ns2cycle(tR, nand_clk)) | + NDTR1_tWHR(ns2cycle(tWHR_min, nand_clk)) | + NDTR1_tAR(ns2cycle(tAR_min, nand_clk)); + + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; + nand_writel(info, NDTR0CS0, ndtr0); + nand_writel(info, NDTR1CS0, ndtr1); +} + +static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host) +{ + const struct nand_sdr_timings *timings; + struct nand_chip *chip = &host->chip; + struct pxa3xx_nand_info *info = host->info_data; + const struct pxa3xx_nand_flash *f = NULL; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + int mode, id, ntypes, i; + + mode = onfi_get_async_timing_mode(chip); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + ntypes = ARRAY_SIZE(builtin_flash_types); + + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + id = chip->read_byte(mtd); + id |= chip->read_byte(mtd) << 0x8; + + for (i = 0; i < ntypes; i++) { + f = &builtin_flash_types[i]; + + if (f->chip_id == id) + break; + } + + if (i == ntypes) { + dev_err(&info->pdev->dev, "Error: timings not found\n"); + return -EINVAL; + } + + pxa3xx_nand_set_timing(host, f->timing); + + if (f->flash_width == 16) { + info->reg_ndcr |= NDCR_DWIDTH_M; + chip->options |= NAND_BUSWIDTH_16; + } + + info->reg_ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; + } else { + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + } + + return 0; +} + +/** + * NOTE: it is a must to set ND_RUN first, then write + * command buffer, otherwise, it does not work. + * We enable all the interrupt at the same time, and + * let pxa3xx_nand_irq to handle all logic. + */ +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + ndcr = info->reg_ndcr; + + if (info->use_ecc) { + ndcr |= NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x1); + } else { + ndcr &= ~NDCR_ECC_EN; + if (info->ecc_bch) + nand_writel(info, NDECCCTRL, 0x0); + } + + ndcr &= ~NDCR_DMA_EN; + + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; + + ndcr |= NDCR_ND_RUN; + + /* clear status bits and run */ + nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, 0); + nand_writel(info, NDCR, ndcr); +} + +static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +{ + uint32_t ndcr; + + ndcr = nand_readl(info, NDCR); + nand_writel(info, NDCR, ndcr | int_mask); +} + +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len) +{ + if (info->ecc_bch) { + u32 ts; + + /* + * According to the datasheet, when reading from NDDB + * with BCH enabled, after each 32 bytes reads, we + * have to make sure that the NDSR.RDDREQ bit is set. + * + * Drain the FIFO 8 32 bits reads at a time, and skip + * the polling on the last read. + */ + while (len > 8) { + readsl(info->mmio_base + NDDB, data, 8); + + ts = get_timer(0); + while (!(nand_readl(info, NDSR) & NDSR_RDDREQ)) { + if (get_timer(ts) > TIMEOUT_DRAIN_FIFO) { + dev_err(&info->pdev->dev, + "Timeout on RDDREQ while draining the FIFO\n"); + return; + } + } + + data += 32; + len -= 8; + } + } + + readsl(info->mmio_base + NDDB, data, len); +} + +static void handle_data_pio(struct pxa3xx_nand_info *info) +{ + switch (info->state) { + case STATE_PIO_WRITING: + if (info->step_chunk_size) + writesl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4)); + + if (info->step_spare_size) + writesl(info->mmio_base + NDDB, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->step_spare_size, 4)); + break; + case STATE_PIO_READING: + if (info->step_chunk_size) + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4)); + + if (info->step_spare_size) + drain_fifo(info, + info->oob_buff + info->oob_buff_pos, + DIV_ROUND_UP(info->step_spare_size, 4)); + break; + default: + dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, + info->state); + BUG(); + } + + /* Update buffer pointers for multi-page read/write */ + info->data_buff_pos += info->step_chunk_size; + info->oob_buff_pos += info->step_spare_size; +} + +static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info) +{ + handle_data_pio(info); + + info->state = STATE_CMD_DONE; + nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); +} + +static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info) +{ + unsigned int status, is_completed = 0, is_ready = 0; + unsigned int ready, cmd_done; + irqreturn_t ret = IRQ_HANDLED; + + if (info->cs == 0) { + ready = NDSR_FLASH_RDY; + cmd_done = NDSR_CS0_CMDD; + } else { + ready = NDSR_RDY; + cmd_done = NDSR_CS1_CMDD; + } + + /* TODO - find out why we need the delay during write operation. */ + ndelay(1); + + status = nand_readl(info, NDSR); + + if (status & NDSR_UNCORERR) + info->retcode = ERR_UNCORERR; + if (status & NDSR_CORERR) { + info->retcode = ERR_CORERR; + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370 && + info->ecc_bch) + info->ecc_err_cnt = NDSR_ERR_CNT(status); + else + info->ecc_err_cnt = 1; + + /* + * Each chunk composing a page is corrected independently, + * and we need to store maximum number of corrected bitflips + * to return it to the MTD layer in ecc.read_page(). + */ + info->max_bitflips = max_t(unsigned int, + info->max_bitflips, + info->ecc_err_cnt); + } + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { + info->state = (status & NDSR_RDDREQ) ? + STATE_PIO_READING : STATE_PIO_WRITING; + /* Call the IRQ thread in U-Boot directly */ + pxa3xx_nand_irq_thread(info); + return 0; + } + if (status & cmd_done) { + info->state = STATE_CMD_DONE; + is_completed = 1; + } + if (status & ready) { + info->state = STATE_READY; + is_ready = 1; + } + + /* + * Clear all status bit before issuing the next command, which + * can and will alter the status bits and will deserve a new + * interrupt on its own. This lets the controller exit the IRQ + */ + nand_writel(info, NDSR, status); + + if (status & NDSR_WRCMDREQ) { + status &= ~NDSR_WRCMDREQ; + info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDCB0, info->ndcb3); + } + + if (is_completed) + info->cmd_complete = 1; + if (is_ready) + info->dev_ready = 1; + + return ret; +} + +static inline int is_buf_blank(uint8_t *buf, size_t len) +{ + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; +} + +static void set_command_address(struct pxa3xx_nand_info *info, + unsigned int page_size, uint16_t column, int page_addr) +{ + /* small page addr setting */ + if (page_size < info->chunk_size) { + info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) + | (column & 0xFF); + + info->ndcb2 = 0; + } else { + info->ndcb1 = ((page_addr & 0xFFFF) << 16) + | (column & 0xFFFF); + + if (page_addr & 0xFF0000) + info->ndcb2 = (page_addr & 0xFF0000) >> 16; + else + info->ndcb2 = 0; + } +} + +static void prepare_start_command(struct pxa3xx_nand_info *info, int command) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + + /* reset data and oob column point to handle data */ + info->buf_start = 0; + info->buf_count = 0; + info->data_buff_pos = 0; + info->oob_buff_pos = 0; + info->step_chunk_size = 0; + info->step_spare_size = 0; + info->cur_chunk = 0; + info->use_ecc = 0; + info->use_spare = 1; + info->retcode = ERR_NONE; + info->ecc_err_cnt = 0; + info->ndcb3 = 0; + info->need_wait = 0; + + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + case NAND_CMD_PAGEPROG: + info->use_ecc = 1; + break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; + default: + info->ndcb1 = 0; + info->ndcb2 = 0; + break; + } + + /* + * If we are about to issue a read command, or about to set + * the write address, then clean the data buffer. + */ + if (command == NAND_CMD_READ0 || + command == NAND_CMD_READOOB || + command == NAND_CMD_SEQIN) { + info->buf_count = mtd->writesize + mtd->oobsize; + memset(info->data_buff, 0xFF, info->buf_count); + } +} + +static int prepare_set_command(struct pxa3xx_nand_info *info, int command, + int ext_cmd_type, uint16_t column, int page_addr) +{ + int addr_cycle, exec_cmd; + struct pxa3xx_nand_host *host; + struct mtd_info *mtd; + + host = info->host[info->cs]; + mtd = nand_to_mtd(&host->chip); + addr_cycle = 0; + exec_cmd = 1; + + if (info->cs != 0) + info->ndcb0 = NDCB0_CSEL; + else + info->ndcb0 = 0; + + if (command == NAND_CMD_SEQIN) + exec_cmd = 0; + + addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles + + host->col_addr_cycles); + + switch (command) { + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + info->buf_start = column; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; + + if (command == NAND_CMD_READOOB) + info->buf_start += mtd->writesize; + + if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + + /* + * Multiple page read needs an 'extended command type' field, + * which is either naked-read or last-read according to the + * state. + */ + if (mtd->writesize == info->chunk_size) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); + } else if (mtd->writesize > info->chunk_size) { + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->step_chunk_size + + info->step_spare_size; + } + + set_command_address(info, mtd->writesize, column, page_addr); + break; + + case NAND_CMD_SEQIN: + + info->buf_start = column; + set_command_address(info, mtd->writesize, 0, page_addr); + + /* + * Multiple page programming needs to execute the initial + * SEQIN command that sets the page address. + */ + if (mtd->writesize > info->chunk_size) { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | addr_cycle + | command; + exec_cmd = 1; + } + break; + + case NAND_CMD_PAGEPROG: + if (is_buf_blank(info->data_buff, + (mtd->writesize + mtd->oobsize))) { + exec_cmd = 0; + break; + } + + if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + + /* Second command setting for large pages */ + if (mtd->writesize > info->chunk_size) { + /* + * Multiple page write uses the 'extended command' + * field. This can be used to issue a command dispatch + * or a naked-write depending on the current stage. + */ + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_LEN_OVRD + | NDCB0_EXT_CMD_TYPE(ext_cmd_type); + info->ndcb3 = info->step_chunk_size + + info->step_spare_size; + + /* + * This is the command dispatch that completes a chunked + * page program operation. + */ + if (info->cur_chunk == info->ntotalchunks) { + info->ndcb0 = NDCB0_CMD_TYPE(0x1) + | NDCB0_EXT_CMD_TYPE(ext_cmd_type) + | command; + info->ndcb1 = 0; + info->ndcb2 = 0; + info->ndcb3 = 0; + } + } else { + info->ndcb0 |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN + | addr_cycle; + } + break; + + case NAND_CMD_PARAM: + info->buf_count = INIT_BUFFER_SIZE; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD + | command; + info->ndcb1 = (column & 0xFF); + info->ndcb3 = INIT_BUFFER_SIZE; + info->step_chunk_size = INIT_BUFFER_SIZE; + break; + + case NAND_CMD_READID: + info->buf_count = READ_ID_BYTES; + info->ndcb0 |= NDCB0_CMD_TYPE(3) + | NDCB0_ADDR_CYC(1) + | command; + info->ndcb1 = (column & 0xFF); + + info->step_chunk_size = 8; + break; + case NAND_CMD_STATUS: + info->buf_count = 1; + info->ndcb0 |= NDCB0_CMD_TYPE(4) + | NDCB0_ADDR_CYC(1) + | command; + + info->step_chunk_size = 8; + break; + + case NAND_CMD_ERASE1: + info->ndcb0 |= NDCB0_CMD_TYPE(2) + | NDCB0_AUTO_RS + | NDCB0_ADDR_CYC(3) + | NDCB0_DBC + | (NAND_CMD_ERASE2 << 8) + | NAND_CMD_ERASE1; + info->ndcb1 = page_addr; + info->ndcb2 = 0; + + break; + case NAND_CMD_RESET: + info->ndcb0 |= NDCB0_CMD_TYPE(5) + | command; + + break; + + case NAND_CMD_ERASE2: + exec_cmd = 0; + break; + + default: + exec_cmd = 0; + dev_err(&info->pdev->dev, "non-supported command %x\n", + command); + break; + } + + return exec_cmd; +} + +static void nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd; + + /* + * if this is a x16 device ,then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + prepare_start_command(info, command); + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, 0, column, page_addr); + + if (exec_cmd) { + u32 ts; + + info->cmd_complete = 0; + info->dev_ready = 0; + info->need_wait = 1; + pxa3xx_nand_start(info); + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->cmd_complete) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Wait timeout!!!\n"); + return; + } + } + } + info->state = STATE_IDLE; +} + +static void nand_cmdfunc_extended(struct mtd_info *mtd, + const unsigned command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int exec_cmd, ext_cmd_type; + + /* + * if this is a x16 device then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (info->reg_ndcr & NDCR_DWIDTH_M) + column /= 2; + + /* + * There may be different NAND chip hooked to + * different chip select, so check whether + * chip select has been changed, if yes, reset the timing + */ + if (info->cs != host->cs) { + info->cs = host->cs; + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); + } + + /* Select the extended command for the first command */ + switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + ext_cmd_type = EXT_CMD_TYPE_MONO; + break; + case NAND_CMD_SEQIN: + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + break; + case NAND_CMD_PAGEPROG: + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + break; + default: + ext_cmd_type = 0; + break; + } + + prepare_start_command(info, command); + + /* + * Prepare the "is ready" completion before starting a command + * transaction sequence. If the command is not executed the + * completion will be completed, see below. + * + * We can do that inside the loop because the command variable + * is invariant and thus so is the exec_cmd. + */ + info->need_wait = 1; + info->dev_ready = 0; + + do { + u32 ts; + + info->state = STATE_PREPARED; + exec_cmd = prepare_set_command(info, command, ext_cmd_type, + column, page_addr); + if (!exec_cmd) { + info->need_wait = 0; + info->dev_ready = 1; + break; + } + + info->cmd_complete = 0; + pxa3xx_nand_start(info); + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->cmd_complete) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Wait timeout!!!\n"); + return; + } + } + + /* Only a few commands need several steps */ + if (command != NAND_CMD_PAGEPROG && + command != NAND_CMD_READ0 && + command != NAND_CMD_READOOB) + break; + + info->cur_chunk++; + + /* Check if the sequence is complete */ + if (info->cur_chunk == info->ntotalchunks && + command != NAND_CMD_PAGEPROG) + break; + + /* + * After a splitted program command sequence has issued + * the command dispatch, the command sequence is complete. + */ + if (info->cur_chunk == (info->ntotalchunks + 1) && + command == NAND_CMD_PAGEPROG && + ext_cmd_type == EXT_CMD_TYPE_DISPATCH) + break; + + if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { + /* Last read: issue a 'last naked read' */ + if (info->cur_chunk == info->ntotalchunks - 1) + ext_cmd_type = EXT_CMD_TYPE_LAST_RW; + else + ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; + + /* + * If a splitted program command has no more data to transfer, + * the command dispatch must be issued to complete. + */ + } else if (command == NAND_CMD_PAGEPROG && + info->cur_chunk == info->ntotalchunks) { + ext_cmd_type = EXT_CMD_TYPE_DISPATCH; + } + } while (1); + + info->state = STATE_IDLE; +} + +static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + + return 0; +} + +static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) +{ + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (info->retcode == ERR_CORERR && info->use_ecc) { + mtd->ecc_stats.corrected += info->ecc_err_cnt; + + } else if (info->retcode == ERR_UNCORERR) { + /* + * for blank page (all 0xff), HW will calculate its ECC as + * 0, which is different from the ECC information within + * OOB, ignore such uncorrectable errors + */ + if (is_buf_blank(buf, mtd->writesize)) + info->retcode = ERR_NONE; + else + mtd->ecc_stats.failed++; + } + + return info->max_bitflips; +} + +static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + char retval = 0xFF; + + if (info->buf_start < info->buf_count) + /* Has just send a new command? */ + retval = info->data_buff[info->buf_start++]; + + return retval; +} + +static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + u16 retval = 0xFFFF; + + if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); + info->buf_start += 2; + } + return retval; +} + +static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(buf, info->data_buff + info->buf_start, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int real_len = min_t(size_t, len, info->buf_count - info->buf_start); + + memcpy(info->data_buff + info->buf_start, buf, real_len); + info->buf_start += real_len; +} + +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) +{ + return; +} + +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + + if (info->need_wait) { + u32 ts; + + info->need_wait = 0; + + ts = get_timer(0); + while (1) { + u32 status; + + status = nand_readl(info, NDSR); + if (status) + pxa3xx_nand_irq(info); + + if (info->dev_ready) + break; + + if (get_timer(ts) > CHIP_DELAY_TIMEOUT) { + dev_err(&info->pdev->dev, "Ready timeout!!!\n"); + return NAND_STATUS_FAIL; + } + } + } + + /* pxa3xx_nand_send_command has waited for command complete */ + if (this->state == FL_WRITING || this->state == FL_ERASING) { + if (info->retcode == ERR_NONE) + return 0; + else + return NAND_STATUS_FAIL; + } + + return NAND_STATUS_READY; +} + +static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata = info->pdata; + + /* Configure default flash values */ + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); + info->reg_ndcr |= NDCR_SPARE_EN; + + return 0; +} + +static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_host *host = info->host[info->cs]; + struct mtd_info *mtd = nand_to_mtd(&info->host[info->cs]->chip); + struct nand_chip *chip = mtd_to_nand(mtd); + + info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; + info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; + info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; +} + +static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata = info->pdata; + uint32_t ndcr = nand_readl(info, NDCR); + + /* Set an initial chunk size */ + info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; + info->reg_ndcr = ndcr & + ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); +} + +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; + return 0; +} + +static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) +{ + struct pxa3xx_nand_info *info = host->info_data; + struct pxa3xx_nand_platform_data *pdata = info->pdata; + struct mtd_info *mtd; + struct nand_chip *chip; + const struct nand_sdr_timings *timings; + int ret; + + mtd = nand_to_mtd(&info->host[info->cs]->chip); + chip = mtd_to_nand(mtd); + + /* configure default flash values */ + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); + info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + + /* use the common timing to make a try */ + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + pxa3xx_nand_set_sdr_timing(host, timings); + + chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); + ret = chip->waitfunc(mtd, chip); + if (ret & NAND_STATUS_FAIL) + return -ENODEV; + + return 0; +} + +static int pxa_ecc_init(struct pxa3xx_nand_info *info, + struct nand_ecc_ctrl *ecc, + int strength, int ecc_stepsize, int page_size) +{ + if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 2048; + info->spare_size = 40; + info->ecc_size = 24; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + } else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 512; + info->spare_size = 8; + info->ecc_size = 8; + ecc->mode = NAND_ECC_HW; + ecc->size = 512; + ecc->strength = 1; + + /* + * Required ECC: 4-bit correction per 512 bytes + * Select: 16-bit correction per 2048 bytes + */ + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { + info->ecc_bch = 1; + info->nfullchunks = 1; + info->ntotalchunks = 1; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_2KB_bch4bit; + ecc->strength = 16; + + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->nfullchunks = 2; + info->ntotalchunks = 2; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch4bit; + ecc->strength = 16; + + } else if (strength == 4 && ecc_stepsize == 512 && page_size == 8192) { + info->ecc_bch = 1; + info->nfullchunks = 4; + info->ntotalchunks = 4; + info->chunk_size = 2048; + info->spare_size = 32; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_8KB_bch4bit; + ecc->strength = 16; + + /* + * Required ECC: 8-bit correction per 512 bytes + * Select: 16-bit correction per 1024 bytes + */ + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 2048) { + info->ecc_bch = 1; + info->nfullchunks = 1; + info->ntotalchunks = 2; + info->chunk_size = 1024; + info->spare_size = 0; + info->last_chunk_size = 1024; + info->last_spare_size = 64; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_2KB_bch8bit; + ecc->strength = 16; + + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { + info->ecc_bch = 1; + info->nfullchunks = 4; + info->ntotalchunks = 5; + info->chunk_size = 1024; + info->spare_size = 0; + info->last_chunk_size = 0; + info->last_spare_size = 64; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_4KB_bch8bit; + ecc->strength = 16; + + } else if (strength == 8 && ecc_stepsize == 512 && page_size == 8192) { + info->ecc_bch = 1; + info->nfullchunks = 8; + info->ntotalchunks = 9; + info->chunk_size = 1024; + info->spare_size = 0; + info->last_chunk_size = 0; + info->last_spare_size = 160; + info->ecc_size = 32; + ecc->mode = NAND_ECC_HW; + ecc->size = info->chunk_size; + ecc->layout = &ecc_layout_8KB_bch8bit; + ecc->strength = 16; + + } else { + dev_err(&info->pdev->dev, + "ECC strength %d at page size %d is not supported\n", + strength, page_size); + return -ENODEV; + } + + return 0; +} + +static int pxa3xx_nand_scan(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + struct pxa3xx_nand_platform_data *pdata = info->pdata; + int ret; + uint16_t ecc_strength, ecc_step; + + if (pdata->keep_config) { + pxa3xx_nand_detect_config(info); + } else { + ret = pxa3xx_nand_config_ident(info); + if (ret) + return ret; + ret = pxa3xx_nand_sensing(host); + if (ret) { + dev_info(&info->pdev->dev, + "There is no chip on cs %d!\n", + info->cs); + return ret; + } + } + + /* Device detection must be done with ECC disabled */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDECCCTRL, 0x0); + + if (nand_scan_ident(mtd, 1, NULL)) + return -ENODEV; + + if (!pdata->keep_config) { + ret = pxa3xx_nand_init_timings(host); + if (ret) { + dev_err(&info->pdev->dev, + "Failed to set timings: %d\n", ret); + return ret; + } + } + +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + /* + * We'll use a bad block table stored in-flash and don't + * allow writing the bad block marker to the flash. + */ + chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB_BBM; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; +#endif + + if (pdata->ecc_strength && pdata->ecc_step_size) { + ecc_strength = pdata->ecc_strength; + ecc_step = pdata->ecc_step_size; + } else { + ecc_strength = chip->ecc_strength_ds; + ecc_step = chip->ecc_step_ds; + } + + /* Set default ECC strength requirements on non-ONFI devices */ + if (ecc_strength < 1 && ecc_step < 1) { + ecc_strength = 1; + ecc_step = 512; + } + + ret = pxa_ecc_init(info, &chip->ecc, ecc_strength, + ecc_step, mtd->writesize); + if (ret) + return ret; + + /* + * If the page size is bigger than the FIFO size, let's check + * we are given the right variant and then switch to the extended + * (aka split) command handling, + */ + if (mtd->writesize > info->chunk_size) { + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) { + chip->cmdfunc = nand_cmdfunc_extended; + } else { + dev_err(&info->pdev->dev, + "unsupported page size on this variant\n"); + return -ENODEV; + } + } + + /* calculate addressing information */ + if (mtd->writesize >= 2048) + host->col_addr_cycles = 2; + else + host->col_addr_cycles = 1; + + /* release the initial buffer */ + kfree(info->data_buff); + + /* allocate the real data + oob buffer */ + info->buf_size = mtd->writesize + mtd->oobsize; + ret = pxa3xx_nand_init_buff(info); + if (ret) + return ret; + info->oob_buff = info->data_buff + mtd->writesize; + + if ((mtd->size >> chip->page_shift) > 65536) + host->row_addr_cycles = 3; + else + host->row_addr_cycles = 2; + + if (!pdata->keep_config) + pxa3xx_nand_config_tail(info); + + return nand_scan_tail(mtd); +} + +static int alloc_nand_resource(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + struct pxa3xx_nand_host *host; + struct nand_chip *chip = NULL; + struct mtd_info *mtd; + int ret, cs; + + pdata = info->pdata; + if (pdata->num_cs <= 0) + return -ENODEV; + + info->variant = pxa3xx_nand_get_variant(); + for (cs = 0; cs < pdata->num_cs; cs++) { + chip = (struct nand_chip *) + ((u8 *)&info[1] + sizeof(*host) * cs); + mtd = nand_to_mtd(chip); + host = (struct pxa3xx_nand_host *)chip; + info->host[cs] = host; + host->cs = cs; + host->info_data = info; + mtd->owner = THIS_MODULE; + + nand_set_controller_data(chip, host); + chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; + chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; + chip->controller = &info->controller; + chip->waitfunc = pxa3xx_nand_waitfunc; + chip->select_chip = pxa3xx_nand_select_chip; + chip->read_word = pxa3xx_nand_read_word; + chip->read_byte = pxa3xx_nand_read_byte; + chip->read_buf = pxa3xx_nand_read_buf; + chip->write_buf = pxa3xx_nand_write_buf; + chip->options |= NAND_NO_SUBPAGE_WRITE; + chip->cmdfunc = nand_cmdfunc; + } + + /* Allocate a buffer to allow flash detection */ + info->buf_size = INIT_BUFFER_SIZE; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) { + ret = -ENOMEM; + goto fail_disable_clk; + } + + /* initialize all interrupts to be disabled */ + disable_int(info, NDSR_MASK); + + return 0; + + kfree(info->data_buff); +fail_disable_clk: + return ret; +} + +static int pxa3xx_nand_probe_dt(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + const void *blob = gd->fdt_blob; + int node = -1; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Get address decoding nodes from the FDT blob */ + do { + node = fdt_node_offset_by_compatible(blob, node, + "marvell,mvebu-pxa3xx-nand"); + if (node < 0) + break; + + /* Bypass disabeld nodes */ + if (!fdtdec_get_is_enabled(blob, node)) + continue; + + /* Get the first enabled NAND controler base address */ + info->mmio_base = + (void __iomem *)fdtdec_get_addr_size_auto_noparent( + blob, node, "reg", 0, NULL, true); + + pdata->num_cs = fdtdec_get_int(blob, node, "num-cs", 1); + if (pdata->num_cs != 1) { + pr_err("pxa3xx driver supports single CS only\n"); + break; + } + + if (fdtdec_get_bool(blob, node, "nand-enable-arbiter")) + pdata->enable_arbiter = 1; + + if (fdtdec_get_bool(blob, node, "nand-keep-config")) + pdata->keep_config = 1; + + /* + * ECC parameters. + * If these are not set, they will be selected according + * to the detected flash type. + */ + /* ECC strength */ + pdata->ecc_strength = fdtdec_get_int(blob, node, + "nand-ecc-strength", 0); + + /* ECC step size */ + pdata->ecc_step_size = fdtdec_get_int(blob, node, + "nand-ecc-step-size", 0); + + info->pdata = pdata; + + /* Currently support only a single NAND controller */ + return 0; + + } while (node >= 0); + + return -EINVAL; +} + +static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata; + int ret, cs, probe_success; + + ret = pxa3xx_nand_probe_dt(info); + if (ret) + return ret; + + pdata = info->pdata; + + ret = alloc_nand_resource(info); + if (ret) { + dev_err(&pdev->dev, "alloc nand resource failed\n"); + return ret; + } + + probe_success = 0; + for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip); + + /* + * The mtd name matches the one used in 'mtdparts' kernel + * parameter. This name cannot be changed or otherwise + * user's mtd partitions configuration would get broken. + */ + mtd->name = "pxa3xx_nand-0"; + info->cs = cs; + ret = pxa3xx_nand_scan(mtd); + if (ret) { + dev_info(&pdev->dev, "failed to scan nand at cs %d\n", + cs); + continue; + } + + if (nand_register(cs, mtd)) + continue; + + probe_success = 1; + } + + if (!probe_success) + return -ENODEV; + + return 0; +} + +/* + * Main initialization routine + */ +void board_nand_init(void) +{ + struct pxa3xx_nand_info *info; + struct pxa3xx_nand_host *host; + int ret; + + info = kzalloc(sizeof(*info) + + sizeof(*host) * CONFIG_SYS_MAX_NAND_DEVICE, + GFP_KERNEL); + if (!info) + return; + + ret = pxa3xx_nand_probe(info); + if (ret) + return; +} diff --git a/drivers/mtd/nand/raw/pxa3xx_nand.h b/drivers/mtd/nand/raw/pxa3xx_nand.h new file mode 100644 index 0000000000..8f24ae6d18 --- /dev/null +++ b/drivers/mtd/nand/raw/pxa3xx_nand.h @@ -0,0 +1,64 @@ +#ifndef __ASM_ARCH_PXA3XX_NAND_H +#define __ASM_ARCH_PXA3XX_NAND_H + +#include +#include + +struct pxa3xx_nand_timing { + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_flash { + uint32_t chip_id; + unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */ + unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */ + struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ +}; + +/* + * Current pxa3xx_nand controller has two chip select which + * both be workable. + * + * Notice should be taken that: + * When you want to use this feature, you should not enable the + * keep configuration feature, for two chip select could be + * attached with different nand chip. The different page size + * and timing requirement make the keep configuration impossible. + */ + +/* The max num of chip select current support */ +#define NUM_CHIP_SELECT (2) +struct pxa3xx_nand_platform_data { + /* the data flash bus is shared between the Static Memory + * Controller and the Data Flash Controller, the arbiter + * controls the ownership of the bus + */ + int enable_arbiter; + + /* allow platform code to keep OBM/bootloader defined NFC config */ + int keep_config; + + /* indicate how many chip selects will be used */ + int num_cs; + + /* use an flash-based bad block table */ + bool flash_bbt; + + /* requested ECC strength and ECC step size */ + int ecc_strength, ecc_step_size; + + const struct mtd_partition *parts[NUM_CHIP_SELECT]; + unsigned int nr_parts[NUM_CHIP_SELECT]; + + const struct pxa3xx_nand_flash *flash; + size_t num_flash; +}; +#endif /* __ASM_ARCH_PXA3XX_NAND_H */ diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c new file mode 100644 index 0000000000..3ccb168d13 --- /dev/null +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -0,0 +1,1850 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Boris BREZILLON + * Copyright (C) 2015 Roy Spliet + * + * Derived from: + * https://github.com/yuq/sunxi-nfc-mtd + * Copyright (C) 2013 Qiang Yu + * + * https://github.com/hno/Allwinner-Info + * Copyright (C) 2013 Henrik Nordström + * + * Copyright (C) 2013 Dmitriy B. + * Copyright (C) 2013 Sergey Lapin + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define NFC_REG_CTL 0x0000 +#define NFC_REG_ST 0x0004 +#define NFC_REG_INT 0x0008 +#define NFC_REG_TIMING_CTL 0x000C +#define NFC_REG_TIMING_CFG 0x0010 +#define NFC_REG_ADDR_LOW 0x0014 +#define NFC_REG_ADDR_HIGH 0x0018 +#define NFC_REG_SECTOR_NUM 0x001C +#define NFC_REG_CNT 0x0020 +#define NFC_REG_CMD 0x0024 +#define NFC_REG_RCMD_SET 0x0028 +#define NFC_REG_WCMD_SET 0x002C +#define NFC_REG_IO_DATA 0x0030 +#define NFC_REG_ECC_CTL 0x0034 +#define NFC_REG_ECC_ST 0x0038 +#define NFC_REG_DEBUG 0x003C +#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) +#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) +#define NFC_REG_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID 0x00A4 +#define NFC_RAM0_BASE 0x0400 +#define NFC_RAM1_BASE 0x0800 + +/* define bit use in NFC_CTL */ +#define NFC_EN BIT(0) +#define NFC_RESET BIT(1) +#define NFC_BUS_WIDTH_MSK BIT(2) +#define NFC_BUS_WIDTH_8 (0 << 2) +#define NFC_BUS_WIDTH_16 (1 << 2) +#define NFC_RB_SEL_MSK BIT(3) +#define NFC_RB_SEL(x) ((x) << 3) +#define NFC_CE_SEL_MSK (0x7 << 24) +#define NFC_CE_SEL(x) ((x) << 24) +#define NFC_CE_CTL BIT(6) +#define NFC_PAGE_SHIFT_MSK (0xf << 8) +#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) +#define NFC_SAM BIT(12) +#define NFC_RAM_METHOD BIT(14) +#define NFC_DEBUG_CTL BIT(31) + +/* define bit use in NFC_ST */ +#define NFC_RB_B2R BIT(0) +#define NFC_CMD_INT_FLAG BIT(1) +#define NFC_DMA_INT_FLAG BIT(2) +#define NFC_CMD_FIFO_STATUS BIT(3) +#define NFC_STA BIT(4) +#define NFC_NATCH_INT_FLAG BIT(5) +#define NFC_RB_STATE(x) BIT(x + 8) + +/* define bit use in NFC_INT */ +#define NFC_B2R_INT_ENABLE BIT(0) +#define NFC_CMD_INT_ENABLE BIT(1) +#define NFC_DMA_INT_ENABLE BIT(2) +#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ + NFC_CMD_INT_ENABLE | \ + NFC_DMA_INT_ENABLE) + +/* define bit use in NFC_TIMING_CTL */ +#define NFC_TIMING_CTL_EDO BIT(8) + +/* define NFC_TIMING_CFG register layout */ +#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ + (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ + (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ + (((tCAD) & 0x7) << 8)) + +/* define bit use in NFC_CMD */ +#define NFC_CMD_LOW_BYTE_MSK 0xff +#define NFC_CMD_HIGH_BYTE_MSK (0xff << 8) +#define NFC_CMD(x) (x) +#define NFC_ADR_NUM_MSK (0x7 << 16) +#define NFC_ADR_NUM(x) (((x) - 1) << 16) +#define NFC_SEND_ADR BIT(19) +#define NFC_ACCESS_DIR BIT(20) +#define NFC_DATA_TRANS BIT(21) +#define NFC_SEND_CMD1 BIT(22) +#define NFC_WAIT_FLAG BIT(23) +#define NFC_SEND_CMD2 BIT(24) +#define NFC_SEQ BIT(25) +#define NFC_DATA_SWAP_METHOD BIT(26) +#define NFC_ROW_AUTO_INC BIT(27) +#define NFC_SEND_CMD3 BIT(28) +#define NFC_SEND_CMD4 BIT(29) +#define NFC_CMD_TYPE_MSK (0x3 << 30) +#define NFC_NORMAL_OP (0 << 30) +#define NFC_ECC_OP (1 << 30) +#define NFC_PAGE_OP (2 << 30) + +/* define bit use in NFC_RCMD_SET */ +#define NFC_READ_CMD_MSK 0xff +#define NFC_RND_READ_CMD0_MSK (0xff << 8) +#define NFC_RND_READ_CMD1_MSK (0xff << 16) + +/* define bit use in NFC_WCMD_SET */ +#define NFC_PROGRAM_CMD_MSK 0xff +#define NFC_RND_WRITE_CMD_MSK (0xff << 8) +#define NFC_READ_CMD0_MSK (0xff << 16) +#define NFC_READ_CMD1_MSK (0xff << 24) + +/* define bit use in NFC_ECC_CTL */ +#define NFC_ECC_EN BIT(0) +#define NFC_ECC_PIPELINE BIT(3) +#define NFC_ECC_EXCEPTION BIT(4) +#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) +#define NFC_ECC_BLOCK_512 (1 << 5) +#define NFC_RANDOM_EN BIT(9) +#define NFC_RANDOM_DIRECTION BIT(10) +#define NFC_ECC_MODE_MSK (0xf << 12) +#define NFC_ECC_MODE(x) ((x) << 12) +#define NFC_RANDOM_SEED_MSK (0x7fff << 16) +#define NFC_RANDOM_SEED(x) ((x) << 16) + +/* define bit use in NFC_ECC_ST */ +#define NFC_ECC_ERR(x) BIT(x) +#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) +#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) + +#define NFC_DEFAULT_TIMEOUT_MS 1000 + +#define NFC_SRAM_SIZE 1024 + +#define NFC_MAX_CS 7 + +/* + * Ready/Busy detection type: describes the Ready/Busy detection modes + * + * @RB_NONE: no external detection available, rely on STATUS command + * and software timeouts + * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy + * pin of the NAND flash chip must be connected to one of the + * native NAND R/B pins (those which can be muxed to the NAND + * Controller) + * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy + * pin of the NAND flash chip must be connected to a GPIO capable + * pin. + */ +enum sunxi_nand_rb_type { + RB_NONE, + RB_NATIVE, + RB_GPIO, +}; + +/* + * Ready/Busy structure: stores information related to Ready/Busy detection + * + * @type: the Ready/Busy detection mode + * @info: information related to the R/B detection mode. Either a gpio + * id or a native R/B id (those supported by the NAND controller). + */ +struct sunxi_nand_rb { + enum sunxi_nand_rb_type type; + union { + struct gpio_desc gpio; + int nativeid; + } info; +}; + +/* + * Chip Select structure: stores information related to NAND Chip Select + * + * @cs: the NAND CS id used to communicate with a NAND Chip + * @rb: the Ready/Busy description + */ +struct sunxi_nand_chip_sel { + u8 cs; + struct sunxi_nand_rb rb; +}; + +/* + * sunxi HW ECC infos: stores information related to HW ECC support + * + * @mode: the sunxi ECC mode field deduced from ECC requirements + * @layout: the OOB layout depending on the ECC requirements and the + * selected ECC mode + */ +struct sunxi_nand_hw_ecc { + int mode; + struct nand_ecclayout layout; +}; + +/* + * NAND chip structure: stores NAND chip device related information + * + * @node: used to store NAND chips into a list + * @nand: base NAND chip structure + * @mtd: base MTD structure + * @clk_rate: clk_rate required for this NAND chip + * @timing_cfg TIMING_CFG register value for this NAND chip + * @selected: current active CS + * @nsels: number of CS lines required by the NAND chip + * @sels: array of CS lines descriptions + */ +struct sunxi_nand_chip { + struct list_head node; + struct nand_chip nand; + unsigned long clk_rate; + u32 timing_cfg; + u32 timing_ctl; + int selected; + int addr_cycles; + u32 addr[2]; + int cmd_cycles; + u8 cmd[2]; + int nsels; + struct sunxi_nand_chip_sel sels[0]; +}; + +static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) +{ + return container_of(nand, struct sunxi_nand_chip, nand); +} + +/* + * NAND Controller structure: stores sunxi NAND controller information + * + * @controller: base controller structure + * @dev: parent device (used to print error messages) + * @regs: NAND controller registers + * @ahb_clk: NAND Controller AHB clock + * @mod_clk: NAND Controller mod clock + * @assigned_cs: bitmask describing already assigned CS lines + * @clk_rate: NAND controller current clock rate + * @chips: a list containing all the NAND chips attached to + * this NAND controller + * @complete: a completion object used to wait for NAND + * controller events + */ +struct sunxi_nfc { + struct nand_hw_control controller; + struct device *dev; + void __iomem *regs; + struct clk *ahb_clk; + struct clk *mod_clk; + unsigned long assigned_cs; + unsigned long clk_rate; + struct list_head chips; +}; + +static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct sunxi_nfc, controller); +} + +static void sunxi_nfc_set_clk_rate(unsigned long hz) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + int div_m, div_n; + + div_m = (clock_get_pll6() + hz - 1) / hz; + for (div_n = 0; div_n < 3 && div_m > 16; div_n++) { + if (div_m % 2) + div_m++; + div_m >>= 1; + } + if (div_m > 16) + div_m = 16; + + /* config mod clock */ + writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 | + CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m), + &ccm->nand0_clk_cfg); + + /* gate on nand clock */ + setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0)); +#ifdef CONFIG_MACH_SUN9I + setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); +#else + setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); +#endif +} + +static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, + unsigned int timeout_ms) +{ + unsigned int timeout_ticks; + u32 time_start, status; + int ret = -ETIMEDOUT; + + if (!timeout_ms) + timeout_ms = NFC_DEFAULT_TIMEOUT_MS; + + timeout_ticks = (timeout_ms * CONFIG_SYS_HZ) / 1000; + + time_start = get_timer(0); + + do { + status = readl(nfc->regs + NFC_REG_ST); + if ((status & flags) == flags) { + ret = 0; + break; + } + + udelay(1); + } while (get_timer(time_start) < timeout_ticks); + + writel(status & flags, nfc->regs + NFC_REG_ST); + + return ret; +} + +static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) +{ + unsigned long timeout = (CONFIG_SYS_HZ * + NFC_DEFAULT_TIMEOUT_MS) / 1000; + u32 time_start; + + time_start = get_timer(0); + do { + if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS)) + return 0; + } while (get_timer(time_start) < timeout); + + dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); + return -ETIMEDOUT; +} + +static int sunxi_nfc_rst(struct sunxi_nfc *nfc) +{ + unsigned long timeout = (CONFIG_SYS_HZ * + NFC_DEFAULT_TIMEOUT_MS) / 1000; + u32 time_start; + + writel(0, nfc->regs + NFC_REG_ECC_CTL); + writel(NFC_RESET, nfc->regs + NFC_REG_CTL); + + time_start = get_timer(0); + do { + if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)) + return 0; + } while (get_timer(time_start) < timeout); + + dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); + return -ETIMEDOUT; +} + +static int sunxi_nfc_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_rb *rb; + unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20); + int ret; + + if (sunxi_nand->selected < 0) + return 0; + + rb = &sunxi_nand->sels[sunxi_nand->selected].rb; + + switch (rb->type) { + case RB_NATIVE: + ret = !!(readl(nfc->regs + NFC_REG_ST) & + NFC_RB_STATE(rb->info.nativeid)); + if (ret) + break; + + sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo); + ret = !!(readl(nfc->regs + NFC_REG_ST) & + NFC_RB_STATE(rb->info.nativeid)); + break; + case RB_GPIO: + ret = dm_gpio_get_value(&rb->info.gpio); + break; + case RB_NONE: + default: + ret = 0; + dev_err(nfc->dev, "cannot check R/B NAND status!\n"); + break; + } + + return ret; +} + +static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_chip_sel *sel; + u32 ctl; + + if (chip > 0 && chip >= sunxi_nand->nsels) + return; + + if (chip == sunxi_nand->selected) + return; + + ctl = readl(nfc->regs + NFC_REG_CTL) & + ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); + + if (chip >= 0) { + sel = &sunxi_nand->sels[chip]; + + ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | + NFC_PAGE_SHIFT(nand->page_shift - 10); + if (sel->rb.type == RB_NONE) { + nand->dev_ready = NULL; + } else { + nand->dev_ready = sunxi_nfc_dev_ready; + if (sel->rb.type == RB_NATIVE) + ctl |= NFC_RB_SEL(sel->rb.info.nativeid); + } + + writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); + + if (nfc->clk_rate != sunxi_nand->clk_rate) { + sunxi_nfc_set_clk_rate(sunxi_nand->clk_rate); + nfc->clk_rate = sunxi_nand->clk_rate; + } + } + + writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); + writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); + writel(ctl, nfc->regs + NFC_REG_CTL); + + sunxi_nand->selected = chip; +} + +static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + cnt = min(len - offs, NFC_SRAM_SIZE); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + break; + + writel(cnt, nfc->regs + NFC_REG_CNT); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; + writel(tmp, nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ret) + break; + + if (buf) + memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, + cnt); + offs += cnt; + } +} + +static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + cnt = min(len - offs, NFC_SRAM_SIZE); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + break; + + writel(cnt, nfc->regs + NFC_REG_CNT); + memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | + NFC_ACCESS_DIR; + writel(tmp, nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ret) + break; + + offs += cnt; + } +} + +static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) +{ + uint8_t ret; + + sunxi_nfc_read_buf(mtd, &ret, 1); + + return ret; +} + +static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int ret; + u32 tmp; + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return; + + if (ctrl & NAND_CTRL_CHANGE) { + tmp = readl(nfc->regs + NFC_REG_CTL); + if (ctrl & NAND_NCE) + tmp |= NFC_CE_CTL; + else + tmp &= ~NFC_CE_CTL; + writel(tmp, nfc->regs + NFC_REG_CTL); + } + + if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && + !(ctrl & (NAND_CLE | NAND_ALE))) { + u32 cmd = 0; + + if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) + return; + + if (sunxi_nand->cmd_cycles--) + cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; + + if (sunxi_nand->cmd_cycles--) { + cmd |= NFC_SEND_CMD2; + writel(sunxi_nand->cmd[1], + nfc->regs + NFC_REG_RCMD_SET); + } + + sunxi_nand->cmd_cycles = 0; + + if (sunxi_nand->addr_cycles) { + cmd |= NFC_SEND_ADR | + NFC_ADR_NUM(sunxi_nand->addr_cycles); + writel(sunxi_nand->addr[0], + nfc->regs + NFC_REG_ADDR_LOW); + } + + if (sunxi_nand->addr_cycles > 4) + writel(sunxi_nand->addr[1], + nfc->regs + NFC_REG_ADDR_HIGH); + + writel(cmd, nfc->regs + NFC_REG_CMD); + sunxi_nand->addr[0] = 0; + sunxi_nand->addr[1] = 0; + sunxi_nand->addr_cycles = 0; + sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + } + + if (ctrl & NAND_CLE) { + sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; + } else if (ctrl & NAND_ALE) { + sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= + dat << ((sunxi_nand->addr_cycles % 4) * 8); + sunxi_nand->addr_cycles++; + } +} + +/* These seed values have been extracted from Allwinner's BSP */ +static const u16 sunxi_nfc_randomizer_page_seeds[] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +/* + * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds + * have been generated using + * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what + * the randomizer engine does internally before de/scrambling OOB data. + * + * Those tables are statically defined to avoid calculating randomizer state + * at runtime. + */ +static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { + 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, + 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, + 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, + 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, + 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, + 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, + 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, + 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, + 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, + 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, + 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, + 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, + 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, + 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, + 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, + 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, +}; + +static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { + 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, + 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, + 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, + 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, + 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, + 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, + 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, + 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, + 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, + 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, + 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, + 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, + 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, + 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, + 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, + 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, +}; + +static u16 sunxi_nfc_randomizer_step(u16 state, int count) +{ + state &= 0x7fff; + + /* + * This loop is just a simple implementation of a Fibonacci LFSR using + * the x16 + x15 + 1 polynomial. + */ + while (count--) + state = ((state >> 1) | + (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) +{ + const u16 *seeds = sunxi_nfc_randomizer_page_seeds; + int mod = mtd->erasesize / mtd->writesize; + + if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) + mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); + + if (ecc) { + if (mtd->ecc_step_size == 512) + seeds = sunxi_nfc_randomizer_ecc512_seeds; + else + seeds = sunxi_nfc_randomizer_ecc1024_seeds; + } + + return seeds[page % mod]; +} + +static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, + int page, bool ecc) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + u16 state; + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + state = sunxi_nfc_randomizer_state(mtd, page, ecc); + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; + writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) +{ + u16 state = sunxi_nfc_randomizer_state(mtd, page, true); + + bbm[0] ^= state; + bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); +} + +static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len, + bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_write_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + +static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len, bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_read_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + +static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_ecc *data = nand->ecc.priv; + u32 ecc_ctl; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | + NFC_ECC_BLOCK_SIZE_MSK); + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; + + if (nand->ecc.size == 512) + ecc_ctl |= NFC_ECC_BLOCK_512; + + writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) +{ + buf[0] = user_data; + buf[1] = user_data >> 8; + buf[2] = user_data >> 16; + buf[3] = user_data >> 24; +} + +static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, + u8 *data, int data_off, + u8 *oob, int oob_off, + int *cur_off, + unsigned int *max_bitflips, + bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int raw_mode = 0; + u32 status; + int ret; + + if (*cur_off != data_off) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); + + sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); + + if (data_off + ecc->size != oob_off) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + sunxi_nfc_randomizer_enable(mtd); + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + sunxi_nfc_randomizer_disable(mtd); + if (ret) + return ret; + + *cur_off = oob_off + ecc->bytes + 4; + + status = readl(nfc->regs + NFC_REG_ECC_ST); + if (status & NFC_ECC_PAT_FOUND(0)) { + u8 pattern = 0xff; + + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) + pattern = 0x0; + + memset(data, pattern, ecc->size); + memset(oob, pattern, ecc->bytes + 4); + + return 1; + } + + ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); + + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); + + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); + + if (status & NFC_ECC_ERR(0)) { + /* + * Re-read the data with the randomizer disabled to identify + * bitflips in erased pages. + */ + if (nand->options & NAND_NEED_SCRAMBLING) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); + nand->read_buf(mtd, data, ecc->size); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand->read_buf(mtd, oob, ecc->bytes + 4); + } + + ret = nand_check_erased_ecc_chunk(data, ecc->size, + oob, ecc->bytes + 4, + NULL, 0, ecc->strength); + if (ret >= 0) + raw_mode = 1; + } else { + /* + * The engine protects 4 bytes of OOB data per chunk. + * Retrieve the corrected OOB bytes. + */ + sunxi_nfc_user_data_to_buf(readl(nfc->regs + + NFC_REG_USER_DATA(0)), + oob); + + /* De-randomize the Bad Block Marker. */ + if (bbm && nand->options & NAND_NEED_SCRAMBLING) + sunxi_nfc_randomize_bbm(mtd, page, oob); + } + + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } + + return raw_mode; +} + +static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off, + bool randomize, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (*cur_off != offset) + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, + offset + mtd->writesize, -1); + + if (!randomize) + sunxi_nfc_read_buf(mtd, oob + offset, len); + else + sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, + false, page); + + *cur_off = mtd->oobsize + mtd->writesize; +} + +static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, + const u8 *data, int data_off, + const u8 *oob, int oob_off, + int *cur_off, bool bbm, + int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int ret; + + if (data_off != *cur_off) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); + + sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); + + /* Fill OOB data in */ + if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { + u8 user_data[4]; + + memcpy(user_data, oob, 4); + sunxi_nfc_randomize_bbm(mtd, page, user_data); + writel(sunxi_nfc_buf_to_user_data(user_data), + nfc->regs + NFC_REG_USER_DATA(0)); + } else { + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(0)); + } + + if (data_off + ecc->size != oob_off) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); + + ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); + if (ret) + return ret; + + sunxi_nfc_randomizer_enable(mtd); + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | + NFC_ACCESS_DIR | NFC_ECC_OP, + nfc->regs + NFC_REG_CMD); + + ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + sunxi_nfc_randomizer_disable(mtd); + if (ret) + return ret; + + *cur_off = oob_off + ecc->bytes + 4; + + return 0; +} + +static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, + u8 *oob, int *cur_off, + int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + int offset = ((ecc->bytes + 4) * ecc->steps); + int len = mtd->oobsize - offset; + + if (len <= 0) + return; + + if (*cur_off != offset) + nand->cmdfunc(mtd, NAND_CMD_RNDIN, + offset + mtd->writesize, -1); + + sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); + + *cur_off = mtd->oobsize + mtd->writesize; +} + +static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + unsigned int max_bitflips = 0; + int ret, i, cur_off = 0; + bool raw_mode = false; + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = buf + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, &max_bitflips, + !i, page); + if (ret < 0) + return ret; + else if (ret) + raw_mode = true; + } + + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, + struct nand_chip *chip, + uint32_t data_offs, uint32_t readlen, + uint8_t *bufpoi, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + unsigned int max_bitflips = 0; + + sunxi_nfc_hw_ecc_enable(mtd); + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + for (i = data_offs / ecc->size; + i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = bufpoi + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, + oob, oob_off + mtd->writesize, + &cur_off, &max_bitflips, !i, page); + if (ret < 0) + return ret; + } + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + const u8 *data = buf + data_off; + const u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, !i, page); + if (ret) + return ret; + } + + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return 0; +} + +static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, + struct nand_chip *chip, + u32 data_offs, u32 data_len, + const u8 *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = data_offs / ecc->size; + i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + const u8 *data = buf + data_off; + const u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, + oob_off + mtd->writesize, + &cur_off, !i, page); + if (ret) + return ret; + } + + sunxi_nfc_hw_ecc_disable(mtd); + + return 0; +} + +static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + unsigned int max_bitflips = 0; + int ret, i, cur_off = 0; + bool raw_mode = false; + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + u8 *data = buf + (i * ecc->size); + u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, + oob_off, &cur_off, + &max_bitflips, !i, page); + if (ret < 0) + return ret; + else if (ret) + raw_mode = true; + } + + if (oob_required) + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + +static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + + sunxi_nfc_hw_ecc_enable(mtd); + + for (i = 0; i < ecc->steps; i++) { + int data_off = i * (ecc->size + ecc->bytes + 4); + int oob_off = data_off + ecc->size; + const u8 *data = buf + (i * ecc->size); + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + + ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, + oob, oob_off, &cur_off, + false, page); + if (ret) + return ret; + } + + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); + + sunxi_nfc_hw_ecc_disable(mtd); + + return 0; +} + +static const s32 tWB_lut[] = {6, 12, 16, 20}; +static const s32 tRHW_lut[] = {4, 8, 12, 20}; + +static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, + u32 clk_period) +{ + u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); + int i; + + for (i = 0; i < lut_size; i++) { + if (clk_cycles <= lut[i]) + return i; + } + + /* Doesn't fit */ + return -EINVAL; +} + +#define sunxi_nand_lookup_timing(l, p, c) \ + _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) + +static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, + const struct nand_sdr_timings *timings) +{ + u32 min_clk_period = 0; + s32 tWB, tADL, tWHR, tRHW, tCAD; + + /* T1 <=> tCLS */ + if (timings->tCLS_min > min_clk_period) + min_clk_period = timings->tCLS_min; + + /* T2 <=> tCLH */ + if (timings->tCLH_min > min_clk_period) + min_clk_period = timings->tCLH_min; + + /* T3 <=> tCS */ + if (timings->tCS_min > min_clk_period) + min_clk_period = timings->tCS_min; + + /* T4 <=> tCH */ + if (timings->tCH_min > min_clk_period) + min_clk_period = timings->tCH_min; + + /* T5 <=> tWP */ + if (timings->tWP_min > min_clk_period) + min_clk_period = timings->tWP_min; + + /* T6 <=> tWH */ + if (timings->tWH_min > min_clk_period) + min_clk_period = timings->tWH_min; + + /* T7 <=> tALS */ + if (timings->tALS_min > min_clk_period) + min_clk_period = timings->tALS_min; + + /* T8 <=> tDS */ + if (timings->tDS_min > min_clk_period) + min_clk_period = timings->tDS_min; + + /* T9 <=> tDH */ + if (timings->tDH_min > min_clk_period) + min_clk_period = timings->tDH_min; + + /* T10 <=> tRR */ + if (timings->tRR_min > (min_clk_period * 3)) + min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); + + /* T11 <=> tALH */ + if (timings->tALH_min > min_clk_period) + min_clk_period = timings->tALH_min; + + /* T12 <=> tRP */ + if (timings->tRP_min > min_clk_period) + min_clk_period = timings->tRP_min; + + /* T13 <=> tREH */ + if (timings->tREH_min > min_clk_period) + min_clk_period = timings->tREH_min; + + /* T14 <=> tRC */ + if (timings->tRC_min > (min_clk_period * 2)) + min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); + + /* T15 <=> tWC */ + if (timings->tWC_min > (min_clk_period * 2)) + min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); + + /* T16 - T19 + tCAD */ + tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, + min_clk_period); + if (tWB < 0) { + dev_err(nfc->dev, "unsupported tWB\n"); + return tWB; + } + + tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; + if (tADL > 3) { + dev_err(nfc->dev, "unsupported tADL\n"); + return -EINVAL; + } + + tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; + if (tWHR > 3) { + dev_err(nfc->dev, "unsupported tWHR\n"); + return -EINVAL; + } + + tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, + min_clk_period); + if (tRHW < 0) { + dev_err(nfc->dev, "unsupported tRHW\n"); + return tRHW; + } + + /* + * TODO: according to ONFI specs this value only applies for DDR NAND, + * but Allwinner seems to set this to 0x7. Mimic them for now. + */ + tCAD = 0x7; + + /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ + chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); + + /* + * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data + * output cycle timings shall be used if the host drives tRC less than + * 30 ns. + */ + chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0; + + /* Convert min_clk_period from picoseconds to nanoseconds */ + min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); + + /* + * Convert min_clk_period into a clk frequency, then get the + * appropriate rate for the NAND controller IP given this formula + * (specified in the datasheet): + * nand clk_rate = min_clk_rate + */ + chip->clk_rate = 1000000000L / min_clk_period; + + return 0; +} + +static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(&chip->nand); + const struct nand_sdr_timings *timings; + int ret; + int mode; + + mode = onfi_get_async_timing_mode(&chip->nand); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + mode = chip->nand.onfi_timing_mode_default; + } else { + uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; + int i; + + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + feature[0] = mode; + for (i = 0; i < chip->nsels; i++) { + chip->nand.select_chip(mtd, i); + ret = chip->nand.onfi_set_features(mtd, + &chip->nand, + ONFI_FEATURE_ADDR_TIMING_MODE, + feature); + chip->nand.select_chip(mtd, -1); + if (ret && ret != -ENOTSUPP) + return ret; + } + } + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + return sunxi_nand_chip_set_timings(chip, timings); +} + +static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + struct sunxi_nand_hw_ecc *data; + struct nand_ecclayout *layout; + int nsectors; + int ret; + int i; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + if (ecc->size != 512 && ecc->size != 1024) + return -EINVAL; + + /* Prefer 1k ECC chunk over 512 ones */ + if (ecc->size == 512 && mtd->writesize > 512) { + ecc->size = 1024; + ecc->strength *= 2; + } + + /* Add ECC info retrieval from DT */ + for (i = 0; i < ARRAY_SIZE(strengths); i++) { + if (ecc->strength <= strengths[i]) { + /* + * Update ecc->strength value with the actual strength + * that will be used by the ECC engine. + */ + ecc->strength = strengths[i]; + break; + } + } + + if (i >= ARRAY_SIZE(strengths)) { + dev_err(nfc->dev, "unsupported strength\n"); + ret = -ENOTSUPP; + goto err; + } + + data->mode = i; + + /* HW ECC always request ECC bytes for 1024 bytes blocks */ + ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); + + /* HW ECC always work with even numbers of ECC bytes */ + ecc->bytes = ALIGN(ecc->bytes, 2); + + layout = &data->layout; + nsectors = mtd->writesize / ecc->size; + + if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { + ret = -EINVAL; + goto err; + } + + layout->eccbytes = (ecc->bytes * nsectors); + + ecc->layout = layout; + ecc->priv = data; + + return 0; + +err: + kfree(data); + + return ret; +} + +#ifndef __UBOOT__ +static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) +{ + kfree(ecc->priv); +} +#endif /* __UBOOT__ */ + +static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_ecclayout *layout; + int nsectors; + int i, j; + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + + ecc->read_page = sunxi_nfc_hw_ecc_read_page; + ecc->write_page = sunxi_nfc_hw_ecc_write_page; + ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; + ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; + layout = ecc->layout; + nsectors = mtd->writesize / ecc->size; + + for (i = 0; i < nsectors; i++) { + if (i) { + layout->oobfree[i].offset = + layout->oobfree[i - 1].offset + + layout->oobfree[i - 1].length + + ecc->bytes; + layout->oobfree[i].length = 4; + } else { + /* + * The first 2 bytes are used for BB markers, hence we + * only have 2 bytes available in the first user data + * section. + */ + layout->oobfree[i].length = 2; + layout->oobfree[i].offset = 2; + } + + for (j = 0; j < ecc->bytes; j++) + layout->eccpos[(ecc->bytes * i) + j] = + layout->oobfree[i].offset + + layout->oobfree[i].length + j; + } + + if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { + layout->oobfree[nsectors].offset = + layout->oobfree[nsectors - 1].offset + + layout->oobfree[nsectors - 1].length + + ecc->bytes; + layout->oobfree[nsectors].length = mtd->oobsize - + ((ecc->bytes + 4) * nsectors); + } + + return 0; +} + +static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_ecclayout *layout; + int nsectors; + int i; + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + + ecc->prepad = 4; + ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; + ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; + + layout = ecc->layout; + nsectors = mtd->writesize / ecc->size; + + for (i = 0; i < (ecc->bytes * nsectors); i++) + layout->eccpos[i] = i; + + layout->oobfree[0].length = mtd->oobsize - i; + layout->oobfree[0].offset = i; + + return 0; +} + +#ifndef __UBOOT__ +static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) +{ + switch (ecc->mode) { + case NAND_ECC_HW: + case NAND_ECC_HW_SYNDROME: + sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); + break; + case NAND_ECC_NONE: + kfree(ecc->layout); + default: + break; + } +} +#endif /* __UBOOT__ */ + +static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + int ret; + + if (!ecc->size) { + ecc->size = nand->ecc_step_ds; + ecc->strength = nand->ecc_strength_ds; + } + + if (!ecc->size || !ecc->strength) + return -EINVAL; + + switch (ecc->mode) { + case NAND_ECC_SOFT_BCH: + break; + case NAND_ECC_HW: + ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + break; + case NAND_ECC_HW_SYNDROME: + ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + break; + case NAND_ECC_NONE: + ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL); + if (!ecc->layout) + return -ENOMEM; + ecc->layout->oobfree[0].length = mtd->oobsize; + case NAND_ECC_SOFT: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum) +{ + const struct nand_sdr_timings *timings; + const void *blob = gd->fdt_blob; + struct sunxi_nand_chip *chip; + struct mtd_info *mtd; + struct nand_chip *nand; + int nsels; + int ret; + int i; + u32 cs[8], rb[8]; + + if (!fdt_getprop(blob, node, "reg", &nsels)) + return -EINVAL; + + nsels /= sizeof(u32); + if (!nsels || nsels > 8) { + dev_err(dev, "invalid reg property size\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(*chip) + + (nsels * sizeof(struct sunxi_nand_chip_sel)), + GFP_KERNEL); + if (!chip) { + dev_err(dev, "could not allocate chip\n"); + return -ENOMEM; + } + + chip->nsels = nsels; + chip->selected = -1; + + for (i = 0; i < nsels; i++) { + cs[i] = -1; + rb[i] = -1; + } + + ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", cs, nsels); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + ret = fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", rb, + nsels); + if (ret) { + dev_err(dev, "could not retrieve reg property: %d\n", ret); + return ret; + } + + for (i = 0; i < nsels; i++) { + int tmp = cs[i]; + + if (tmp > NFC_MAX_CS) { + dev_err(dev, + "invalid reg value: %u (max CS = 7)\n", + tmp); + return -EINVAL; + } + + if (test_and_set_bit(tmp, &nfc->assigned_cs)) { + dev_err(dev, "CS %d already assigned\n", tmp); + return -EINVAL; + } + + chip->sels[i].cs = tmp; + + tmp = rb[i]; + if (tmp >= 0 && tmp < 2) { + chip->sels[i].rb.type = RB_NATIVE; + chip->sels[i].rb.info.nativeid = tmp; + } else { + ret = gpio_request_by_name_nodev(offset_to_ofnode(node), + "rb-gpios", i, + &chip->sels[i].rb.info.gpio, + GPIOD_IS_IN); + if (ret) + chip->sels[i].rb.type = RB_GPIO; + else + chip->sels[i].rb.type = RB_NONE; + } + } + + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) { + ret = PTR_ERR(timings); + dev_err(dev, + "could not retrieve timings for ONFI mode 0: %d\n", + ret); + return ret; + } + + ret = sunxi_nand_chip_set_timings(chip, timings); + if (ret) { + dev_err(dev, "could not configure chip timings: %d\n", ret); + return ret; + } + + nand = &chip->nand; + /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ + nand->chip_delay = 200; + nand->controller = &nfc->controller; + /* + * Set the ECC mode to the default value in case nothing is specified + * in the DT. + */ + nand->ecc.mode = NAND_ECC_HW; + nand->flash_node = node; + nand->select_chip = sunxi_nfc_select_chip; + nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; + nand->read_buf = sunxi_nfc_read_buf; + nand->write_buf = sunxi_nfc_write_buf; + nand->read_byte = sunxi_nfc_read_byte; + + mtd = nand_to_mtd(nand); + ret = nand_scan_ident(mtd, nsels, NULL); + if (ret) + return ret; + + if (nand->bbt_options & NAND_BBT_USE_FLASH) + nand->bbt_options |= NAND_BBT_NO_OOB; + + if (nand->options & NAND_NEED_SCRAMBLING) + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->options |= NAND_SUBPAGE_READ; + + ret = sunxi_nand_chip_init_timings(chip); + if (ret) { + dev_err(dev, "could not configure chip timings: %d\n", ret); + return ret; + } + + ret = sunxi_nand_ecc_init(mtd, &nand->ecc); + if (ret) { + dev_err(dev, "ECC init failed: %d\n", ret); + return ret; + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(dev, "nand_scan_tail failed: %d\n", ret); + return ret; + } + + ret = nand_register(devnum, mtd); + if (ret) { + dev_err(dev, "failed to register mtd device: %d\n", ret); + return ret; + } + + list_add_tail(&chip->node, &nfc->chips); + + return 0; +} + +static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc) +{ + const void *blob = gd->fdt_blob; + int nand_node; + int ret, i = 0; + + for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0; + nand_node = fdt_next_subnode(blob, nand_node)) + i++; + + if (i > 8) { + dev_err(dev, "too many NAND chips: %d (max = 8)\n", i); + return -EINVAL; + } + + i = 0; + for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0; + nand_node = fdt_next_subnode(blob, nand_node)) { + ret = sunxi_nand_chip_init(nand_node, nfc, i++); + if (ret) + return ret; + } + + return 0; +} + +#ifndef __UBOOT__ +static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) +{ + struct sunxi_nand_chip *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, + node); + nand_release(&chip->mtd); + sunxi_nand_ecc_cleanup(&chip->nand.ecc); + list_del(&chip->node); + kfree(chip); + } +} +#endif /* __UBOOT__ */ + +void sunxi_nand_init(void) +{ + const void *blob = gd->fdt_blob; + struct sunxi_nfc *nfc; + fdt_addr_t regs; + int node; + int ret; + + nfc = kzalloc(sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return; + + spin_lock_init(&nfc->controller.lock); + init_waitqueue_head(&nfc->controller.wq); + INIT_LIST_HEAD(&nfc->chips); + + node = fdtdec_next_compatible(blob, 0, COMPAT_SUNXI_NAND); + if (node < 0) { + pr_err("unable to find nfc node in device tree\n"); + goto err; + } + + if (!fdtdec_get_is_enabled(blob, node)) { + pr_err("nfc disabled in device tree\n"); + goto err; + } + + regs = fdtdec_get_addr(blob, node, "reg"); + if (regs == FDT_ADDR_T_NONE) { + pr_err("unable to find nfc address in device tree\n"); + goto err; + } + + nfc->regs = (void *)regs; + + ret = sunxi_nfc_rst(nfc); + if (ret) + goto err; + + ret = sunxi_nand_chips_init(node, nfc); + if (ret) { + dev_err(dev, "failed to init nand chips\n"); + goto err; + } + + return; + +err: + kfree(nfc); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Boris BREZILLON"); +MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c new file mode 100644 index 0000000000..6cde9814c4 --- /dev/null +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014-2015, Antmicro Ltd + * Copyright (c) 2015, AW-SOM Technologies + */ + +#include +#include +#include +#include +#include +#include + +/* registers */ +#define NFC_CTL 0x00000000 +#define NFC_ST 0x00000004 +#define NFC_INT 0x00000008 +#define NFC_TIMING_CTL 0x0000000C +#define NFC_TIMING_CFG 0x00000010 +#define NFC_ADDR_LOW 0x00000014 +#define NFC_ADDR_HIGH 0x00000018 +#define NFC_SECTOR_NUM 0x0000001C +#define NFC_CNT 0x00000020 +#define NFC_CMD 0x00000024 +#define NFC_RCMD_SET 0x00000028 +#define NFC_WCMD_SET 0x0000002C +#define NFC_IO_DATA 0x00000030 +#define NFC_ECC_CTL 0x00000034 +#define NFC_ECC_ST 0x00000038 +#define NFC_DEBUG 0x0000003C +#define NFC_ECC_CNT0 0x00000040 +#define NFC_ECC_CNT1 0x00000044 +#define NFC_ECC_CNT2 0x00000048 +#define NFC_ECC_CNT3 0x0000004C +#define NFC_USER_DATA_BASE 0x00000050 +#define NFC_EFNAND_STATUS 0x00000090 +#define NFC_SPARE_AREA 0x000000A0 +#define NFC_PATTERN_ID 0x000000A4 +#define NFC_RAM0_BASE 0x00000400 +#define NFC_RAM1_BASE 0x00000800 + +#define NFC_CTL_EN (1 << 0) +#define NFC_CTL_RESET (1 << 1) +#define NFC_CTL_RAM_METHOD (1 << 14) +#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8) +#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) + + +#define NFC_ECC_EN (1 << 0) +#define NFC_ECC_PIPELINE (1 << 3) +#define NFC_ECC_EXCEPTION (1 << 4) +#define NFC_ECC_BLOCK_SIZE (1 << 5) +#define NFC_ECC_RANDOM_EN (1 << 9) +#define NFC_ECC_RANDOM_DIRECTION (1 << 10) + + +#define NFC_ADDR_NUM_OFFSET 16 +#define NFC_SEND_ADDR (1 << 19) +#define NFC_ACCESS_DIR (1 << 20) +#define NFC_DATA_TRANS (1 << 21) +#define NFC_SEND_CMD1 (1 << 22) +#define NFC_WAIT_FLAG (1 << 23) +#define NFC_SEND_CMD2 (1 << 24) +#define NFC_SEQ (1 << 25) +#define NFC_DATA_SWAP_METHOD (1 << 26) +#define NFC_ROW_AUTO_INC (1 << 27) +#define NFC_SEND_CMD3 (1 << 28) +#define NFC_SEND_CMD4 (1 << 29) +#define NFC_RAW_CMD (0 << 30) +#define NFC_ECC_CMD (1 << 30) +#define NFC_PAGE_CMD (2 << 30) + +#define NFC_ST_CMD_INT_FLAG (1 << 1) +#define NFC_ST_DMA_INT_FLAG (1 << 2) +#define NFC_ST_CMD_FIFO_STAT (1 << 3) + +#define NFC_READ_CMD_OFFSET 0 +#define NFC_RANDOM_READ_CMD0_OFFSET 8 +#define NFC_RANDOM_READ_CMD1_OFFSET 16 + +#define NFC_CMD_RNDOUTSTART 0xE0 +#define NFC_CMD_RNDOUT 0x05 +#define NFC_CMD_READSTART 0x30 + +struct nfc_config { + int page_size; + int ecc_strength; + int ecc_size; + int addr_cycles; + int nseeds; + bool randomize; + bool valid; +}; + +/* minimal "boot0" style NAND support for Allwinner A20 */ + +/* random seed used by linux */ +const uint16_t random_seed[128] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +#define DEFAULT_TIMEOUT_US 100000 + +static int check_value_inner(int offset, int expected_bits, + int timeout_us, int negation) +{ + do { + int val = readl(offset) & expected_bits; + if (negation ? !val : val) + return 1; + udelay(1); + } while (--timeout_us); + + return 0; +} + +static inline int check_value(int offset, int expected_bits, + int timeout_us) +{ + return check_value_inner(offset, expected_bits, timeout_us, 0); +} + +static inline int check_value_negated(int offset, int unexpected_bits, + int timeout_us) +{ + return check_value_inner(offset, unexpected_bits, timeout_us, 1); +} + +static int nand_wait_cmd_fifo_empty(void) +{ + if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT, + DEFAULT_TIMEOUT_US)) { + printf("nand: timeout waiting for empty cmd FIFO\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nand_wait_int(void) +{ + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, + DEFAULT_TIMEOUT_US)) { + printf("nand: timeout waiting for interruption\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int nand_exec_cmd(u32 cmd) +{ + int ret; + + ret = nand_wait_cmd_fifo_empty(); + if (ret) + return ret; + + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); + writel(cmd, SUNXI_NFC_BASE + NFC_CMD); + + return nand_wait_int(); +} + +void nand_init(void) +{ + uint32_t val; + + board_nand_init(); + + val = readl(SUNXI_NFC_BASE + NFC_CTL); + /* enable and reset CTL */ + writel(val | NFC_CTL_EN | NFC_CTL_RESET, + SUNXI_NFC_BASE + NFC_CTL); + + if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, + NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { + printf("Couldn't initialize nand\n"); + } + + /* reset NAND */ + nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET); +} + +static void nand_apply_config(const struct nfc_config *conf) +{ + u32 val; + + nand_wait_cmd_fifo_empty(); + + val = readl(SUNXI_NFC_BASE + NFC_CTL); + val &= ~NFC_CTL_PAGE_SIZE_MASK; + writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size), + SUNXI_NFC_BASE + NFC_CTL); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); + writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA); +} + +static int nand_load_page(const struct nfc_config *conf, u32 offs) +{ + int page = offs / conf->page_size; + + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), + SUNXI_NFC_BASE + NFC_RCMD_SET); + writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); + writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); + + return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | + NFC_SEND_ADDR | NFC_WAIT_FLAG | + ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET)); +} + +static int nand_change_column(u16 column) +{ + int ret; + + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), + SUNXI_NFC_BASE + NFC_RCMD_SET); + writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW); + + ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | + (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | + NFC_CMD_RNDOUT); + if (ret) + return ret; + + /* Ensure tCCS has passed before reading data */ + udelay(1); + + return 0; +} + +static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116}; + +static int nand_read_page(const struct nfc_config *conf, u32 offs, + void *dest, int len) +{ + int nsectors = len / conf->ecc_size; + u16 rand_seed = 0; + int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; + int page = offs / conf->page_size; + u32 ecc_st; + int i; + + if (offs % conf->page_size || len % conf->ecc_size || + len > conf->page_size || len < 0) + return -EINVAL; + + /* Choose correct seed if randomized */ + if (conf->randomize) + rand_seed = random_seed[page % conf->nseeds]; + + /* Retrieve data from SRAM (PIO) */ + for (i = 0; i < nsectors; i++) { + int data_off = i * conf->ecc_size; + int oob_off = conf->page_size + (i * oob_chunk_sz); + u8 *data = dest + data_off; + + /* Clear ECC status and restart ECC engine */ + writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); + writel((rand_seed << 16) | (conf->ecc_strength << 12) | + (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | + (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | + NFC_ECC_EN | NFC_ECC_EXCEPTION, + SUNXI_NFC_BASE + NFC_ECC_CTL); + + /* Move the data in SRAM */ + nand_change_column(data_off); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); + nand_exec_cmd(NFC_DATA_TRANS); + + /* + * Let the ECC engine consume the ECC bytes and possibly correct + * the data. + */ + nand_change_column(oob_off); + nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD); + + /* Get the ECC status */ + ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST); + + /* ECC error detected. */ + if (ecc_st & 0xffff) + return -EIO; + + /* + * Return 1 if the first chunk is empty (needed for + * configuration detection). + */ + if (!i && (ecc_st & 0x10000)) + return 1; + + /* Retrieve the data from SRAM */ + memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE, + conf->ecc_size); + + /* Stop the ECC engine */ + writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN, + SUNXI_NFC_BASE + NFC_ECC_CTL); + + if (data_off + conf->ecc_size >= len) + break; + } + + return 0; +} + +static int nand_max_ecc_strength(struct nfc_config *conf) +{ + int max_oobsize, max_ecc_bytes; + int nsectors = conf->page_size / conf->ecc_size; + int i; + + /* + * ECC strength is limited by the size of the OOB area which is + * correlated with the page size. + */ + switch (conf->page_size) { + case 2048: + max_oobsize = 64; + break; + case 4096: + max_oobsize = 256; + break; + case 8192: + max_oobsize = 640; + break; + case 16384: + max_oobsize = 1664; + break; + default: + return -EINVAL; + } + + max_ecc_bytes = max_oobsize / nsectors; + + for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { + if (ecc_bytes[i] > max_ecc_bytes) + break; + } + + if (!i) + return -EINVAL; + + return i - 1; +} + +static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, + void *dest) +{ + /* NAND with pages > 4k will likely require 1k sector size. */ + int min_ecc_size = conf->page_size > 4096 ? 1024 : 512; + int page = offs / conf->page_size; + int ret; + + /* + * In most cases, 1k sectors are preferred over 512b ones, start + * testing this config first. + */ + for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size; + conf->ecc_size >>= 1) { + int max_ecc_strength = nand_max_ecc_strength(conf); + + nand_apply_config(conf); + + /* + * We are starting from the maximum ECC strength because + * most of the time NAND vendors provide an OOB area that + * barely meets the ECC requirements. + */ + for (conf->ecc_strength = max_ecc_strength; + conf->ecc_strength >= 0; + conf->ecc_strength--) { + conf->randomize = false; + if (nand_change_column(0)) + return -EIO; + + /* + * Only read the first sector to speedup detection. + */ + ret = nand_read_page(conf, offs, dest, conf->ecc_size); + if (!ret) { + return 0; + } else if (ret > 0) { + /* + * If page is empty we can't deduce anything + * about the ECC config => stop the detection. + */ + return -EINVAL; + } + + conf->randomize = true; + conf->nseeds = ARRAY_SIZE(random_seed); + do { + if (nand_change_column(0)) + return -EIO; + + if (!nand_read_page(conf, offs, dest, + conf->ecc_size)) + return 0; + + /* + * Find the next ->nseeds value that would + * change the randomizer seed for the page + * we're trying to read. + */ + while (conf->nseeds >= 16) { + int seed = page % conf->nseeds; + + conf->nseeds >>= 1; + if (seed != page % conf->nseeds) + break; + } + } while (conf->nseeds >= 16); + } + } + + return -EINVAL; +} + +static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) +{ + if (conf->valid) + return 0; + + /* + * Modern NANDs are more likely than legacy ones, so we start testing + * with 5 address cycles. + */ + for (conf->addr_cycles = 5; + conf->addr_cycles >= 4; + conf->addr_cycles--) { + int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384; + + /* + * Ignoring 1k pages cause I'm not even sure this case exist + * in the real world. + */ + for (conf->page_size = 2048; conf->page_size <= max_page_size; + conf->page_size <<= 1) { + if (nand_load_page(conf, offs)) + return -1; + + if (!nand_detect_ecc_config(conf, offs, dest)) { + conf->valid = true; + return 0; + } + } + } + + return -EINVAL; +} + +static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, + unsigned int size, void *dest) +{ + int first_seed = 0, page, ret; + + size = ALIGN(size, conf->page_size); + page = offs / conf->page_size; + if (conf->randomize) + first_seed = page % conf->nseeds; + + for (; size; size -= conf->page_size) { + if (nand_load_page(conf, offs)) + return -1; + + ret = nand_read_page(conf, offs, dest, conf->page_size); + /* + * The ->nseeds value should be equal to the number of pages + * in an eraseblock. Since we don't know this information in + * advance we might have picked a wrong value. + */ + if (ret < 0 && conf->randomize) { + int cur_seed = page % conf->nseeds; + + /* + * We already tried all the seed values => we are + * facing a real corruption. + */ + if (cur_seed < first_seed) + return -EIO; + + /* Try to adjust ->nseeds and read the page again... */ + conf->nseeds = cur_seed; + + if (nand_change_column(0)) + return -EIO; + + /* ... it still fails => it's a real corruption. */ + if (nand_read_page(conf, offs, dest, conf->page_size)) + return -EIO; + } else if (ret && conf->randomize) { + memset(dest, 0xff, conf->page_size); + } + + page++; + offs += conf->page_size; + dest += conf->page_size; + } + + return 0; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) +{ + static struct nfc_config conf = { }; + int ret; + + ret = nand_detect_config(&conf, offs, dest); + if (ret) + return ret; + + return nand_read_buffer(&conf, offs, size, dest); +} + +void nand_deselect(void) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); +#ifdef CONFIG_MACH_SUN9I + clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); +#else + clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); +#endif + clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); +} diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c new file mode 100644 index 0000000000..74acdfb308 --- /dev/null +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -0,0 +1,1002 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2011 NVIDIA Corporation + * (C) Copyright 2006 Detlev Zundel, dzu@denx.de + * (C) Copyright 2006 DENX Software Engineering + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tegra_nand.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define NAND_CMD_TIMEOUT_MS 10 + +#define SKIPPED_SPARE_BYTES 4 + +/* ECC bytes to be generated for tag data */ +#define TAG_ECC_BYTES 4 + +static const struct udevice_id tegra_nand_dt_ids[] = { + { + .compatible = "nvidia,tegra20-nand", + }, + { /* sentinel */ } +}; + +/* 64 byte oob block info for large page (== 2KB) device + * + * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC: + * Skipped bytes(4) + * Main area Ecc(36) + * Tag data(20) + * Tag data Ecc(4) + * + * Yaffs2 will use 16 tag bytes. + */ +static struct nand_ecclayout eccoob = { + .eccbytes = 36, + .eccpos = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, + }, + .oobavail = 20, + .oobfree = { + { + .offset = 40, + .length = 20, + }, + } +}; + +enum { + ECC_OK, + ECC_TAG_ERROR = 1 << 0, + ECC_DATA_ERROR = 1 << 1 +}; + +/* Timing parameters */ +enum { + FDT_NAND_MAX_TRP_TREA, + FDT_NAND_TWB, + FDT_NAND_MAX_TCR_TAR_TRR, + FDT_NAND_TWHR, + FDT_NAND_MAX_TCS_TCH_TALS_TALH, + FDT_NAND_TWH, + FDT_NAND_TWP, + FDT_NAND_TRH, + FDT_NAND_TADL, + + FDT_NAND_TIMING_COUNT +}; + +/* Information about an attached NAND chip */ +struct fdt_nand { + struct nand_ctlr *reg; + int enabled; /* 1 to enable, 0 to disable */ + struct gpio_desc wp_gpio; /* write-protect GPIO */ + s32 width; /* bit width, normally 8 */ + u32 timing[FDT_NAND_TIMING_COUNT]; +}; + +struct nand_drv { + struct nand_ctlr *reg; + struct fdt_nand config; +}; + +struct tegra_nand_info { + struct udevice *dev; + struct nand_drv nand_ctrl; + struct nand_chip nand_chip; +}; + +/** + * Wait for command completion + * + * @param reg nand_ctlr structure + * @return + * 1 - Command completed + * 0 - Timeout + */ +static int nand_waitfor_cmd_completion(struct nand_ctlr *reg) +{ + u32 reg_val; + int running; + int i; + + for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) { + if ((readl(®->command) & CMD_GO) || + !(readl(®->status) & STATUS_RBSY0) || + !(readl(®->isr) & ISR_IS_CMD_DONE)) { + udelay(1); + continue; + } + reg_val = readl(®->dma_mst_ctrl); + /* + * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE + * is set, that means DMA engine is running. + * + * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE + * is cleared, indicating DMA transfer completion. + */ + running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE | + DMA_MST_CTRL_EN_B_ENABLE); + if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE)) + return 1; + udelay(1); + } + return 0; +} + +/** + * Read one byte from the chip + * + * @param mtd MTD device structure + * @return data byte + * + * Read function for 8bit bus-width + */ +static uint8_t read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_drv *info; + + info = (struct nand_drv *)nand_get_controller_data(chip); + + writel(CMD_GO | CMD_PIO | CMD_RX | CMD_CE0 | CMD_A_VALID, + &info->reg->command); + if (!nand_waitfor_cmd_completion(info->reg)) + printf("Command timeout\n"); + + return (uint8_t)readl(&info->reg->resp); +} + +/** + * Read len bytes from the chip into a buffer + * + * @param mtd MTD device structure + * @param buf buffer to store data to + * @param len number of bytes to read + * + * Read function for 8bit bus-width + */ +static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i, s; + unsigned int reg; + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_drv *info = (struct nand_drv *)nand_get_controller_data(chip); + + for (i = 0; i < len; i += 4) { + s = (len - i) > 4 ? 4 : len - i; + writel(CMD_PIO | CMD_RX | CMD_A_VALID | CMD_CE0 | + ((s - 1) << CMD_TRANS_SIZE_SHIFT) | CMD_GO, + &info->reg->command); + if (!nand_waitfor_cmd_completion(info->reg)) + puts("Command timeout during read_buf\n"); + reg = readl(&info->reg->resp); + memcpy(buf + i, ®, s); + } +} + +/** + * Check NAND status to see if it is ready or not + * + * @param mtd MTD device structure + * @return + * 1 - ready + * 0 - not ready + */ +static int nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int reg_val; + struct nand_drv *info; + + info = (struct nand_drv *)nand_get_controller_data(chip); + + reg_val = readl(&info->reg->status); + if (reg_val & STATUS_RBSY0) + return 1; + else + return 0; +} + +/* Dummy implementation: we don't support multiple chips */ +static void nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + switch (chipnr) { + case -1: + case 0: + break; + + default: + BUG(); + } +} + +/** + * Clear all interrupt status bits + * + * @param reg nand_ctlr structure + */ +static void nand_clear_interrupt_status(struct nand_ctlr *reg) +{ + u32 reg_val; + + /* Clear interrupt status */ + reg_val = readl(®->isr); + writel(reg_val, ®->isr); +} + +/** + * Send command to NAND device + * + * @param mtd MTD device structure + * @param command the command to be sent + * @param column the column address for this command, -1 if none + * @param page_addr the page address for this command, -1 if none + */ +static void nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_drv *info; + + info = (struct nand_drv *)nand_get_controller_data(chip); + + /* + * Write out the command to the device. + * + * Only command NAND_CMD_RESET or NAND_CMD_READID will come + * here before mtd->writesize is initialized. + */ + + /* Emulate NAND_CMD_READOOB */ + if (command == NAND_CMD_READOOB) { + assert(mtd->writesize != 0); + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Adjust columns for 16 bit bus-width */ + if (column != -1 && (chip->options & NAND_BUSWIDTH_16)) + column >>= 1; + + nand_clear_interrupt_status(info->reg); + + /* Stop DMA engine, clear DMA completion status */ + writel(DMA_MST_CTRL_EN_A_DISABLE + | DMA_MST_CTRL_EN_B_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + &info->reg->dma_mst_ctrl); + + /* + * Program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + case NAND_CMD_READID: + writel(NAND_CMD_READID, &info->reg->cmd_reg1); + writel(column & 0xFF, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_PARAM: + writel(NAND_CMD_PARAM, &info->reg->cmd_reg1); + writel(column & 0xFF, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_READ0: + writel(NAND_CMD_READ0, &info->reg->cmd_reg1); + writel(NAND_CMD_READSTART, &info->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &info->reg->addr_reg1); + writel(page_addr >> 16, &info->reg->addr_reg2); + return; + case NAND_CMD_SEQIN: + writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1); + writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2); + writel((page_addr << 16) | (column & 0xFFFF), + &info->reg->addr_reg1); + writel(page_addr >> 16, + &info->reg->addr_reg2); + return; + case NAND_CMD_PAGEPROG: + return; + case NAND_CMD_ERASE1: + writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1); + writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2); + writel(page_addr, &info->reg->addr_reg1); + writel(CMD_GO | CMD_CLE | CMD_ALE | + CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3, + &info->reg->command); + break; + case NAND_CMD_ERASE2: + return; + case NAND_CMD_STATUS: + writel(NAND_CMD_STATUS, &info->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX + | ((1 - 0) << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_RESET: + writel(NAND_CMD_RESET, &info->reg->cmd_reg1); + writel(CMD_GO | CMD_CLE | CMD_CE0, + &info->reg->command); + break; + case NAND_CMD_RNDOUT: + default: + printf("%s: Unsupported command %d\n", __func__, command); + return; + } + if (!nand_waitfor_cmd_completion(info->reg)) + printf("Command 0x%02X timeout\n", command); +} + +/** + * Check whether the pointed buffer are all 0xff (blank). + * + * @param buf data buffer for blank check + * @param len length of the buffer in byte + * @return + * 1 - blank + * 0 - non-blank + */ +static int blank_check(u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != 0xFF) + return 0; + return 1; +} + +/** + * After a DMA transfer for read, we call this function to see whether there + * is any uncorrectable error on the pointed data buffer or oob buffer. + * + * @param reg nand_ctlr structure + * @param databuf data buffer + * @param a_len data buffer length + * @param oobbuf oob buffer + * @param b_len oob buffer length + * @return + * ECC_OK - no ECC error or correctable ECC error + * ECC_TAG_ERROR - uncorrectable tag ECC error + * ECC_DATA_ERROR - uncorrectable data ECC error + * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error + */ +static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf, + int a_len, u8 *oobbuf, int b_len) +{ + int return_val = ECC_OK; + u32 reg_val; + + if (!(readl(®->isr) & ISR_IS_ECC_ERR)) + return ECC_OK; + + /* + * Area A is used for the data block (databuf). Area B is used for + * the spare block (oobbuf) + */ + reg_val = readl(®->dec_status); + if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on data area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) && + !blank_check(databuf, a_len)) + return_val |= ECC_DATA_ERROR; + } + + if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) { + reg_val = readl(®->bch_dec_status_buf); + /* + * If uncorrectable error occurs on tag area, then see whether + * they are all FF. If all are FF, it's a blank page. + * Not error. + */ + if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) && + !blank_check(oobbuf, b_len)) + return_val |= ECC_TAG_ERROR; + } + + return return_val; +} + +/** + * Set GO bit to send command to device + * + * @param reg nand_ctlr structure + */ +static void start_command(struct nand_ctlr *reg) +{ + u32 reg_val; + + reg_val = readl(®->command); + reg_val |= CMD_GO; + writel(reg_val, ®->command); +} + +/** + * Clear command GO bit, DMA GO bit, and DMA completion status + * + * @param reg nand_ctlr structure + */ +static void stop_command(struct nand_ctlr *reg) +{ + /* Stop command */ + writel(0, ®->command); + + /* Stop DMA engine and clear DMA completion status */ + writel(DMA_MST_CTRL_GO_DISABLE + | DMA_MST_CTRL_IS_DMA_DONE, + ®->dma_mst_ctrl); +} + +/** + * Set up NAND bus width and page size + * + * @param info nand_info structure + * @param *reg_val address of reg_val + * @return 0 if ok, -1 on error + */ +static int set_bus_width_page_size(struct mtd_info *our_mtd, + struct fdt_nand *config, u32 *reg_val) +{ + if (config->width == 8) + *reg_val = CFG_BUS_WIDTH_8BIT; + else if (config->width == 16) + *reg_val = CFG_BUS_WIDTH_16BIT; + else { + debug("%s: Unsupported bus width %d\n", __func__, + config->width); + return -1; + } + + if (our_mtd->writesize == 512) + *reg_val |= CFG_PAGE_SIZE_512; + else if (our_mtd->writesize == 2048) + *reg_val |= CFG_PAGE_SIZE_2048; + else if (our_mtd->writesize == 4096) + *reg_val |= CFG_PAGE_SIZE_4096; + else { + debug("%s: Unsupported page size %d\n", __func__, + our_mtd->writesize); + return -1; + } + + return 0; +} + +/** + * Page read/write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + * @param page page number + * @param with_ecc 1 to enable ECC, 0 to disable ECC + * @param is_writing 0 for read, 1 for write + * @return 0 when successfully completed + * -EIO when command timeout + */ +static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + /* 4*128=512 (byte) is the value that our HW can support. */ + ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128); + char *tag_ptr; + struct nand_drv *info; + struct fdt_nand *config; + unsigned int bbflags; + struct bounce_buffer bbstate, bbstate_oob; + + if ((uintptr_t)buf & 0x03) { + printf("buf %p has to be 4-byte aligned\n", buf); + return -EINVAL; + } + + info = (struct nand_drv *)nand_get_controller_data(chip); + config = &info->config; + if (set_bus_width_page_size(mtd, config, ®_val)) + return -EINVAL; + + /* Need to be 4-byte aligned */ + tag_ptr = (char *)tag_buf; + + stop_command(info->reg); + + if (is_writing) + bbflags = GEN_BB_READ; + else + bbflags = GEN_BB_WRITE; + + bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift, + bbflags); + writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a); + writel(virt_to_phys(bbstate.bounce_buffer), &info->reg->data_block_ptr); + + /* Set ECC selection, configure ECC settings */ + if (with_ecc) { + if (is_writing) + memcpy(tag_ptr, chip->oob_poi + free->offset, + chip->ecc.layout->oobavail + TAG_ECC_BYTES); + tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES; + reg_val |= (CFG_SKIP_SPARE_SEL_4 + | CFG_SKIP_SPARE_ENABLE + | CFG_HW_ECC_CORRECTION_ENABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_SEL_RS + | CFG_HW_ECC_ENABLE + | CFG_TVAL4 + | (tag_size - 1)); + + if (!is_writing) + tag_size += SKIPPED_SPARE_BYTES; + bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size, + bbflags); + } else { + tag_size = mtd->oobsize; + reg_val |= (CFG_SKIP_SPARE_DISABLE + | CFG_HW_ECC_CORRECTION_DISABLE + | CFG_ECC_EN_TAG_DISABLE + | CFG_HW_ECC_DISABLE + | (tag_size - 1)); + bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, + tag_size, bbflags); + } + writel(reg_val, &info->reg->config); + writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr); + writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + writel(tag_size - 1, &info->reg->dma_cfg_b); + + nand_clear_interrupt_status(info->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_A_VALID + | CMD_B_VALID + | (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT) + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &info->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_A_ENABLE + | DMA_MST_CTRL_EN_B_ENABLE; + + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &info->reg->dma_mst_ctrl); + + start_command(info->reg); + + if (!nand_waitfor_cmd_completion(info->reg)) { + if (!is_writing) + printf("Read Page 0x%X timeout ", page); + else + printf("Write Page 0x%X timeout ", page); + if (with_ecc) + printf("with ECC"); + else + printf("without ECC"); + printf("\n"); + return -EIO; + } + + bounce_buffer_stop(&bbstate_oob); + bounce_buffer_stop(&bbstate); + + if (with_ecc && !is_writing) { + memcpy(chip->oob_poi, tag_ptr, + SKIPPED_SPARE_BYTES); + memcpy(chip->oob_poi + free->offset, + tag_ptr + SKIPPED_SPARE_BYTES, + chip->ecc.layout->oobavail); + reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf, + 1 << chip->page_shift, + (u8 *)(tag_ptr + SKIPPED_SPARE_BYTES), + chip->ecc.layout->oobavail); + if (reg_val & ECC_TAG_ERROR) + printf("Read Page 0x%X tag ECC error\n", page); + if (reg_val & ECC_DATA_ERROR) + printf("Read Page 0x%X data ECC error\n", + page); + if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR)) + return -EIO; + } + return 0; +} + +/** + * Hardware ecc based page read function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf buffer to store read data + * @param page page number to read + * @return 0 when successfully completed + * -EIO when command timeout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 1, 0); +} + +/** + * Hardware ecc based page write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + */ +static int nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, int oob_required, + int page) +{ + nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1); + return 0; +} + + +/** + * Read raw page data without ecc + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf buffer to store read data + * @param page page number to read + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + return nand_rw_page(mtd, chip, buf, page, 0, 0); +} + +/** + * Raw page write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param buf data buffer + */ +static int nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1); + return 0; +} + +/** + * OOB data read/write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to read + * @param with_ecc 1 to enable ECC, 0 to disable ECC + * @param is_writing 0 for read, 1 for write + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int with_ecc, int is_writing) +{ + u32 reg_val; + int tag_size; + struct nand_oobfree *free = chip->ecc.layout->oobfree; + struct nand_drv *info; + unsigned int bbflags; + struct bounce_buffer bbstate_oob; + + if (((int)chip->oob_poi) & 0x03) + return -EINVAL; + info = (struct nand_drv *)nand_get_controller_data(chip); + if (set_bus_width_page_size(mtd, &info->config, ®_val)) + return -EINVAL; + + stop_command(info->reg); + + /* Set ECC selection */ + tag_size = mtd->oobsize; + if (with_ecc) + reg_val |= CFG_ECC_EN_TAG_ENABLE; + else + reg_val |= (CFG_ECC_EN_TAG_DISABLE); + + reg_val |= ((tag_size - 1) | + CFG_SKIP_SPARE_DISABLE | + CFG_HW_ECC_CORRECTION_DISABLE | + CFG_HW_ECC_DISABLE); + writel(reg_val, &info->reg->config); + + if (is_writing && with_ecc) + tag_size -= TAG_ECC_BYTES; + + if (is_writing) + bbflags = GEN_BB_READ; + else + bbflags = GEN_BB_WRITE; + + bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size, + bbflags); + writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr); + + writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); + + writel(tag_size - 1, &info->reg->dma_cfg_b); + + nand_clear_interrupt_status(info->reg); + + reg_val = CMD_CLE | CMD_ALE + | CMD_SEC_CMD + | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) + | CMD_B_VALID + | CMD_CE0; + if (!is_writing) + reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); + else + reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); + writel(reg_val, &info->reg->command); + + /* Setup DMA engine */ + reg_val = DMA_MST_CTRL_GO_ENABLE + | DMA_MST_CTRL_BURST_8WORDS + | DMA_MST_CTRL_EN_B_ENABLE; + if (!is_writing) + reg_val |= DMA_MST_CTRL_DIR_READ; + else + reg_val |= DMA_MST_CTRL_DIR_WRITE; + + writel(reg_val, &info->reg->dma_mst_ctrl); + + start_command(info->reg); + + if (!nand_waitfor_cmd_completion(info->reg)) { + if (!is_writing) + printf("Read OOB of Page 0x%X timeout\n", page); + else + printf("Write OOB of Page 0x%X timeout\n", page); + return -EIO; + } + + bounce_buffer_stop(&bbstate_oob); + + if (with_ecc && !is_writing) { + reg_val = (u32)check_ecc_error(info->reg, 0, 0, + (u8 *)(chip->oob_poi + free->offset), + chip->ecc.layout->oobavail); + if (reg_val & ECC_TAG_ERROR) + printf("Read OOB of Page 0x%X tag ECC error\n", page); + } + return 0; +} + +/** + * OOB data read function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to read + */ +static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + nand_rw_oob(mtd, chip, page, 0, 0); + return 0; +} + +/** + * OOB data write function + * + * @param mtd mtd info structure + * @param chip nand chip info structure + * @param page page number to write + * @return 0 when successfully completed + * -EINVAL when chip->oob_poi is not double-word aligned + * -EIO when command timeout + */ +static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + return nand_rw_oob(mtd, chip, page, 0, 1); +} + +/** + * Set up NAND memory timings according to the provided parameters + * + * @param timing Timing parameters + * @param reg NAND controller register address + */ +static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT], + struct nand_ctlr *reg) +{ + u32 reg_val, clk_rate, clk_period, time_val; + + clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH, + CLOCK_ID_PERIPH) / 1000000; + clk_period = 1000 / clk_rate; + reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << + TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWB] / clk_period) << + TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK; + time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period; + if (time_val > 2) + reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) & + TIMING_TCR_TAR_TRR_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) << + TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK; + time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period; + if (time_val > 1) + reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) & + TIMING_TCS_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWH] / clk_period) << + TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TWP] / clk_period) << + TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK; + reg_val |= ((timing[FDT_NAND_TRH] / clk_period) << + TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK; + reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << + TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK; + writel(reg_val, ®->timing); + + reg_val = 0; + time_val = timing[FDT_NAND_TADL] / clk_period; + if (time_val > 2) + reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK; + writel(reg_val, ®->timing2); +} + +/** + * Decode NAND parameters from the device tree + * + * @param dev Driver model device + * @param config Device tree NAND configuration + * @return 0 if ok, -ve on error (FDT_ERR_...) + */ +static int fdt_decode_nand(struct udevice *dev, struct fdt_nand *config) +{ + int err; + + config->reg = (struct nand_ctlr *)dev_read_addr(dev); + config->enabled = dev_read_enabled(dev); + config->width = dev_read_u32_default(dev, "nvidia,nand-width", 8); + err = gpio_request_by_name(dev, "nvidia,wp-gpios", 0, &config->wp_gpio, + GPIOD_IS_OUT); + if (err) + return err; + err = dev_read_u32_array(dev, "nvidia,timing", config->timing, + FDT_NAND_TIMING_COUNT); + if (err < 0) + return err; + + return 0; +} + +static int tegra_probe(struct udevice *dev) +{ + struct tegra_nand_info *tegra = dev_get_priv(dev); + struct nand_chip *nand = &tegra->nand_chip; + struct nand_drv *info = &tegra->nand_ctrl; + struct fdt_nand *config = &info->config; + struct mtd_info *our_mtd; + int ret; + + if (fdt_decode_nand(dev, config)) { + printf("Could not decode nand-flash in device tree\n"); + return -1; + } + if (!config->enabled) + return -1; + info->reg = config->reg; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &eccoob; + + nand->options = LP_OPTIONS; + nand->cmdfunc = nand_command; + nand->read_byte = read_byte; + nand->read_buf = read_buf; + nand->ecc.read_page = nand_read_page_hwecc; + nand->ecc.write_page = nand_write_page_hwecc; + nand->ecc.read_page_raw = nand_read_page_raw; + nand->ecc.write_page_raw = nand_write_page_raw; + nand->ecc.read_oob = nand_read_oob; + nand->ecc.write_oob = nand_write_oob; + nand->ecc.strength = 1; + nand->select_chip = nand_select_chip; + nand->dev_ready = nand_dev_ready; + nand_set_controller_data(nand, &tegra->nand_ctrl); + + /* Disable subpage writes as we do not provide ecc->hwctl */ + nand->options |= NAND_NO_SUBPAGE_WRITE; + + /* Adjust controller clock rate */ + clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000); + + /* Adjust timing for NAND device */ + setup_timing(config->timing, info->reg); + + dm_gpio_set_value(&config->wp_gpio, 1); + + our_mtd = nand_to_mtd(nand); + ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); + if (ret) + return ret; + + nand->ecc.size = our_mtd->writesize; + nand->ecc.bytes = our_mtd->oobsize; + + ret = nand_scan_tail(our_mtd); + if (ret) + return ret; + + ret = nand_register(0, our_mtd); + if (ret) { + dev_err(dev, "Failed to register MTD: %d\n", ret); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(tegra_nand) = { + .name = "tegra-nand", + .id = UCLASS_MTD, + .of_match = tegra_nand_dt_ids, + .probe = tegra_probe, + .priv_auto_alloc_size = sizeof(struct tegra_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(tegra_nand), &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize %s. (error %d)\n", dev->name, + ret); +} diff --git a/drivers/mtd/nand/raw/tegra_nand.h b/drivers/mtd/nand/raw/tegra_nand.h new file mode 100644 index 0000000000..7740160661 --- /dev/null +++ b/drivers/mtd/nand/raw/tegra_nand.h @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2011 NVIDIA Corporation + */ + +/* register offset */ +#define COMMAND_0 0x00 +#define CMD_GO (1 << 31) +#define CMD_CLE (1 << 30) +#define CMD_ALE (1 << 29) +#define CMD_PIO (1 << 28) +#define CMD_TX (1 << 27) +#define CMD_RX (1 << 26) +#define CMD_SEC_CMD (1 << 25) +#define CMD_AFT_DAT_MASK (1 << 24) +#define CMD_AFT_DAT_DISABLE 0 +#define CMD_AFT_DAT_ENABLE (1 << 24) +#define CMD_TRANS_SIZE_SHIFT 20 +#define CMD_TRANS_SIZE_PAGE 8 +#define CMD_A_VALID (1 << 19) +#define CMD_B_VALID (1 << 18) +#define CMD_RD_STATUS_CHK (1 << 17) +#define CMD_R_BSY_CHK (1 << 16) +#define CMD_CE7 (1 << 15) +#define CMD_CE6 (1 << 14) +#define CMD_CE5 (1 << 13) +#define CMD_CE4 (1 << 12) +#define CMD_CE3 (1 << 11) +#define CMD_CE2 (1 << 10) +#define CMD_CE1 (1 << 9) +#define CMD_CE0 (1 << 8) +#define CMD_CLE_BYTE_SIZE_SHIFT 4 +enum { + CMD_CLE_BYTES1 = 0, + CMD_CLE_BYTES2, + CMD_CLE_BYTES3, + CMD_CLE_BYTES4, +}; +#define CMD_ALE_BYTE_SIZE_SHIFT 0 +enum { + CMD_ALE_BYTES1 = 0, + CMD_ALE_BYTES2, + CMD_ALE_BYTES3, + CMD_ALE_BYTES4, + CMD_ALE_BYTES5, + CMD_ALE_BYTES6, + CMD_ALE_BYTES7, + CMD_ALE_BYTES8 +}; + +#define STATUS_0 0x04 +#define STATUS_RBSY0 (1 << 8) + +#define ISR_0 0x08 +#define ISR_IS_CMD_DONE (1 << 5) +#define ISR_IS_ECC_ERR (1 << 4) + +#define IER_0 0x0C + +#define CFG_0 0x10 +#define CFG_HW_ECC_MASK (1 << 31) +#define CFG_HW_ECC_DISABLE 0 +#define CFG_HW_ECC_ENABLE (1 << 31) +#define CFG_HW_ECC_SEL_MASK (1 << 30) +#define CFG_HW_ECC_SEL_HAMMING 0 +#define CFG_HW_ECC_SEL_RS (1 << 30) +#define CFG_HW_ECC_CORRECTION_MASK (1 << 29) +#define CFG_HW_ECC_CORRECTION_DISABLE 0 +#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29) +#define CFG_PIPELINE_EN_MASK (1 << 28) +#define CFG_PIPELINE_EN_DISABLE 0 +#define CFG_PIPELINE_EN_ENABLE (1 << 28) +#define CFG_ECC_EN_TAG_MASK (1 << 27) +#define CFG_ECC_EN_TAG_DISABLE 0 +#define CFG_ECC_EN_TAG_ENABLE (1 << 27) +#define CFG_TVALUE_MASK (3 << 24) +enum { + CFG_TVAL4 = 0 << 24, + CFG_TVAL6 = 1 << 24, + CFG_TVAL8 = 2 << 24 +}; +#define CFG_SKIP_SPARE_MASK (1 << 23) +#define CFG_SKIP_SPARE_DISABLE 0 +#define CFG_SKIP_SPARE_ENABLE (1 << 23) +#define CFG_COM_BSY_MASK (1 << 22) +#define CFG_COM_BSY_DISABLE 0 +#define CFG_COM_BSY_ENABLE (1 << 22) +#define CFG_BUS_WIDTH_MASK (1 << 21) +#define CFG_BUS_WIDTH_8BIT 0 +#define CFG_BUS_WIDTH_16BIT (1 << 21) +#define CFG_LPDDR1_MODE_MASK (1 << 20) +#define CFG_LPDDR1_MODE_DISABLE 0 +#define CFG_LPDDR1_MODE_ENABLE (1 << 20) +#define CFG_EDO_MODE_MASK (1 << 19) +#define CFG_EDO_MODE_DISABLE 0 +#define CFG_EDO_MODE_ENABLE (1 << 19) +#define CFG_PAGE_SIZE_SEL_MASK (7 << 16) +enum { + CFG_PAGE_SIZE_256 = 0 << 16, + CFG_PAGE_SIZE_512 = 1 << 16, + CFG_PAGE_SIZE_1024 = 2 << 16, + CFG_PAGE_SIZE_2048 = 3 << 16, + CFG_PAGE_SIZE_4096 = 4 << 16 +}; +#define CFG_SKIP_SPARE_SEL_MASK (3 << 14) +enum { + CFG_SKIP_SPARE_SEL_4 = 0 << 14, + CFG_SKIP_SPARE_SEL_8 = 1 << 14, + CFG_SKIP_SPARE_SEL_12 = 2 << 14, + CFG_SKIP_SPARE_SEL_16 = 3 << 14 +}; +#define CFG_TAG_BYTE_SIZE_MASK 0x1FF + +#define TIMING_0 0x14 +#define TIMING_TRP_RESP_CNT_SHIFT 28 +#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT) +#define TIMING_TWB_CNT_SHIFT 24 +#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT) +#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20 +#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT) +#define TIMING_TWHR_CNT_SHIFT 16 +#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT) +#define TIMING_TCS_CNT_SHIFT 14 +#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT) +#define TIMING_TWH_CNT_SHIFT 12 +#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT) +#define TIMING_TWP_CNT_SHIFT 8 +#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT) +#define TIMING_TRH_CNT_SHIFT 4 +#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT) +#define TIMING_TRP_CNT_SHIFT 0 +#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT) + +#define RESP_0 0x18 + +#define TIMING2_0 0x1C +#define TIMING2_TADL_CNT_SHIFT 0 +#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT) + +#define CMD_REG1_0 0x20 +#define CMD_REG2_0 0x24 +#define ADDR_REG1_0 0x28 +#define ADDR_REG2_0 0x2C + +#define DMA_MST_CTRL_0 0x30 +#define DMA_MST_CTRL_GO_MASK (1 << 31) +#define DMA_MST_CTRL_GO_DISABLE 0 +#define DMA_MST_CTRL_GO_ENABLE (1 << 31) +#define DMA_MST_CTRL_DIR_MASK (1 << 30) +#define DMA_MST_CTRL_DIR_READ 0 +#define DMA_MST_CTRL_DIR_WRITE (1 << 30) +#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29) +#define DMA_MST_CTRL_PERF_EN_DISABLE 0 +#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29) +#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27) +#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0 +#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27) +#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24 +#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT) +enum { + DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT, + DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT +}; +#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20) +#define DMA_MST_CTRL_EN_A_MASK (1 << 2) +#define DMA_MST_CTRL_EN_A_DISABLE 0 +#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2) +#define DMA_MST_CTRL_EN_B_MASK (1 << 1) +#define DMA_MST_CTRL_EN_B_DISABLE 0 +#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1) + +#define DMA_CFG_A_0 0x34 +#define DMA_CFG_B_0 0x38 +#define FIFO_CTRL_0 0x3C +#define DATA_BLOCK_PTR_0 0x40 +#define TAG_PTR_0 0x44 +#define ECC_PTR_0 0x48 + +#define DEC_STATUS_0 0x4C +#define DEC_STATUS_A_ECC_FAIL (1 << 1) +#define DEC_STATUS_B_ECC_FAIL (1 << 0) + +#define BCH_CONFIG_0 0xCC +#define BCH_CONFIG_BCH_TVALUE_SHIFT 4 +#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT) +enum { + BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT, + BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT +}; +#define BCH_CONFIG_BCH_ECC_MASK (1 << 0) +#define BCH_CONFIG_BCH_ECC_DISABLE 0 +#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0) + +#define BCH_DEC_RESULT_0 0xD0 +#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8) +#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF + +#define BCH_DEC_STATUS_BUF_0 0xD4 +#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000 +#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000 +#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14) +#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13) +#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8) +#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF + +#define LP_OPTIONS 0 + +struct nand_ctlr { + u32 command; /* offset 00h */ + u32 status; /* offset 04h */ + u32 isr; /* offset 08h */ + u32 ier; /* offset 0Ch */ + u32 config; /* offset 10h */ + u32 timing; /* offset 14h */ + u32 resp; /* offset 18h */ + u32 timing2; /* offset 1Ch */ + u32 cmd_reg1; /* offset 20h */ + u32 cmd_reg2; /* offset 24h */ + u32 addr_reg1; /* offset 28h */ + u32 addr_reg2; /* offset 2Ch */ + u32 dma_mst_ctrl; /* offset 30h */ + u32 dma_cfg_a; /* offset 34h */ + u32 dma_cfg_b; /* offset 38h */ + u32 fifo_ctrl; /* offset 3Ch */ + u32 data_block_ptr; /* offset 40h */ + u32 tag_ptr; /* offset 44h */ + u32 resv1; /* offset 48h */ + u32 dec_status; /* offset 4Ch */ + u32 hwstatus_cmd; /* offset 50h */ + u32 hwstatus_mask; /* offset 54h */ + u32 resv2[29]; + u32 bch_config; /* offset CCh */ + u32 bch_dec_result; /* offset D0h */ + u32 bch_dec_status_buf; + /* offset D4h */ +}; diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c new file mode 100644 index 0000000000..619d0403e9 --- /dev/null +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2009-2015 Freescale Semiconductor, Inc. and others + * + * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. + * Ported to U-Boot by Stefan Agner + * Based on RFC driver posted on Kernel Mailing list by Bill Pringlemeir + * Jason ported to M54418TWR and MVFA5. + * Authors: Stefan Agner + * Bill Pringlemeir + * Shaohui Xie + * Jason Jin + * + * Based on original driver mpc5121_nfc.c. + * + * Limitations: + * - Untested on MPC5125 and M54418. + * - DMA and pipelining not used. + * - 2K pages or less. + * - HW ECC: Only 2K page with 64+ OOB. + * - HW ECC: Only 24 and 32-bit error correction implemented. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* Register Offsets */ +#define NFC_FLASH_CMD1 0x3F00 +#define NFC_FLASH_CMD2 0x3F04 +#define NFC_COL_ADDR 0x3F08 +#define NFC_ROW_ADDR 0x3F0c +#define NFC_ROW_ADDR_INC 0x3F14 +#define NFC_FLASH_STATUS1 0x3F18 +#define NFC_FLASH_STATUS2 0x3F1c +#define NFC_CACHE_SWAP 0x3F28 +#define NFC_SECTOR_SIZE 0x3F2c +#define NFC_FLASH_CONFIG 0x3F30 +#define NFC_IRQ_STATUS 0x3F38 + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x1000) + +#define PAGE_2K 0x0800 +#define OOB_64 0x0040 +#define OOB_MAX 0x0100 + +/* + * NFC_CMD2[CODE] values. See section: + * - 31.4.7 Flash Command Code Description, Vybrid manual + * - 23.8.6 Flash Command Sequencer, MPC5125 manual + * + * Briefly these are bitmasks of controller cycles. + */ +#define READ_PAGE_CMD_CODE 0x7EE0 +#define READ_ONFI_PARAM_CMD_CODE 0x4860 +#define PROGRAM_PAGE_CMD_CODE 0x7FC0 +#define ERASE_CMD_CODE 0x4EC0 +#define READ_ID_CMD_CODE 0x4804 +#define RESET_CMD_CODE 0x4040 +#define STATUS_READ_CMD_CODE 0x4068 + +/* NFC ECC mode define */ +#define ECC_BYPASS 0 +#define ECC_45_BYTE 6 +#define ECC_60_BYTE 7 + +/*** Register Mask and bit definitions */ + +/* NFC_FLASH_CMD1 Field */ +#define CMD_BYTE2_MASK 0xFF000000 +#define CMD_BYTE2_SHIFT 24 + +/* NFC_FLASH_CM2 Field */ +#define CMD_BYTE1_MASK 0xFF000000 +#define CMD_BYTE1_SHIFT 24 +#define CMD_CODE_MASK 0x00FFFF00 +#define CMD_CODE_SHIFT 8 +#define BUFNO_MASK 0x00000006 +#define BUFNO_SHIFT 1 +#define START_BIT (1<<0) + +/* NFC_COL_ADDR Field */ +#define COL_ADDR_MASK 0x0000FFFF +#define COL_ADDR_SHIFT 0 + +/* NFC_ROW_ADDR Field */ +#define ROW_ADDR_MASK 0x00FFFFFF +#define ROW_ADDR_SHIFT 0 +#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 +#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 +#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 +#define ROW_ADDR_CHIP_SEL_SHIFT 24 + +/* NFC_FLASH_STATUS2 Field */ +#define STATUS_BYTE1_MASK 0x000000FF + +/* NFC_FLASH_CONFIG Field */ +#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 +#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 +#define CONFIG_ECC_SRAM_REQ_BIT (1<<21) +#define CONFIG_DMA_REQ_BIT (1<<20) +#define CONFIG_ECC_MODE_MASK 0x000E0000 +#define CONFIG_ECC_MODE_SHIFT 17 +#define CONFIG_FAST_FLASH_BIT (1<<16) +#define CONFIG_16BIT (1<<7) +#define CONFIG_BOOT_MODE_BIT (1<<6) +#define CONFIG_ADDR_AUTO_INCR_BIT (1<<5) +#define CONFIG_BUFNO_AUTO_INCR_BIT (1<<4) +#define CONFIG_PAGE_CNT_MASK 0xF +#define CONFIG_PAGE_CNT_SHIFT 0 + +/* NFC_IRQ_STATUS Field */ +#define IDLE_IRQ_BIT (1<<29) +#define IDLE_EN_BIT (1<<20) +#define CMD_DONE_CLEAR_BIT (1<<18) +#define IDLE_CLEAR_BIT (1<<17) + +#define NFC_TIMEOUT (1000) + +/* + * ECC status - seems to consume 8 bytes (double word). The documented + * status byte is located in the lowest byte of the second word (which is + * the 4th or 7th byte depending on endianness). + * Calculate an offset to store the ECC status at the end of the buffer. + */ +#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) + +#define ECC_STATUS 0x4 +#define ECC_STATUS_MASK 0x80 +#define ECC_STATUS_ERR_COUNT 0x3F + +enum vf610_nfc_alt_buf { + ALT_BUF_DATA = 0, + ALT_BUF_ID = 1, + ALT_BUF_STAT = 2, + ALT_BUF_ONFI = 3, +}; + +struct vf610_nfc { + struct nand_chip chip; + void __iomem *regs; + uint buf_offset; + int write_sz; + /* Status and ID are in alternate locations. */ + enum vf610_nfc_alt_buf alt_buf; +}; + +#define mtd_to_nfc(_mtd) nand_get_controller_data(mtd_to_nand(_mtd)) + +#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) +#define ECC_HW_MODE ECC_45_BYTE + +static struct nand_ecclayout vf610_nfc_ecc = { + .eccbytes = 45, + .eccpos = {19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { + {.offset = 2, + .length = 17} } +}; +#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) +#define ECC_HW_MODE ECC_60_BYTE + +static struct nand_ecclayout vf610_nfc_ecc = { + .eccbytes = 60, + .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63 }, + .oobfree = { + {.offset = 2, + .length = 2} } +}; +#endif + +static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + return readl(nfc->regs + reg); +} + +static inline void vf610_nfc_write(struct mtd_info *mtd, uint reg, u32 val) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + writel(val, nfc->regs + reg); +} + +static inline void vf610_nfc_set(struct mtd_info *mtd, uint reg, u32 bits) +{ + vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) | bits); +} + +static inline void vf610_nfc_clear(struct mtd_info *mtd, uint reg, u32 bits) +{ + vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) & ~bits); +} + +static inline void vf610_nfc_set_field(struct mtd_info *mtd, u32 reg, + u32 mask, u32 shift, u32 val) +{ + vf610_nfc_write(mtd, reg, + (vf610_nfc_read(mtd, reg) & (~mask)) | val << shift); +} + +static inline void vf610_nfc_memcpy(void *dst, const void *src, size_t n) +{ + /* + * Use this accessor for the internal SRAM buffers. On the ARM + * Freescale Vybrid SoC it's known that the driver can treat + * the SRAM buffer as if it's memory. Other platform might need + * to treat the buffers differently. + * + * For the time being, use memcpy + */ + memcpy(dst, src, n); +} + +/* Clear flags for upcoming command */ +static inline void vf610_nfc_clear_status(void __iomem *regbase) +{ + void __iomem *reg = regbase + NFC_IRQ_STATUS; + u32 tmp = __raw_readl(reg); + tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; + __raw_writel(tmp, reg); +} + +/* Wait for complete operation */ +static void vf610_nfc_done(struct mtd_info *mtd) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint start; + + /* + * Barrier is needed after this write. This write need + * to be done before reading the next register the first + * time. + * vf610_nfc_set implicates such a barrier by using writel + * to write to the register. + */ + vf610_nfc_set(mtd, NFC_FLASH_CMD2, START_BIT); + + start = get_timer(0); + + while (!(vf610_nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) { + if (get_timer(start) > NFC_TIMEOUT) { + printf("Timeout while waiting for IDLE.\n"); + return; + } + } + vf610_nfc_clear_status(nfc->regs); +} + +static u8 vf610_nfc_get_id(struct mtd_info *mtd, int col) +{ + u32 flash_id; + + if (col < 4) { + flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS1); + flash_id >>= (3 - col) * 8; + } else { + flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS2); + flash_id >>= 24; + } + + return flash_id & 0xff; +} + +static u8 vf610_nfc_get_status(struct mtd_info *mtd) +{ + return vf610_nfc_read(mtd, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; +} + +/* Single command */ +static void vf610_nfc_send_command(void __iomem *regbase, u32 cmd_byte1, + u32 cmd_code) +{ + void __iomem *reg = regbase + NFC_FLASH_CMD2; + u32 tmp; + vf610_nfc_clear_status(regbase); + + tmp = __raw_readl(reg); + tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); + tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; + tmp |= cmd_code << CMD_CODE_SHIFT; + __raw_writel(tmp, reg); +} + +/* Two commands */ +static void vf610_nfc_send_commands(void __iomem *regbase, u32 cmd_byte1, + u32 cmd_byte2, u32 cmd_code) +{ + void __iomem *reg = regbase + NFC_FLASH_CMD1; + u32 tmp; + vf610_nfc_send_command(regbase, cmd_byte1, cmd_code); + + tmp = __raw_readl(reg); + tmp &= ~CMD_BYTE2_MASK; + tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; + __raw_writel(tmp, reg); +} + +static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + if (column != -1) { + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + if (nfc->chip.options & NAND_BUSWIDTH_16) + column = column / 2; + vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, + COL_ADDR_SHIFT, column); + } + if (page != -1) + vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, page); +} + +static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode) +{ + vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); +} + +static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size) +{ + __raw_writel(size, regbase + NFC_SECTOR_SIZE); +} + +/* Send command to NAND chip */ +static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; + + nfc->buf_offset = max(column, 0); + nfc->alt_buf = ALT_BUF_DATA; + + switch (command) { + case NAND_CMD_SEQIN: + /* Use valid column/page from preread... */ + vf610_nfc_addr_cycle(mtd, column, page); + nfc->buf_offset = 0; + + /* + * SEQIN => data => PAGEPROG sequence is done by the controller + * hence we do not need to issue the command here... + */ + return; + case NAND_CMD_PAGEPROG: + trfr_sz += nfc->write_sz; + vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN, + command, PROGRAM_PAGE_CMD_CODE); + break; + + case NAND_CMD_RESET: + vf610_nfc_transfer_size(nfc->regs, 0); + vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE); + break; + + case NAND_CMD_READOOB: + trfr_sz += mtd->oobsize; + column = mtd->writesize; + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(mtd, column, page); + vf610_nfc_ecc_mode(mtd, ECC_BYPASS); + break; + + case NAND_CMD_READ0: + trfr_sz += mtd->writesize + mtd->oobsize; + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); + vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(mtd, column, page); + break; + + case NAND_CMD_PARAM: + nfc->alt_buf = ALT_BUF_ONFI; + trfr_sz = 3 * sizeof(struct nand_onfi_params); + vf610_nfc_transfer_size(nfc->regs, trfr_sz); + vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM, + READ_ONFI_PARAM_CMD_CODE); + vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, column); + vf610_nfc_ecc_mode(mtd, ECC_BYPASS); + break; + + case NAND_CMD_ERASE1: + vf610_nfc_transfer_size(nfc->regs, 0); + vf610_nfc_send_commands(nfc->regs, command, + NAND_CMD_ERASE2, ERASE_CMD_CODE); + vf610_nfc_addr_cycle(mtd, column, page); + break; + + case NAND_CMD_READID: + nfc->alt_buf = ALT_BUF_ID; + nfc->buf_offset = 0; + vf610_nfc_transfer_size(nfc->regs, 0); + vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE); + vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, column); + break; + + case NAND_CMD_STATUS: + nfc->alt_buf = ALT_BUF_STAT; + vf610_nfc_transfer_size(nfc->regs, 0); + vf610_nfc_send_command(nfc->regs, command, STATUS_READ_CMD_CODE); + break; + default: + return; + } + + vf610_nfc_done(mtd); + + nfc->write_sz = 0; +} + +/* Read data from NFC buffers */ +static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + + /* Alternate buffers are only supported through read_byte */ + if (nfc->alt_buf) + return; + + vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + + nfc->buf_offset += len; +} + +/* Write data to NFC buffers */ +static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->buf_offset; + uint l; + + l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); + vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); + + nfc->write_sz += l; + nfc->buf_offset += l; +} + +/* Read byte from NFC buffers */ +static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u8 tmp; + uint c = nfc->buf_offset; + + switch (nfc->alt_buf) { + case ALT_BUF_ID: + tmp = vf610_nfc_get_id(mtd, c); + break; + case ALT_BUF_STAT: + tmp = vf610_nfc_get_status(mtd); + break; +#ifdef __LITTLE_ENDIAN + case ALT_BUF_ONFI: + /* Reverse byte since the controller uses big endianness */ + c = nfc->buf_offset ^ 0x3; + /* fall-through */ +#endif + default: + tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); + break; + } + nfc->buf_offset++; + return tmp; +} + +/* Read word from NFC buffers */ +static u16 vf610_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + return tmp; +} + +/* If not provided, upper layers apply a fixed delay. */ +static int vf610_nfc_dev_ready(struct mtd_info *mtd) +{ + /* NFC handles R/B internally; always ready. */ + return 1; +} + +/* + * This function supports Vybrid only (MPC5125 would have full RB and four CS) + */ +static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef CONFIG_VF610 + u32 tmp = vf610_nfc_read(mtd, NFC_ROW_ADDR); + tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); + + if (chip >= 0) { + tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; + tmp |= (1 << chip) << ROW_ADDR_CHIP_SEL_SHIFT; + } + + vf610_nfc_write(mtd, NFC_ROW_ADDR, tmp); +#endif +} + +/* Count the number of 0's in buff upto max_bits */ +static inline int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + uint32_t *buff32 = (uint32_t *)buff; + int k, written_bits = 0; + + for (k = 0; k < (size / 4); k++) { + written_bits += hweight32(~buff32[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, + uint8_t *oob, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; + u8 ecc_status; + u8 ecc_count; + int flips; + int flips_threshold = nfc->chip.ecc.strength / 2; + + ecc_status = vf610_nfc_read(mtd, ecc_status_off) & 0xff; + ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; + + if (!(ecc_status & ECC_STATUS_MASK)) + return ecc_count; + + /* Read OOB without ECC unit enabled */ + vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); + vf610_nfc_read_buf(mtd, oob, mtd->oobsize); + + /* + * On an erased page, bit count (including OOB) should be zero or + * at least less then half of the ECC strength. + */ + flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold); + flips += count_written_bits(oob, mtd->oobsize, flips_threshold); + + if (unlikely(flips > flips_threshold)) + return -EINVAL; + + /* Erased page. */ + memset(dat, 0xff, nfc->chip.ecc.size); + memset(oob, 0xff, mtd->oobsize); + return flips; +} + +static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) +{ + int eccsize = chip->ecc.size; + int stat; + + vf610_nfc_read_buf(mtd, buf, eccsize); + if (oob_required) + vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); + + if (stat < 0) { + mtd->ecc_stats.failed++; + return 0; + } else { + mtd->ecc_stats.corrected += stat; + return stat; + } +} + +/* + * ECC will be calculated automatically + */ +static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required, int page) +{ + struct vf610_nfc *nfc = mtd_to_nfc(mtd); + + vf610_nfc_write_buf(mtd, buf, mtd->writesize); + if (oob_required) + vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + /* Always write whole page including OOB due to HW ECC */ + nfc->write_sz = mtd->writesize + mtd->oobsize; + + return 0; +} + +struct vf610_nfc_config { + int hardware_ecc; + int width; + int flash_bbt; +}; + +static int vf610_nfc_nand_init(int devnum, void __iomem *addr) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + struct vf610_nfc *nfc; + int err = 0; + struct vf610_nfc_config cfg = { + .hardware_ecc = 1, +#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT + .width = 16, +#else + .width = 8, +#endif + .flash_bbt = 1, + }; + + nfc = malloc(sizeof(*nfc)); + if (!nfc) { + printf(KERN_ERR "%s: Memory exhausted!\n", __func__); + return -ENOMEM; + } + + chip = &nfc->chip; + nfc->regs = addr; + + mtd = nand_to_mtd(chip); + nand_set_controller_data(chip, nfc); + + if (cfg.width == 16) + chip->options |= NAND_BUSWIDTH_16; + + chip->dev_ready = vf610_nfc_dev_ready; + chip->cmdfunc = vf610_nfc_command; + chip->read_byte = vf610_nfc_read_byte; + chip->read_word = vf610_nfc_read_word; + chip->read_buf = vf610_nfc_read_buf; + chip->write_buf = vf610_nfc_write_buf; + chip->select_chip = vf610_nfc_select_chip; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + chip->ecc.size = PAGE_2K; + + /* Set configuration register. */ + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); + vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); + vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); + + /* Disable virtual pages, only one elementary transfer unit */ + vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, + CONFIG_PAGE_CNT_SHIFT, 1); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) { + err = -ENXIO; + goto error; + } + + if (cfg.width == 16) + vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); + + /* Bad block options. */ + if (cfg.flash_bbt) + chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB | + NAND_BBT_CREATE; + + /* Single buffer only, max 256 OOB minus ECC status */ + if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { + dev_err(nfc->dev, "Unsupported flash page size\n"); + err = -ENXIO; + goto error; + } + + if (cfg.hardware_ecc) { + if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { + dev_err(nfc->dev, "Unsupported flash with hwecc\n"); + err = -ENXIO; + goto error; + } + + if (chip->ecc.size != mtd->writesize) { + dev_err(nfc->dev, "ecc size: %d\n", chip->ecc.size); + dev_err(nfc->dev, "Step size needs to be page size\n"); + err = -ENXIO; + goto error; + } + + /* Current HW ECC layouts only use 64 bytes of OOB */ + if (mtd->oobsize > 64) + mtd->oobsize = 64; + + /* propagate ecc.layout to mtd_info */ + mtd->ecclayout = chip->ecc.layout; + chip->ecc.read_page = vf610_nfc_read_page; + chip->ecc.write_page = vf610_nfc_write_page; + chip->ecc.mode = NAND_ECC_HW; + + chip->ecc.size = PAGE_2K; + chip->ecc.layout = &vf610_nfc_ecc; +#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) + chip->ecc.strength = 24; + chip->ecc.bytes = 45; +#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) + chip->ecc.strength = 32; + chip->ecc.bytes = 60; +#endif + + /* Set ECC_STATUS offset */ + vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_ECC_SRAM_ADDR_MASK, + CONFIG_ECC_SRAM_ADDR_SHIFT, + ECC_SRAM_ADDR >> 3); + + /* Enable ECC status in SRAM */ + vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); + } + + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + return err; + + err = nand_register(devnum, mtd); + if (err) + return err; + + return 0; + +error: + return err; +} + +void board_nand_init(void) +{ + int err = vf610_nfc_nand_init(0, (void __iomem *)CONFIG_SYS_NAND_BASE); + if (err) + printf("VF610 NAND init failed (err %d)\n", err); +} diff --git a/drivers/mtd/nand/raw/zynq_nand.c b/drivers/mtd/nand/raw/zynq_nand.c new file mode 100644 index 0000000000..e932a58bf6 --- /dev/null +++ b/drivers/mtd/nand/raw/zynq_nand.c @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Xilinx, Inc. + * + * Xilinx Zynq NAND Flash Controller Driver + * This driver is based on plat_nand.c and mxc_nand.c drivers + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The NAND flash driver defines */ +#define ZYNQ_NAND_CMD_PHASE 1 +#define ZYNQ_NAND_DATA_PHASE 2 +#define ZYNQ_NAND_ECC_SIZE 512 +#define ZYNQ_NAND_SET_OPMODE_8BIT (0 << 0) +#define ZYNQ_NAND_SET_OPMODE_16BIT (1 << 0) +#define ZYNQ_NAND_ECC_STATUS (1 << 6) +#define ZYNQ_MEMC_CLRCR_INT_CLR1 (1 << 4) +#define ZYNQ_MEMC_SR_RAW_INT_ST1 (1 << 6) +#define ZYNQ_MEMC_SR_INT_ST1 (1 << 4) +#define ZYNQ_MEMC_NAND_ECC_MODE_MASK 0xC + +/* Flash memory controller operating parameters */ +#define ZYNQ_NAND_CLR_CONFIG ((0x1 << 1) | /* Disable interrupt */ \ + (0x1 << 4) | /* Clear interrupt */ \ + (0x1 << 6)) /* Disable ECC interrupt */ + +#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS + +/* Assuming 50MHz clock (20ns cycle time) and 3V operation */ +#define ZYNQ_NAND_SET_CYCLES ((0x2 << 20) | /* t_rr from nand_cycles */ \ + (0x2 << 17) | /* t_ar from nand_cycles */ \ + (0x1 << 14) | /* t_clr from nand_cycles */ \ + (0x3 << 11) | /* t_wp from nand_cycles */ \ + (0x2 << 8) | /* t_rea from nand_cycles */ \ + (0x5 << 4) | /* t_wc from nand_cycles */ \ + (0x5 << 0)) /* t_rc from nand_cycles */ +#endif + + +#define ZYNQ_NAND_DIRECT_CMD ((0x4 << 23) | /* Chip 0 from interface 1 */ \ + (0x2 << 21)) /* UpdateRegs operation */ + +#define ZYNQ_NAND_ECC_CONFIG ((0x1 << 2) | /* ECC available on APB */ \ + (0x1 << 4) | /* ECC read at end of page */ \ + (0x0 << 5)) /* No Jumping */ + +#define ZYNQ_NAND_ECC_CMD1 ((0x80) | /* Write command */ \ + (0x00 << 8) | /* Read command */ \ + (0x30 << 16) | /* Read End command */ \ + (0x1 << 24)) /* Read End command calid */ + +#define ZYNQ_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ + (0x05 << 8) | /* Read col change cmd */ \ + (0xE0 << 16) | /* Read col change end cmd */ \ + (0x1 << 24)) /* Read col change + end cmd valid */ +/* AXI Address definitions */ +#define START_CMD_SHIFT 3 +#define END_CMD_SHIFT 11 +#define END_CMD_VALID_SHIFT 20 +#define ADDR_CYCLES_SHIFT 21 +#define CLEAR_CS_SHIFT 21 +#define ECC_LAST_SHIFT 10 +#define COMMAND_PHASE (0 << 19) +#define DATA_PHASE (1 << 19) +#define ONDIE_ECC_FEATURE_ADDR 0x90 +#define ONDIE_ECC_FEATURE_ENABLE 0x08 + +#define ZYNQ_NAND_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */ +#define ZYNQ_NAND_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */ + +/* ECC block registers bit position and bit mask */ +#define ZYNQ_NAND_ECC_BUSY (1 << 6) /* ECC block is busy */ +#define ZYNQ_NAND_ECC_MASK 0x00FFFFFF /* ECC value mask */ + +#define ZYNQ_NAND_ROW_ADDR_CYCL_MASK 0x0F +#define ZYNQ_NAND_COL_ADDR_CYCL_MASK 0xF0 + +#define ZYNQ_NAND_MIO_NUM_NAND_8BIT 13 +#define ZYNQ_NAND_MIO_NUM_NAND_16BIT 8 + +enum zynq_nand_bus_width { + NAND_BW_UNKNOWN = -1, + NAND_BW_8BIT, + NAND_BW_16BIT, +}; + +#ifndef NAND_CMD_LOCK_TIGHT +#define NAND_CMD_LOCK_TIGHT 0x2c +#endif + +#ifndef NAND_CMD_LOCK_STATUS +#define NAND_CMD_LOCK_STATUS 0x7a +#endif + +/* SMC register set */ +struct zynq_nand_smc_regs { + u32 csr; /* 0x00 */ + u32 reserved0[2]; + u32 cfr; /* 0x0C */ + u32 dcr; /* 0x10 */ + u32 scr; /* 0x14 */ + u32 sor; /* 0x18 */ + u32 reserved1[249]; + u32 esr; /* 0x400 */ + u32 emcr; /* 0x404 */ + u32 emcmd1r; /* 0x408 */ + u32 emcmd2r; /* 0x40C */ + u32 reserved2[2]; + u32 eval0r; /* 0x418 */ +}; +#define zynq_nand_smc_base ((struct zynq_nand_smc_regs __iomem *)\ + ZYNQ_SMC_BASEADDR) + +/* + * struct zynq_nand_info - Defines the NAND flash driver instance + * @parts: Pointer to the mtd_partition structure + * @nand_base: Virtual address of the NAND flash device + * @end_cmd_pending: End command is pending + * @end_cmd: End command + */ +struct zynq_nand_info { + void __iomem *nand_base; + u8 end_cmd_pending; + u8 end_cmd; +}; + +/* + * struct zynq_nand_command_format - Defines NAND flash command format + * @start_cmd: First cycle command (Start command) + * @end_cmd: Second cycle command (Last command) + * @addr_cycles: Number of address cycles required to send the address + * @end_cmd_valid: The second cycle command is valid for cmd or data phase + */ +struct zynq_nand_command_format { + u8 start_cmd; + u8 end_cmd; + u8 addr_cycles; + u8 end_cmd_valid; +}; + +/* The NAND flash operations command format */ +static const struct zynq_nand_command_format zynq_nand_commands[] = { + {NAND_CMD_READ0, NAND_CMD_READSTART, 5, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_READID, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_STATUS, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, ZYNQ_NAND_DATA_PHASE}, + {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, 0}, + {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, ZYNQ_NAND_CMD_PHASE}, + {NAND_CMD_RESET, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_PARAM, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, 0}, + {NAND_CMD_LOCK, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_LOCK_TIGHT, NAND_CMD_NONE, 0, 0}, + {NAND_CMD_UNLOCK1, NAND_CMD_NONE, 3, 0}, + {NAND_CMD_UNLOCK2, NAND_CMD_NONE, 3, 0}, + {NAND_CMD_LOCK_STATUS, NAND_CMD_NONE, 3, 0}, + {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0}, + /* Add all the flash commands supported by the flash device */ +}; + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout nand_oob_16 = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + { .offset = 8, .length = 8 } + } +}; + +static struct nand_ecclayout nand_oob_64 = { + .eccbytes = 12, + .eccpos = { + 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63}, + .oobfree = { + { .offset = 2, .length = 50 } + } +}; + +static struct nand_ecclayout ondie_nand_oob_64 = { + .eccbytes = 32, + + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 24, 25, 26, 27, 28, 29, 30, 31, + 40, 41, 42, 43, 44, 45, 46, 47, + 56, 57, 58, 59, 60, 61, 62, 63 + }, + + .oobfree = { + { .offset = 4, .length = 4 }, + { .offset = 20, .length = 4 }, + { .offset = 36, .length = 4 }, + { .offset = 52, .length = 4 } + } +}; + +/* bbt decriptors for chips with on-die ECC and + chips with 64-byte OOB */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 4, + .len = 4, + .veroffs = 20, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/* + * zynq_nand_waitfor_ecc_completion - Wait for ECC completion + * + * returns: status for command completion, -1 for Timeout + */ +static int zynq_nand_waitfor_ecc_completion(void) +{ + unsigned long timeout; + u32 status; + + /* Wait max 10us */ + timeout = 10; + status = readl(&zynq_nand_smc_base->esr); + while (status & ZYNQ_NAND_ECC_BUSY) { + status = readl(&zynq_nand_smc_base->esr); + if (timeout == 0) + return -1; + timeout--; + udelay(1); + } + + return status; +} + +/* + * zynq_nand_init_nand_flash - Initialize NAND controller + * @option: Device property flags + * + * This function initializes the NAND flash interface on the NAND controller. + * + * returns: 0 on success or error value on failure + */ +static int zynq_nand_init_nand_flash(int option) +{ + u32 status; + + /* disable interrupts */ + writel(ZYNQ_NAND_CLR_CONFIG, &zynq_nand_smc_base->cfr); +#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS + /* Initialize the NAND interface by setting cycles and operation mode */ + writel(ZYNQ_NAND_SET_CYCLES, &zynq_nand_smc_base->scr); +#endif + if (option & NAND_BUSWIDTH_16) + writel(ZYNQ_NAND_SET_OPMODE_16BIT, &zynq_nand_smc_base->sor); + else + writel(ZYNQ_NAND_SET_OPMODE_8BIT, &zynq_nand_smc_base->sor); + + writel(ZYNQ_NAND_DIRECT_CMD, &zynq_nand_smc_base->dcr); + + /* Wait till the ECC operation is complete */ + status = zynq_nand_waitfor_ecc_completion(); + if (status < 0) { + printf("%s: Timeout\n", __func__); + return status; + } + + /* Set the command1 and command2 register */ + writel(ZYNQ_NAND_ECC_CMD1, &zynq_nand_smc_base->emcmd1r); + writel(ZYNQ_NAND_ECC_CMD2, &zynq_nand_smc_base->emcmd2r); + + return 0; +} + +/* + * zynq_nand_calculate_hwecc - Calculate Hardware ECC + * @mtd: Pointer to the mtd_info structure + * @data: Pointer to the page data + * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored + * + * This function retrieves the Hardware ECC data from the controller and returns + * ECC data back to the MTD subsystem. + * + * returns: 0 on success or error value on failure + */ +static int zynq_nand_calculate_hwecc(struct mtd_info *mtd, const u8 *data, + u8 *ecc_code) +{ + u32 ecc_value = 0; + u8 ecc_reg, ecc_byte; + u32 ecc_status; + + /* Wait till the ECC operation is complete */ + ecc_status = zynq_nand_waitfor_ecc_completion(); + if (ecc_status < 0) { + printf("%s: Timeout\n", __func__); + return ecc_status; + } + + for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) { + /* Read ECC value for each block */ + ecc_value = readl(&zynq_nand_smc_base->eval0r + ecc_reg); + + /* Get the ecc status from ecc read value */ + ecc_status = (ecc_value >> 24) & 0xFF; + + /* ECC value valid */ + if (ecc_status & ZYNQ_NAND_ECC_STATUS) { + for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) { + /* Copy ECC bytes to MTD buffer */ + *ecc_code = ecc_value & 0xFF; + ecc_value = ecc_value >> 8; + ecc_code++; + } + } else { + debug("%s: ecc status failed\n", __func__); + } + } + + return 0; +} + +/* + * onehot - onehot function + * @value: value to check for onehot + * + * This function checks whether a value is onehot or not. + * onehot is if and only if one bit is set. + * + * FIXME: Try to move this in common.h + */ +static bool onehot(unsigned short value) +{ + bool onehot; + + onehot = value && !(value & (value - 1)); + return onehot; +} + +/* + * zynq_nand_correct_data - ECC correction function + * @mtd: Pointer to the mtd_info structure + * @buf: Pointer to the page data + * @read_ecc: Pointer to the ECC value read from spare data area + * @calc_ecc: Pointer to the calculated ECC value + * + * This function corrects the ECC single bit errors & detects 2-bit errors. + * + * returns: 0 if no ECC errors found + * 1 if single bit error found and corrected. + * -1 if multiple ECC errors found. + */ +static int zynq_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + unsigned char bit_addr; + unsigned int byte_addr; + unsigned short ecc_odd, ecc_even; + unsigned short read_ecc_lower, read_ecc_upper; + unsigned short calc_ecc_lower, calc_ecc_upper; + + read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff; + read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; + + calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff; + calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; + + ecc_odd = read_ecc_lower ^ calc_ecc_lower; + ecc_even = read_ecc_upper ^ calc_ecc_upper; + + if ((ecc_odd == 0) && (ecc_even == 0)) + return 0; /* no error */ + + if (ecc_odd == (~ecc_even & 0xfff)) { + /* bits [11:3] of error code is byte offset */ + byte_addr = (ecc_odd >> 3) & 0x1ff; + /* bits [2:0] of error code is bit offset */ + bit_addr = ecc_odd & 0x7; + /* Toggling error bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + } + + if (onehot(ecc_odd | ecc_even)) + return 1; /* one error in parity */ + + return -1; /* Uncorrectable error */ +} + +/* + * zynq_nand_read_oob - [REPLACABLE] the most common OOB data read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int zynq_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + unsigned long data_phase_addr = 0; + int data_width = 4; + u8 *p; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + + p = chip->oob_poi; + chip->read_buf(mtd, p, (mtd->oobsize - data_width)); + p += mtd->oobsize - data_width; + + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->read_buf(mtd, p, data_width); + + return 0; +} + +/* + * zynq_nand_write_oob - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +static int zynq_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0, data_width = 4; + const u8 *buf = chip->oob_poi; + unsigned long data_phase_addr = 0; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + + chip->write_buf(mtd, buf, (mtd->oobsize - data_width)); + buf += mtd->oobsize - data_width; + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + chip->write_buf(mtd, buf, data_width); + + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/* + * zynq_nand_read_page_raw - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to read + */ +static int zynq_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + unsigned long data_width = 4; + unsigned long data_phase_addr = 0; + u8 *p; + + chip->read_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->read_buf(mtd, p, (mtd->oobsize - data_width)); + p += (mtd->oobsize - data_width); + + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + chip->read_buf(mtd, p, data_width); + return 0; +} + +static int zynq_nand_read_page_raw_nooob(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + chip->read_buf(mtd, buf, mtd->writesize); + return 0; +} + +static int zynq_nand_read_subpage_raw(struct mtd_info *mtd, + struct nand_chip *chip, u32 data_offs, + u32 readlen, u8 *buf, int page) +{ + if (data_offs != 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1); + buf += data_offs; + } + chip->read_buf(mtd, buf, readlen); + + return 0; +} + +/* + * zynq_nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int zynq_nand_write_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + unsigned long data_width = 4; + unsigned long data_phase_addr = 0; + u8 *p; + + chip->write_buf(mtd, buf, mtd->writesize); + + p = chip->oob_poi; + chip->write_buf(mtd, p, (mtd->oobsize - data_width)); + p += (mtd->oobsize - data_width); + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + + chip->write_buf(mtd, p, data_width); + + return 0; +} + +/* + * nand_write_page_hwecc - Hardware ECC based page write function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the data buffer + * @oob_required: must write chip->oob_poi to OOB + * + * This functions writes data and hardware generated ECC values in to the page. + */ +static int zynq_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + int i, eccsteps, eccsize = chip->ecc.size; + u8 *ecc_calc = chip->buffers->ecccalc; + const u8 *p = buf; + u32 *eccpos = chip->ecc.layout->eccpos; + unsigned long data_phase_addr = 0; + unsigned long data_width = 4; + u8 *oob_ptr; + + for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { + chip->write_buf(mtd, p, eccsize); + p += eccsize; + } + chip->write_buf(mtd, p, (eccsize - data_width)); + p += eccsize - data_width; + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long) chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + chip->write_buf(mtd, p, data_width); + + /* Wait for ECC to be calculated and read the error values */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]); + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + + /* Write the spare area with ECC bytes */ + oob_ptr = chip->oob_poi; + chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); + + data_phase_addr = (unsigned long)chip->IO_ADDR_W; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + data_phase_addr |= (1 << END_CMD_VALID_SHIFT); + chip->IO_ADDR_W = (void __iomem *)data_phase_addr; + oob_ptr += (mtd->oobsize - data_width); + chip->write_buf(mtd, oob_ptr, data_width); + + return 0; +} + +/* + * zynq_nand_write_page_swecc - [REPLACABLE] software ecc based page + * write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB + */ +static int zynq_nand_write_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, const u8 *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + u8 *ecc_calc = chip->buffers->ecccalc; + const u8 *p = buf; + u32 *eccpos = chip->ecc.layout->eccpos; + + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); +} + +/* + * nand_read_page_hwecc - Hardware ECC based page read function + * @mtd: Pointer to the mtd info structure + * @chip: Pointer to the NAND chip info structure + * @buf: Pointer to the buffer to store read data + * @oob_required: must write chip->oob_poi to OOB + * @page: page number to read + * + * This functions reads data and checks the data integrity by comparing hardware + * generated ECC values and read ECC values from spare area. + * + * returns: 0 always and updates ECC operation status in to MTD structure + */ +static int zynq_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + int i, stat, eccsteps, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + u8 *p = buf; + u8 *ecc_calc = chip->buffers->ecccalc; + u8 *ecc_code = chip->buffers->ecccode; + u32 *eccpos = chip->ecc.layout->eccpos; + unsigned long data_phase_addr = 0; + unsigned long data_width = 4; + u8 *oob_ptr; + + for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { + chip->read_buf(mtd, p, eccsize); + p += eccsize; + } + chip->read_buf(mtd, p, (eccsize - data_width)); + p += eccsize - data_width; + + /* Set ECC Last bit to 1 */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->read_buf(mtd, p, data_width); + + /* Read the calculated ECC value */ + p = buf; + chip->ecc.calculate(mtd, p, &ecc_calc[0]); + + /* Clear ECC last bit */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + /* Read the stored ECC value */ + oob_ptr = chip->oob_poi; + chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); + + /* de-assert chip select */ + data_phase_addr = (unsigned long)chip->IO_ADDR_R; + data_phase_addr |= ZYNQ_NAND_CLEAR_CS; + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + + oob_ptr += (mtd->oobsize - data_width); + chip->read_buf(mtd, oob_ptr, data_width); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = ~(chip->oob_poi[eccpos[i]]); + + eccsteps = chip->ecc.steps; + p = buf; + + /* Check ECC error for all blocks and correct if it is correctable */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* + * zynq_nand_read_page_swecc - [REPLACABLE] software ecc based page + * read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + */ +static int zynq_nand_read_page_swecc(struct mtd_info *mtd, + struct nand_chip *chip, u8 *buf, int oob_required, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + u8 *p = buf; + u8 *ecc_calc = chip->buffers->ecccalc; + u8 *ecc_code = chip->buffers->ecccode; + u32 *eccpos = chip->ecc.layout->eccpos; + + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/* + * zynq_nand_select_chip - Select the flash device + * @mtd: Pointer to the mtd_info structure + * @chip: Chip number to be selected + * + * This function is empty as the NAND controller handles chip select line + * internally based on the chip address passed in command and data phase. + */ +static void zynq_nand_select_chip(struct mtd_info *mtd, int chip) +{ + /* Not support multiple chips yet */ +} + +/* + * zynq_nand_cmd_function - Send command to NAND device + * @mtd: Pointer to the mtd_info structure + * @command: The command to be sent to the flash device + * @column: The column address for this command, -1 if none + * @page_addr: The page address for this command, -1 if none + */ +static void zynq_nand_cmd_function(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + const struct zynq_nand_command_format *curr_cmd = NULL; + u8 addr_cycles = 0; + struct zynq_nand_info *xnand = (struct zynq_nand_info *)chip->priv; + void *cmd_addr; + unsigned long cmd_data = 0; + unsigned long cmd_phase_addr = 0; + unsigned long data_phase_addr = 0; + u8 end_cmd = 0; + u8 end_cmd_valid = 0; + u32 index; + + if (xnand->end_cmd_pending) { + /* Check for end command if this command request is same as the + * pending command then return + */ + if (xnand->end_cmd == command) { + xnand->end_cmd = 0; + xnand->end_cmd_pending = 0; + return; + } + } + + /* Emulate NAND_CMD_READOOB for large page device */ + if ((mtd->writesize > ZYNQ_NAND_ECC_SIZE) && + (command == NAND_CMD_READOOB)) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + /* Get the command format */ + for (index = 0; index < ARRAY_SIZE(zynq_nand_commands); index++) + if (command == zynq_nand_commands[index].start_cmd) + break; + + if (index == ARRAY_SIZE(zynq_nand_commands)) { + printf("%s: Unsupported start cmd %02x\n", __func__, command); + return; + } + curr_cmd = &zynq_nand_commands[index]; + + /* Clear interrupt */ + writel(ZYNQ_MEMC_CLRCR_INT_CLR1, &zynq_nand_smc_base->cfr); + + /* Get the command phase address */ + if (curr_cmd->end_cmd_valid == ZYNQ_NAND_CMD_PHASE) + end_cmd_valid = 1; + + if (curr_cmd->end_cmd == NAND_CMD_NONE) + end_cmd = 0x0; + else + end_cmd = curr_cmd->end_cmd; + + if (command == NAND_CMD_READ0 || + command == NAND_CMD_SEQIN) { + addr_cycles = chip->onfi_params.addr_cycles & + ZYNQ_NAND_ROW_ADDR_CYCL_MASK; + addr_cycles += ((chip->onfi_params.addr_cycles & + ZYNQ_NAND_COL_ADDR_CYCL_MASK) >> 4); + } else { + addr_cycles = curr_cmd->addr_cycles; + } + + cmd_phase_addr = (unsigned long)xnand->nand_base | + (addr_cycles << ADDR_CYCLES_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (COMMAND_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (curr_cmd->start_cmd << START_CMD_SHIFT); + + cmd_addr = (void __iomem *)cmd_phase_addr; + + /* Get the data phase address */ + end_cmd_valid = 0; + + data_phase_addr = (unsigned long)xnand->nand_base | + (0x0 << CLEAR_CS_SHIFT) | + (end_cmd_valid << END_CMD_VALID_SHIFT) | + (DATA_PHASE) | + (end_cmd << END_CMD_SHIFT) | + (0x0 << ECC_LAST_SHIFT); + + chip->IO_ADDR_R = (void __iomem *)data_phase_addr; + chip->IO_ADDR_W = chip->IO_ADDR_R; + + /* Command phase AXI Read & Write */ + if (column != -1 && page_addr != -1) { + /* Adjust columns for 16 bit bus width */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + cmd_data = column; + if (mtd->writesize > ZYNQ_NAND_ECC_SIZE) { + cmd_data |= page_addr << 16; + /* Another address cycle for devices > 128MiB */ + if (chip->chipsize > (128 << 20)) { + writel(cmd_data, cmd_addr); + cmd_data = (page_addr >> 16); + } + } else { + cmd_data |= page_addr << 8; + } + } else if (page_addr != -1) { /* Erase */ + cmd_data = page_addr; + } else if (column != -1) { /* Change read/write column, read id etc */ + /* Adjust columns for 16 bit bus width */ + if ((chip->options & NAND_BUSWIDTH_16) && + ((command == NAND_CMD_READ0) || + (command == NAND_CMD_SEQIN) || + (command == NAND_CMD_RNDOUT) || + (command == NAND_CMD_RNDIN))) + column >>= 1; + cmd_data = column; + } + + writel(cmd_data, cmd_addr); + + if (curr_cmd->end_cmd_valid) { + xnand->end_cmd = curr_cmd->end_cmd; + xnand->end_cmd_pending = 1; + } + + ndelay(100); + + if ((command == NAND_CMD_READ0) || + (command == NAND_CMD_RESET) || + (command == NAND_CMD_PARAM) || + (command == NAND_CMD_GET_FEATURES)) + /* wait until command is processed */ + nand_wait_ready(mtd); +} + +/* + * zynq_nand_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void zynq_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + /* Make sure that buf is 32 bit aligned */ + if (((unsigned long)buf & 0x3) != 0) { + if (((unsigned long)buf & 0x1) != 0) { + if (len) { + *buf = readb(chip->IO_ADDR_R); + buf += 1; + len--; + } + } + + if (((unsigned long)buf & 0x3) != 0) { + if (len >= 2) { + *(u16 *)buf = readw(chip->IO_ADDR_R); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + *(u32 *)buf = readl(chip->IO_ADDR_R); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + *(u16 *)buf = readw(chip->IO_ADDR_R); + buf += 2; + len -= 2; + } + if (len) + *buf = readb(chip->IO_ADDR_R); + } +} + +/* + * zynq_nand_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void zynq_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + const u32 *nand = chip->IO_ADDR_W; + + /* Make sure that buf is 32 bit aligned */ + if (((unsigned long)buf & 0x3) != 0) { + if (((unsigned long)buf & 0x1) != 0) { + if (len) { + writeb(*buf, nand); + buf += 1; + len--; + } + } + + if (((unsigned long)buf & 0x3) != 0) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + } + } + + /* copy aligned data */ + while (len >= 4) { + writel(*(u32 *)buf, nand); + buf += 4; + len -= 4; + } + + /* mop up any remaining bytes */ + if (len) { + if (len >= 2) { + writew(*(u16 *)buf, nand); + buf += 2; + len -= 2; + } + + if (len) + writeb(*buf, nand); + } +} + +/* + * zynq_nand_device_ready - Check device ready/busy line + * @mtd: Pointer to the mtd_info structure + * + * returns: 0 on busy or 1 on ready state + */ +static int zynq_nand_device_ready(struct mtd_info *mtd) +{ + u32 csr_val; + + csr_val = readl(&zynq_nand_smc_base->csr); + /* Check the raw_int_status1 bit */ + if (csr_val & ZYNQ_MEMC_SR_RAW_INT_ST1) { + /* Clear the interrupt condition */ + writel(ZYNQ_MEMC_SR_INT_ST1, &zynq_nand_smc_base->cfr); + return 1; + } + + return 0; +} + +static int zynq_nand_check_is_16bit_bw_flash(void) +{ + int is_16bit_bw = NAND_BW_UNKNOWN; + int mio_num_8bit = 0, mio_num_16bit = 0; + + mio_num_8bit = zynq_slcr_get_mio_pin_status("nand8"); + if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT) + is_16bit_bw = NAND_BW_8BIT; + + mio_num_16bit = zynq_slcr_get_mio_pin_status("nand16"); + if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT && + mio_num_16bit == ZYNQ_NAND_MIO_NUM_NAND_16BIT) + is_16bit_bw = NAND_BW_16BIT; + + return is_16bit_bw; +} + +static int zynq_nand_init(struct nand_chip *nand_chip, int devnum) +{ + struct zynq_nand_info *xnand; + struct mtd_info *mtd; + unsigned long ecc_page_size; + u8 maf_id, dev_id, i; + u8 get_feature[4]; + u8 set_feature[4] = {ONDIE_ECC_FEATURE_ENABLE, 0x00, 0x00, 0x00}; + unsigned long ecc_cfg; + int ondie_ecc_enabled = 0; + int err = -1; + int is_16bit_bw; + + xnand = calloc(1, sizeof(struct zynq_nand_info)); + if (!xnand) { + printf("%s: failed to allocate\n", __func__); + goto fail; + } + + xnand->nand_base = (void __iomem *)ZYNQ_NAND_BASEADDR; + mtd = nand_to_mtd(nand_chip); + + nand_chip->priv = xnand; + mtd->priv = nand_chip; + + /* Set address of NAND IO lines */ + nand_chip->IO_ADDR_R = xnand->nand_base; + nand_chip->IO_ADDR_W = xnand->nand_base; + + /* Set the driver entry points for MTD */ + nand_chip->cmdfunc = zynq_nand_cmd_function; + nand_chip->dev_ready = zynq_nand_device_ready; + nand_chip->select_chip = zynq_nand_select_chip; + + /* If we don't set this delay driver sets 20us by default */ + nand_chip->chip_delay = 30; + + /* Buffer read/write routines */ + nand_chip->read_buf = zynq_nand_read_buf; + nand_chip->write_buf = zynq_nand_write_buf; + + is_16bit_bw = zynq_nand_check_is_16bit_bw_flash(); + if (is_16bit_bw == NAND_BW_UNKNOWN) { + printf("%s: Unable detect NAND based on MIO settings\n", + __func__); + goto fail; + } + + if (is_16bit_bw == NAND_BW_16BIT) + nand_chip->options = NAND_BUSWIDTH_16; + + nand_chip->bbt_options = NAND_BBT_USE_FLASH; + + /* Initialize the NAND flash interface on NAND controller */ + if (zynq_nand_init_nand_flash(nand_chip->options) < 0) { + printf("%s: nand flash init failed\n", __func__); + goto fail; + } + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + printf("%s: nand_scan_ident failed\n", __func__); + goto fail; + } + /* Send the command for reading device ID */ + nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ + maf_id = nand_chip->read_byte(mtd); + dev_id = nand_chip->read_byte(mtd); + + if ((maf_id == 0x2c) && ((dev_id == 0xf1) || + (dev_id == 0xa1) || (dev_id == 0xb1) || + (dev_id == 0xaa) || (dev_id == 0xba) || + (dev_id == 0xda) || (dev_id == 0xca) || + (dev_id == 0xac) || (dev_id == 0xbc) || + (dev_id == 0xdc) || (dev_id == 0xcc) || + (dev_id == 0xa3) || (dev_id == 0xb3) || + (dev_id == 0xd3) || (dev_id == 0xc3))) { + nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + for (i = 0; i < 4; i++) + writeb(set_feature[i], nand_chip->IO_ADDR_W); + + /* Wait for 1us after writing data with SET_FEATURES command */ + ndelay(1000); + + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, + ONDIE_ECC_FEATURE_ADDR, -1); + nand_chip->read_buf(mtd, get_feature, 4); + + if (get_feature[0] & ONDIE_ECC_FEATURE_ENABLE) { + debug("%s: OnDie ECC flash\n", __func__); + ondie_ecc_enabled = 1; + } else { + printf("%s: Unable to detect OnDie ECC\n", __func__); + } + } + + if (ondie_ecc_enabled) { + /* Bypass the controller ECC block */ + ecc_cfg = readl(&zynq_nand_smc_base->emcr); + ecc_cfg &= ~ZYNQ_MEMC_NAND_ECC_MODE_MASK; + writel(ecc_cfg, &zynq_nand_smc_base->emcr); + + /* The software ECC routines won't work + * with the SMC controller + */ + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.strength = 1; + nand_chip->ecc.read_page = zynq_nand_read_page_raw_nooob; + nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw; + nand_chip->ecc.write_page = zynq_nand_write_page_raw; + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; + nand_chip->ecc.read_oob = zynq_nand_read_oob; + nand_chip->ecc.write_oob = zynq_nand_write_oob; + nand_chip->ecc.size = mtd->writesize; + nand_chip->ecc.bytes = 0; + + /* NAND with on-die ECC supports subpage reads */ + nand_chip->options |= NAND_SUBPAGE_READ; + + /* On-Die ECC spare bytes offset 8 is used for ECC codes */ + if (ondie_ecc_enabled) { + nand_chip->ecc.layout = &ondie_nand_oob_64; + /* Use the BBT pattern descriptors */ + nand_chip->bbt_td = &bbt_main_descr; + nand_chip->bbt_md = &bbt_mirror_descr; + } + } else { + /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */ + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.strength = 1; + nand_chip->ecc.size = ZYNQ_NAND_ECC_SIZE; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.calculate = zynq_nand_calculate_hwecc; + nand_chip->ecc.correct = zynq_nand_correct_data; + nand_chip->ecc.hwctl = NULL; + nand_chip->ecc.read_page = zynq_nand_read_page_hwecc; + nand_chip->ecc.write_page = zynq_nand_write_page_hwecc; + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; + nand_chip->ecc.read_oob = zynq_nand_read_oob; + nand_chip->ecc.write_oob = zynq_nand_write_oob; + + switch (mtd->writesize) { + case 512: + ecc_page_size = 0x1; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + case 1024: + ecc_page_size = 0x2; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + case 2048: + ecc_page_size = 0x3; + /* Set the ECC memory config register */ + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), + &zynq_nand_smc_base->emcr); + break; + default: + nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.calculate = nand_calculate_ecc; + nand_chip->ecc.correct = nand_correct_data; + nand_chip->ecc.read_page = zynq_nand_read_page_swecc; + nand_chip->ecc.write_page = zynq_nand_write_page_swecc; + nand_chip->ecc.size = 256; + break; + } + + if (mtd->oobsize == 16) + nand_chip->ecc.layout = &nand_oob_16; + else if (mtd->oobsize == 64) + nand_chip->ecc.layout = &nand_oob_64; + else + printf("%s: No oob layout found\n", __func__); + } + + /* Second phase scan */ + if (nand_scan_tail(mtd)) { + printf("%s: nand_scan_tail failed\n", __func__); + goto fail; + } + if (nand_register(devnum, mtd)) + goto fail; + return 0; +fail: + free(xnand); + return err; +} + +static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; + +void board_nand_init(void) +{ + struct nand_chip *nand = &nand_chip[0]; + + if (zynq_nand_init(nand, 0)) + puts("ZYNQ NAND init failed\n"); +} diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c deleted file mode 100644 index 3ccb168d13..0000000000 --- a/drivers/mtd/nand/sunxi_nand.c +++ /dev/null @@ -1,1850 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2013 Boris BREZILLON - * Copyright (C) 2015 Roy Spliet - * - * Derived from: - * https://github.com/yuq/sunxi-nfc-mtd - * Copyright (C) 2013 Qiang Yu - * - * https://github.com/hno/Allwinner-Info - * Copyright (C) 2013 Henrik Nordström - * - * Copyright (C) 2013 Dmitriy B. - * Copyright (C) 2013 Sergey Lapin - * - * 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. - * - * 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. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -DECLARE_GLOBAL_DATA_PTR; - -#define NFC_REG_CTL 0x0000 -#define NFC_REG_ST 0x0004 -#define NFC_REG_INT 0x0008 -#define NFC_REG_TIMING_CTL 0x000C -#define NFC_REG_TIMING_CFG 0x0010 -#define NFC_REG_ADDR_LOW 0x0014 -#define NFC_REG_ADDR_HIGH 0x0018 -#define NFC_REG_SECTOR_NUM 0x001C -#define NFC_REG_CNT 0x0020 -#define NFC_REG_CMD 0x0024 -#define NFC_REG_RCMD_SET 0x0028 -#define NFC_REG_WCMD_SET 0x002C -#define NFC_REG_IO_DATA 0x0030 -#define NFC_REG_ECC_CTL 0x0034 -#define NFC_REG_ECC_ST 0x0038 -#define NFC_REG_DEBUG 0x003C -#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) -#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) -#define NFC_REG_SPARE_AREA 0x00A0 -#define NFC_REG_PAT_ID 0x00A4 -#define NFC_RAM0_BASE 0x0400 -#define NFC_RAM1_BASE 0x0800 - -/* define bit use in NFC_CTL */ -#define NFC_EN BIT(0) -#define NFC_RESET BIT(1) -#define NFC_BUS_WIDTH_MSK BIT(2) -#define NFC_BUS_WIDTH_8 (0 << 2) -#define NFC_BUS_WIDTH_16 (1 << 2) -#define NFC_RB_SEL_MSK BIT(3) -#define NFC_RB_SEL(x) ((x) << 3) -#define NFC_CE_SEL_MSK (0x7 << 24) -#define NFC_CE_SEL(x) ((x) << 24) -#define NFC_CE_CTL BIT(6) -#define NFC_PAGE_SHIFT_MSK (0xf << 8) -#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) -#define NFC_SAM BIT(12) -#define NFC_RAM_METHOD BIT(14) -#define NFC_DEBUG_CTL BIT(31) - -/* define bit use in NFC_ST */ -#define NFC_RB_B2R BIT(0) -#define NFC_CMD_INT_FLAG BIT(1) -#define NFC_DMA_INT_FLAG BIT(2) -#define NFC_CMD_FIFO_STATUS BIT(3) -#define NFC_STA BIT(4) -#define NFC_NATCH_INT_FLAG BIT(5) -#define NFC_RB_STATE(x) BIT(x + 8) - -/* define bit use in NFC_INT */ -#define NFC_B2R_INT_ENABLE BIT(0) -#define NFC_CMD_INT_ENABLE BIT(1) -#define NFC_DMA_INT_ENABLE BIT(2) -#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ - NFC_CMD_INT_ENABLE | \ - NFC_DMA_INT_ENABLE) - -/* define bit use in NFC_TIMING_CTL */ -#define NFC_TIMING_CTL_EDO BIT(8) - -/* define NFC_TIMING_CFG register layout */ -#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ - (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ - (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ - (((tCAD) & 0x7) << 8)) - -/* define bit use in NFC_CMD */ -#define NFC_CMD_LOW_BYTE_MSK 0xff -#define NFC_CMD_HIGH_BYTE_MSK (0xff << 8) -#define NFC_CMD(x) (x) -#define NFC_ADR_NUM_MSK (0x7 << 16) -#define NFC_ADR_NUM(x) (((x) - 1) << 16) -#define NFC_SEND_ADR BIT(19) -#define NFC_ACCESS_DIR BIT(20) -#define NFC_DATA_TRANS BIT(21) -#define NFC_SEND_CMD1 BIT(22) -#define NFC_WAIT_FLAG BIT(23) -#define NFC_SEND_CMD2 BIT(24) -#define NFC_SEQ BIT(25) -#define NFC_DATA_SWAP_METHOD BIT(26) -#define NFC_ROW_AUTO_INC BIT(27) -#define NFC_SEND_CMD3 BIT(28) -#define NFC_SEND_CMD4 BIT(29) -#define NFC_CMD_TYPE_MSK (0x3 << 30) -#define NFC_NORMAL_OP (0 << 30) -#define NFC_ECC_OP (1 << 30) -#define NFC_PAGE_OP (2 << 30) - -/* define bit use in NFC_RCMD_SET */ -#define NFC_READ_CMD_MSK 0xff -#define NFC_RND_READ_CMD0_MSK (0xff << 8) -#define NFC_RND_READ_CMD1_MSK (0xff << 16) - -/* define bit use in NFC_WCMD_SET */ -#define NFC_PROGRAM_CMD_MSK 0xff -#define NFC_RND_WRITE_CMD_MSK (0xff << 8) -#define NFC_READ_CMD0_MSK (0xff << 16) -#define NFC_READ_CMD1_MSK (0xff << 24) - -/* define bit use in NFC_ECC_CTL */ -#define NFC_ECC_EN BIT(0) -#define NFC_ECC_PIPELINE BIT(3) -#define NFC_ECC_EXCEPTION BIT(4) -#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) -#define NFC_ECC_BLOCK_512 (1 << 5) -#define NFC_RANDOM_EN BIT(9) -#define NFC_RANDOM_DIRECTION BIT(10) -#define NFC_ECC_MODE_MSK (0xf << 12) -#define NFC_ECC_MODE(x) ((x) << 12) -#define NFC_RANDOM_SEED_MSK (0x7fff << 16) -#define NFC_RANDOM_SEED(x) ((x) << 16) - -/* define bit use in NFC_ECC_ST */ -#define NFC_ECC_ERR(x) BIT(x) -#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) -#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) - -#define NFC_DEFAULT_TIMEOUT_MS 1000 - -#define NFC_SRAM_SIZE 1024 - -#define NFC_MAX_CS 7 - -/* - * Ready/Busy detection type: describes the Ready/Busy detection modes - * - * @RB_NONE: no external detection available, rely on STATUS command - * and software timeouts - * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy - * pin of the NAND flash chip must be connected to one of the - * native NAND R/B pins (those which can be muxed to the NAND - * Controller) - * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy - * pin of the NAND flash chip must be connected to a GPIO capable - * pin. - */ -enum sunxi_nand_rb_type { - RB_NONE, - RB_NATIVE, - RB_GPIO, -}; - -/* - * Ready/Busy structure: stores information related to Ready/Busy detection - * - * @type: the Ready/Busy detection mode - * @info: information related to the R/B detection mode. Either a gpio - * id or a native R/B id (those supported by the NAND controller). - */ -struct sunxi_nand_rb { - enum sunxi_nand_rb_type type; - union { - struct gpio_desc gpio; - int nativeid; - } info; -}; - -/* - * Chip Select structure: stores information related to NAND Chip Select - * - * @cs: the NAND CS id used to communicate with a NAND Chip - * @rb: the Ready/Busy description - */ -struct sunxi_nand_chip_sel { - u8 cs; - struct sunxi_nand_rb rb; -}; - -/* - * sunxi HW ECC infos: stores information related to HW ECC support - * - * @mode: the sunxi ECC mode field deduced from ECC requirements - * @layout: the OOB layout depending on the ECC requirements and the - * selected ECC mode - */ -struct sunxi_nand_hw_ecc { - int mode; - struct nand_ecclayout layout; -}; - -/* - * NAND chip structure: stores NAND chip device related information - * - * @node: used to store NAND chips into a list - * @nand: base NAND chip structure - * @mtd: base MTD structure - * @clk_rate: clk_rate required for this NAND chip - * @timing_cfg TIMING_CFG register value for this NAND chip - * @selected: current active CS - * @nsels: number of CS lines required by the NAND chip - * @sels: array of CS lines descriptions - */ -struct sunxi_nand_chip { - struct list_head node; - struct nand_chip nand; - unsigned long clk_rate; - u32 timing_cfg; - u32 timing_ctl; - int selected; - int addr_cycles; - u32 addr[2]; - int cmd_cycles; - u8 cmd[2]; - int nsels; - struct sunxi_nand_chip_sel sels[0]; -}; - -static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) -{ - return container_of(nand, struct sunxi_nand_chip, nand); -} - -/* - * NAND Controller structure: stores sunxi NAND controller information - * - * @controller: base controller structure - * @dev: parent device (used to print error messages) - * @regs: NAND controller registers - * @ahb_clk: NAND Controller AHB clock - * @mod_clk: NAND Controller mod clock - * @assigned_cs: bitmask describing already assigned CS lines - * @clk_rate: NAND controller current clock rate - * @chips: a list containing all the NAND chips attached to - * this NAND controller - * @complete: a completion object used to wait for NAND - * controller events - */ -struct sunxi_nfc { - struct nand_hw_control controller; - struct device *dev; - void __iomem *regs; - struct clk *ahb_clk; - struct clk *mod_clk; - unsigned long assigned_cs; - unsigned long clk_rate; - struct list_head chips; -}; - -static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) -{ - return container_of(ctrl, struct sunxi_nfc, controller); -} - -static void sunxi_nfc_set_clk_rate(unsigned long hz) -{ - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - int div_m, div_n; - - div_m = (clock_get_pll6() + hz - 1) / hz; - for (div_n = 0; div_n < 3 && div_m > 16; div_n++) { - if (div_m % 2) - div_m++; - div_m >>= 1; - } - if (div_m > 16) - div_m = 16; - - /* config mod clock */ - writel(CCM_NAND_CTRL_ENABLE | CCM_NAND_CTRL_PLL6 | - CCM_NAND_CTRL_N(div_n) | CCM_NAND_CTRL_M(div_m), - &ccm->nand0_clk_cfg); - - /* gate on nand clock */ - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0)); -#ifdef CONFIG_MACH_SUN9I - setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); -#else - setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); -#endif -} - -static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, - unsigned int timeout_ms) -{ - unsigned int timeout_ticks; - u32 time_start, status; - int ret = -ETIMEDOUT; - - if (!timeout_ms) - timeout_ms = NFC_DEFAULT_TIMEOUT_MS; - - timeout_ticks = (timeout_ms * CONFIG_SYS_HZ) / 1000; - - time_start = get_timer(0); - - do { - status = readl(nfc->regs + NFC_REG_ST); - if ((status & flags) == flags) { - ret = 0; - break; - } - - udelay(1); - } while (get_timer(time_start) < timeout_ticks); - - writel(status & flags, nfc->regs + NFC_REG_ST); - - return ret; -} - -static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) -{ - unsigned long timeout = (CONFIG_SYS_HZ * - NFC_DEFAULT_TIMEOUT_MS) / 1000; - u32 time_start; - - time_start = get_timer(0); - do { - if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS)) - return 0; - } while (get_timer(time_start) < timeout); - - dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); - return -ETIMEDOUT; -} - -static int sunxi_nfc_rst(struct sunxi_nfc *nfc) -{ - unsigned long timeout = (CONFIG_SYS_HZ * - NFC_DEFAULT_TIMEOUT_MS) / 1000; - u32 time_start; - - writel(0, nfc->regs + NFC_REG_ECC_CTL); - writel(NFC_RESET, nfc->regs + NFC_REG_CTL); - - time_start = get_timer(0); - do { - if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)) - return 0; - } while (get_timer(time_start) < timeout); - - dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); - return -ETIMEDOUT; -} - -static int sunxi_nfc_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_rb *rb; - unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20); - int ret; - - if (sunxi_nand->selected < 0) - return 0; - - rb = &sunxi_nand->sels[sunxi_nand->selected].rb; - - switch (rb->type) { - case RB_NATIVE: - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - if (ret) - break; - - sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo); - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - break; - case RB_GPIO: - ret = dm_gpio_get_value(&rb->info.gpio); - break; - case RB_NONE: - default: - ret = 0; - dev_err(nfc->dev, "cannot check R/B NAND status!\n"); - break; - } - - return ret; -} - -static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_chip_sel *sel; - u32 ctl; - - if (chip > 0 && chip >= sunxi_nand->nsels) - return; - - if (chip == sunxi_nand->selected) - return; - - ctl = readl(nfc->regs + NFC_REG_CTL) & - ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); - - if (chip >= 0) { - sel = &sunxi_nand->sels[chip]; - - ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | - NFC_PAGE_SHIFT(nand->page_shift - 10); - if (sel->rb.type == RB_NONE) { - nand->dev_ready = NULL; - } else { - nand->dev_ready = sunxi_nfc_dev_ready; - if (sel->rb.type == RB_NATIVE) - ctl |= NFC_RB_SEL(sel->rb.info.nativeid); - } - - writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); - - if (nfc->clk_rate != sunxi_nand->clk_rate) { - sunxi_nfc_set_clk_rate(sunxi_nand->clk_rate); - nfc->clk_rate = sunxi_nand->clk_rate; - } - } - - writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); - writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); - writel(ctl, nfc->regs + NFC_REG_CTL); - - sunxi_nand->selected = chip; -} - -static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - int cnt; - int offs = 0; - u32 tmp; - - while (len > offs) { - cnt = min(len - offs, NFC_SRAM_SIZE); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - break; - - writel(cnt, nfc->regs + NFC_REG_CNT); - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; - writel(tmp, nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - if (ret) - break; - - if (buf) - memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, - cnt); - offs += cnt; - } -} - -static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - int cnt; - int offs = 0; - u32 tmp; - - while (len > offs) { - cnt = min(len - offs, NFC_SRAM_SIZE); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - break; - - writel(cnt, nfc->regs + NFC_REG_CNT); - memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); - tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | - NFC_ACCESS_DIR; - writel(tmp, nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - if (ret) - break; - - offs += cnt; - } -} - -static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) -{ - uint8_t ret; - - sunxi_nfc_read_buf(mtd, &ret, 1); - - return ret; -} - -static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, - unsigned int ctrl) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); - struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - int ret; - u32 tmp; - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return; - - if (ctrl & NAND_CTRL_CHANGE) { - tmp = readl(nfc->regs + NFC_REG_CTL); - if (ctrl & NAND_NCE) - tmp |= NFC_CE_CTL; - else - tmp &= ~NFC_CE_CTL; - writel(tmp, nfc->regs + NFC_REG_CTL); - } - - if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && - !(ctrl & (NAND_CLE | NAND_ALE))) { - u32 cmd = 0; - - if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) - return; - - if (sunxi_nand->cmd_cycles--) - cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; - - if (sunxi_nand->cmd_cycles--) { - cmd |= NFC_SEND_CMD2; - writel(sunxi_nand->cmd[1], - nfc->regs + NFC_REG_RCMD_SET); - } - - sunxi_nand->cmd_cycles = 0; - - if (sunxi_nand->addr_cycles) { - cmd |= NFC_SEND_ADR | - NFC_ADR_NUM(sunxi_nand->addr_cycles); - writel(sunxi_nand->addr[0], - nfc->regs + NFC_REG_ADDR_LOW); - } - - if (sunxi_nand->addr_cycles > 4) - writel(sunxi_nand->addr[1], - nfc->regs + NFC_REG_ADDR_HIGH); - - writel(cmd, nfc->regs + NFC_REG_CMD); - sunxi_nand->addr[0] = 0; - sunxi_nand->addr[1] = 0; - sunxi_nand->addr_cycles = 0; - sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - } - - if (ctrl & NAND_CLE) { - sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; - } else if (ctrl & NAND_ALE) { - sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= - dat << ((sunxi_nand->addr_cycles % 4) * 8); - sunxi_nand->addr_cycles++; - } -} - -/* These seed values have been extracted from Allwinner's BSP */ -static const u16 sunxi_nfc_randomizer_page_seeds[] = { - 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, - 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, - 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, - 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, - 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, - 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, - 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, - 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, - 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, - 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, - 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, - 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, - 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, - 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, - 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, - 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, -}; - -/* - * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds - * have been generated using - * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what - * the randomizer engine does internally before de/scrambling OOB data. - * - * Those tables are statically defined to avoid calculating randomizer state - * at runtime. - */ -static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { - 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, - 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, - 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, - 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, - 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, - 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, - 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, - 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, - 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, - 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, - 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, - 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, - 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, - 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, - 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, - 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, -}; - -static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { - 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, - 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, - 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, - 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, - 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, - 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, - 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, - 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, - 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, - 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, - 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, - 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, - 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, - 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, - 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, - 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, -}; - -static u16 sunxi_nfc_randomizer_step(u16 state, int count) -{ - state &= 0x7fff; - - /* - * This loop is just a simple implementation of a Fibonacci LFSR using - * the x16 + x15 + 1 polynomial. - */ - while (count--) - state = ((state >> 1) | - (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; - - return state; -} - -static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) -{ - const u16 *seeds = sunxi_nfc_randomizer_page_seeds; - int mod = mtd->erasesize / mtd->writesize; - - if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) - mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); - - if (ecc) { - if (mtd->ecc_step_size == 512) - seeds = sunxi_nfc_randomizer_ecc512_seeds; - else - seeds = sunxi_nfc_randomizer_ecc1024_seeds; - } - - return seeds[page % mod]; -} - -static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, - int page, bool ecc) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - u16 state; - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - state = sunxi_nfc_randomizer_state(mtd, page, ecc); - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; - writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - if (!(nand->options & NAND_NEED_SCRAMBLING)) - return; - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) -{ - u16 state = sunxi_nfc_randomizer_state(mtd, page, true); - - bbm[0] ^= state; - bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); -} - -static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, - const uint8_t *buf, int len, - bool ecc, int page) -{ - sunxi_nfc_randomizer_config(mtd, page, ecc); - sunxi_nfc_randomizer_enable(mtd); - sunxi_nfc_write_buf(mtd, buf, len); - sunxi_nfc_randomizer_disable(mtd); -} - -static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, - int len, bool ecc, int page) -{ - sunxi_nfc_randomizer_config(mtd, page, ecc); - sunxi_nfc_randomizer_enable(mtd); - sunxi_nfc_read_buf(mtd, buf, len); - sunxi_nfc_randomizer_disable(mtd); -} - -static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct sunxi_nand_hw_ecc *data = nand->ecc.priv; - u32 ecc_ctl; - - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | - NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; - - if (nand->ecc.size == 512) - ecc_ctl |= NFC_ECC_BLOCK_512; - - writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); -} - -static void sunxi_nfc_hw_ecc_disable(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - nfc->regs + NFC_REG_ECC_CTL); -} - -static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) -{ - buf[0] = user_data; - buf[1] = user_data >> 8; - buf[2] = user_data >> 16; - buf[3] = user_data >> 24; -} - -static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, - u8 *data, int data_off, - u8 *oob, int oob_off, - int *cur_off, - unsigned int *max_bitflips, - bool bbm, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int raw_mode = 0; - u32 status; - int ret; - - if (*cur_off != data_off) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); - - sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); - - if (data_off + ecc->size != oob_off) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - sunxi_nfc_randomizer_enable(mtd); - writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - sunxi_nfc_randomizer_disable(mtd); - if (ret) - return ret; - - *cur_off = oob_off + ecc->bytes + 4; - - status = readl(nfc->regs + NFC_REG_ECC_ST); - if (status & NFC_ECC_PAT_FOUND(0)) { - u8 pattern = 0xff; - - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) - pattern = 0x0; - - memset(data, pattern, ecc->size); - memset(oob, pattern, ecc->bytes + 4); - - return 1; - } - - ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); - - memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); - - if (status & NFC_ECC_ERR(0)) { - /* - * Re-read the data with the randomizer disabled to identify - * bitflips in erased pages. - */ - if (nand->options & NAND_NEED_SCRAMBLING) { - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); - nand->read_buf(mtd, data, ecc->size); - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); - } - - ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, - NULL, 0, ecc->strength); - if (ret >= 0) - raw_mode = 1; - } else { - /* - * The engine protects 4 bytes of OOB data per chunk. - * Retrieve the corrected OOB bytes. - */ - sunxi_nfc_user_data_to_buf(readl(nfc->regs + - NFC_REG_USER_DATA(0)), - oob); - - /* De-randomize the Bad Block Marker. */ - if (bbm && nand->options & NAND_NEED_SCRAMBLING) - sunxi_nfc_randomize_bbm(mtd, page, oob); - } - - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - *max_bitflips = max_t(unsigned int, *max_bitflips, ret); - } - - return raw_mode; -} - -static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off, - bool randomize, int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); - int len = mtd->oobsize - offset; - - if (len <= 0) - return; - - if (*cur_off != offset) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, - offset + mtd->writesize, -1); - - if (!randomize) - sunxi_nfc_read_buf(mtd, oob + offset, len); - else - sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, - false, page); - - *cur_off = mtd->oobsize + mtd->writesize; -} - -static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) -{ - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - -static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, - const u8 *data, int data_off, - const u8 *oob, int oob_off, - int *cur_off, bool bbm, - int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int ret; - - if (data_off != *cur_off) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); - - sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); - - /* Fill OOB data in */ - if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { - u8 user_data[4]; - - memcpy(user_data, oob, 4); - sunxi_nfc_randomize_bbm(mtd, page, user_data); - writel(sunxi_nfc_buf_to_user_data(user_data), - nfc->regs + NFC_REG_USER_DATA(0)); - } else { - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(0)); - } - - if (data_off + ecc->size != oob_off) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); - - ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); - if (ret) - return ret; - - sunxi_nfc_randomizer_enable(mtd); - writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | - NFC_ACCESS_DIR | NFC_ECC_OP, - nfc->regs + NFC_REG_CMD); - - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); - sunxi_nfc_randomizer_disable(mtd); - if (ret) - return ret; - - *cur_off = oob_off + ecc->bytes + 4; - - return 0; -} - -static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off, - int page) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); - int len = mtd->oobsize - offset; - - if (len <= 0) - return; - - if (*cur_off != offset) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, - offset + mtd->writesize, -1); - - sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); - - *cur_off = mtd->oobsize + mtd->writesize; -} - -static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - int ret, i, cur_off = 0; - bool raw_mode = false; - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = buf + data_off; - u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, &max_bitflips, - !i, page); - if (ret < 0) - return ret; - else if (ret) - raw_mode = true; - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, - !raw_mode, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, - struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, - uint8_t *bufpoi, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - unsigned int max_bitflips = 0; - - sunxi_nfc_hw_ecc_enable(mtd); - - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - for (i = data_offs / ecc->size; - i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - u8 *data = bufpoi + data_off; - u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, - oob, oob_off + mtd->writesize, - &cur_off, &max_bitflips, !i, page); - if (ret < 0) - return ret; - } - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - const u8 *data = buf + data_off; - const u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, !i, page); - if (ret) - return ret; - } - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - &cur_off, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return 0; -} - -static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, - struct nand_chip *chip, - u32 data_offs, u32 data_len, - const u8 *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = data_offs / ecc->size; - i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { - int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); - const u8 *data = buf + data_off; - const u8 *oob = chip->oob_poi + oob_off; - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, - oob_off + mtd->writesize, - &cur_off, !i, page); - if (ret) - return ret; - } - - sunxi_nfc_hw_ecc_disable(mtd); - - return 0; -} - -static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - unsigned int max_bitflips = 0; - int ret, i, cur_off = 0; - bool raw_mode = false; - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - u8 *data = buf + (i * ecc->size); - u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, - oob_off, &cur_off, - &max_bitflips, !i, page); - if (ret < 0) - return ret; - else if (ret) - raw_mode = true; - } - - if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, - !raw_mode, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return max_bitflips; -} - -static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required, int page) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ret, i, cur_off = 0; - - sunxi_nfc_hw_ecc_enable(mtd); - - for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); - int oob_off = data_off + ecc->size; - const u8 *data = buf + (i * ecc->size); - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); - - ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, - oob, oob_off, &cur_off, - false, page); - if (ret) - return ret; - } - - if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, - &cur_off, page); - - sunxi_nfc_hw_ecc_disable(mtd); - - return 0; -} - -static const s32 tWB_lut[] = {6, 12, 16, 20}; -static const s32 tRHW_lut[] = {4, 8, 12, 20}; - -static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, - u32 clk_period) -{ - u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); - int i; - - for (i = 0; i < lut_size; i++) { - if (clk_cycles <= lut[i]) - return i; - } - - /* Doesn't fit */ - return -EINVAL; -} - -#define sunxi_nand_lookup_timing(l, p, c) \ - _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) - -static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, - const struct nand_sdr_timings *timings) -{ - u32 min_clk_period = 0; - s32 tWB, tADL, tWHR, tRHW, tCAD; - - /* T1 <=> tCLS */ - if (timings->tCLS_min > min_clk_period) - min_clk_period = timings->tCLS_min; - - /* T2 <=> tCLH */ - if (timings->tCLH_min > min_clk_period) - min_clk_period = timings->tCLH_min; - - /* T3 <=> tCS */ - if (timings->tCS_min > min_clk_period) - min_clk_period = timings->tCS_min; - - /* T4 <=> tCH */ - if (timings->tCH_min > min_clk_period) - min_clk_period = timings->tCH_min; - - /* T5 <=> tWP */ - if (timings->tWP_min > min_clk_period) - min_clk_period = timings->tWP_min; - - /* T6 <=> tWH */ - if (timings->tWH_min > min_clk_period) - min_clk_period = timings->tWH_min; - - /* T7 <=> tALS */ - if (timings->tALS_min > min_clk_period) - min_clk_period = timings->tALS_min; - - /* T8 <=> tDS */ - if (timings->tDS_min > min_clk_period) - min_clk_period = timings->tDS_min; - - /* T9 <=> tDH */ - if (timings->tDH_min > min_clk_period) - min_clk_period = timings->tDH_min; - - /* T10 <=> tRR */ - if (timings->tRR_min > (min_clk_period * 3)) - min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); - - /* T11 <=> tALH */ - if (timings->tALH_min > min_clk_period) - min_clk_period = timings->tALH_min; - - /* T12 <=> tRP */ - if (timings->tRP_min > min_clk_period) - min_clk_period = timings->tRP_min; - - /* T13 <=> tREH */ - if (timings->tREH_min > min_clk_period) - min_clk_period = timings->tREH_min; - - /* T14 <=> tRC */ - if (timings->tRC_min > (min_clk_period * 2)) - min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); - - /* T15 <=> tWC */ - if (timings->tWC_min > (min_clk_period * 2)) - min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); - - /* T16 - T19 + tCAD */ - tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, - min_clk_period); - if (tWB < 0) { - dev_err(nfc->dev, "unsupported tWB\n"); - return tWB; - } - - tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; - if (tADL > 3) { - dev_err(nfc->dev, "unsupported tADL\n"); - return -EINVAL; - } - - tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; - if (tWHR > 3) { - dev_err(nfc->dev, "unsupported tWHR\n"); - return -EINVAL; - } - - tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, - min_clk_period); - if (tRHW < 0) { - dev_err(nfc->dev, "unsupported tRHW\n"); - return tRHW; - } - - /* - * TODO: according to ONFI specs this value only applies for DDR NAND, - * but Allwinner seems to set this to 0x7. Mimic them for now. - */ - tCAD = 0x7; - - /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ - chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); - - /* - * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data - * output cycle timings shall be used if the host drives tRC less than - * 30 ns. - */ - chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0; - - /* Convert min_clk_period from picoseconds to nanoseconds */ - min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); - - /* - * Convert min_clk_period into a clk frequency, then get the - * appropriate rate for the NAND controller IP given this formula - * (specified in the datasheet): - * nand clk_rate = min_clk_rate - */ - chip->clk_rate = 1000000000L / min_clk_period; - - return 0; -} - -static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(&chip->nand); - const struct nand_sdr_timings *timings; - int ret; - int mode; - - mode = onfi_get_async_timing_mode(&chip->nand); - if (mode == ONFI_TIMING_MODE_UNKNOWN) { - mode = chip->nand.onfi_timing_mode_default; - } else { - uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; - int i; - - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - feature[0] = mode; - for (i = 0; i < chip->nsels; i++) { - chip->nand.select_chip(mtd, i); - ret = chip->nand.onfi_set_features(mtd, - &chip->nand, - ONFI_FEATURE_ADDR_TIMING_MODE, - feature); - chip->nand.select_chip(mtd, -1); - if (ret && ret != -ENOTSUPP) - return ret; - } - } - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (IS_ERR(timings)) - return PTR_ERR(timings); - - return sunxi_nand_chip_set_timings(chip, timings); -} - -static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc) -{ - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; - struct sunxi_nand_hw_ecc *data; - struct nand_ecclayout *layout; - int nsectors; - int ret; - int i; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (ecc->size != 512 && ecc->size != 1024) - return -EINVAL; - - /* Prefer 1k ECC chunk over 512 ones */ - if (ecc->size == 512 && mtd->writesize > 512) { - ecc->size = 1024; - ecc->strength *= 2; - } - - /* Add ECC info retrieval from DT */ - for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (ecc->strength <= strengths[i]) { - /* - * Update ecc->strength value with the actual strength - * that will be used by the ECC engine. - */ - ecc->strength = strengths[i]; - break; - } - } - - if (i >= ARRAY_SIZE(strengths)) { - dev_err(nfc->dev, "unsupported strength\n"); - ret = -ENOTSUPP; - goto err; - } - - data->mode = i; - - /* HW ECC always request ECC bytes for 1024 bytes blocks */ - ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); - - /* HW ECC always work with even numbers of ECC bytes */ - ecc->bytes = ALIGN(ecc->bytes, 2); - - layout = &data->layout; - nsectors = mtd->writesize / ecc->size; - - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { - ret = -EINVAL; - goto err; - } - - layout->eccbytes = (ecc->bytes * nsectors); - - ecc->layout = layout; - ecc->priv = data; - - return 0; - -err: - kfree(data); - - return ret; -} - -#ifndef __UBOOT__ -static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) -{ - kfree(ecc->priv); -} -#endif /* __UBOOT__ */ - -static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc) -{ - struct nand_ecclayout *layout; - int nsectors; - int i, j; - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); - if (ret) - return ret; - - ecc->read_page = sunxi_nfc_hw_ecc_read_page; - ecc->write_page = sunxi_nfc_hw_ecc_write_page; - ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; - ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; - layout = ecc->layout; - nsectors = mtd->writesize / ecc->size; - - for (i = 0; i < nsectors; i++) { - if (i) { - layout->oobfree[i].offset = - layout->oobfree[i - 1].offset + - layout->oobfree[i - 1].length + - ecc->bytes; - layout->oobfree[i].length = 4; - } else { - /* - * The first 2 bytes are used for BB markers, hence we - * only have 2 bytes available in the first user data - * section. - */ - layout->oobfree[i].length = 2; - layout->oobfree[i].offset = 2; - } - - for (j = 0; j < ecc->bytes; j++) - layout->eccpos[(ecc->bytes * i) + j] = - layout->oobfree[i].offset + - layout->oobfree[i].length + j; - } - - if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { - layout->oobfree[nsectors].offset = - layout->oobfree[nsectors - 1].offset + - layout->oobfree[nsectors - 1].length + - ecc->bytes; - layout->oobfree[nsectors].length = mtd->oobsize - - ((ecc->bytes + 4) * nsectors); - } - - return 0; -} - -static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, - struct nand_ecc_ctrl *ecc) -{ - struct nand_ecclayout *layout; - int nsectors; - int i; - int ret; - - ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); - if (ret) - return ret; - - ecc->prepad = 4; - ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; - ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; - - layout = ecc->layout; - nsectors = mtd->writesize / ecc->size; - - for (i = 0; i < (ecc->bytes * nsectors); i++) - layout->eccpos[i] = i; - - layout->oobfree[0].length = mtd->oobsize - i; - layout->oobfree[0].offset = i; - - return 0; -} - -#ifndef __UBOOT__ -static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) -{ - switch (ecc->mode) { - case NAND_ECC_HW: - case NAND_ECC_HW_SYNDROME: - sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); - break; - case NAND_ECC_NONE: - kfree(ecc->layout); - default: - break; - } -} -#endif /* __UBOOT__ */ - -static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - int ret; - - if (!ecc->size) { - ecc->size = nand->ecc_step_ds; - ecc->strength = nand->ecc_strength_ds; - } - - if (!ecc->size || !ecc->strength) - return -EINVAL; - - switch (ecc->mode) { - case NAND_ECC_SOFT_BCH: - break; - case NAND_ECC_HW: - ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc); - if (ret) - return ret; - break; - case NAND_ECC_HW_SYNDROME: - ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc); - if (ret) - return ret; - break; - case NAND_ECC_NONE: - ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL); - if (!ecc->layout) - return -ENOMEM; - ecc->layout->oobfree[0].length = mtd->oobsize; - case NAND_ECC_SOFT: - break; - default: - return -EINVAL; - } - - return 0; -} - -static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum) -{ - const struct nand_sdr_timings *timings; - const void *blob = gd->fdt_blob; - struct sunxi_nand_chip *chip; - struct mtd_info *mtd; - struct nand_chip *nand; - int nsels; - int ret; - int i; - u32 cs[8], rb[8]; - - if (!fdt_getprop(blob, node, "reg", &nsels)) - return -EINVAL; - - nsels /= sizeof(u32); - if (!nsels || nsels > 8) { - dev_err(dev, "invalid reg property size\n"); - return -EINVAL; - } - - chip = kzalloc(sizeof(*chip) + - (nsels * sizeof(struct sunxi_nand_chip_sel)), - GFP_KERNEL); - if (!chip) { - dev_err(dev, "could not allocate chip\n"); - return -ENOMEM; - } - - chip->nsels = nsels; - chip->selected = -1; - - for (i = 0; i < nsels; i++) { - cs[i] = -1; - rb[i] = -1; - } - - ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", cs, nsels); - if (ret) { - dev_err(dev, "could not retrieve reg property: %d\n", ret); - return ret; - } - - ret = fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", rb, - nsels); - if (ret) { - dev_err(dev, "could not retrieve reg property: %d\n", ret); - return ret; - } - - for (i = 0; i < nsels; i++) { - int tmp = cs[i]; - - if (tmp > NFC_MAX_CS) { - dev_err(dev, - "invalid reg value: %u (max CS = 7)\n", - tmp); - return -EINVAL; - } - - if (test_and_set_bit(tmp, &nfc->assigned_cs)) { - dev_err(dev, "CS %d already assigned\n", tmp); - return -EINVAL; - } - - chip->sels[i].cs = tmp; - - tmp = rb[i]; - if (tmp >= 0 && tmp < 2) { - chip->sels[i].rb.type = RB_NATIVE; - chip->sels[i].rb.info.nativeid = tmp; - } else { - ret = gpio_request_by_name_nodev(offset_to_ofnode(node), - "rb-gpios", i, - &chip->sels[i].rb.info.gpio, - GPIOD_IS_IN); - if (ret) - chip->sels[i].rb.type = RB_GPIO; - else - chip->sels[i].rb.type = RB_NONE; - } - } - - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) { - ret = PTR_ERR(timings); - dev_err(dev, - "could not retrieve timings for ONFI mode 0: %d\n", - ret); - return ret; - } - - ret = sunxi_nand_chip_set_timings(chip, timings); - if (ret) { - dev_err(dev, "could not configure chip timings: %d\n", ret); - return ret; - } - - nand = &chip->nand; - /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ - nand->chip_delay = 200; - nand->controller = &nfc->controller; - /* - * Set the ECC mode to the default value in case nothing is specified - * in the DT. - */ - nand->ecc.mode = NAND_ECC_HW; - nand->flash_node = node; - nand->select_chip = sunxi_nfc_select_chip; - nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; - nand->read_buf = sunxi_nfc_read_buf; - nand->write_buf = sunxi_nfc_write_buf; - nand->read_byte = sunxi_nfc_read_byte; - - mtd = nand_to_mtd(nand); - ret = nand_scan_ident(mtd, nsels, NULL); - if (ret) - return ret; - - if (nand->bbt_options & NAND_BBT_USE_FLASH) - nand->bbt_options |= NAND_BBT_NO_OOB; - - if (nand->options & NAND_NEED_SCRAMBLING) - nand->options |= NAND_NO_SUBPAGE_WRITE; - - nand->options |= NAND_SUBPAGE_READ; - - ret = sunxi_nand_chip_init_timings(chip); - if (ret) { - dev_err(dev, "could not configure chip timings: %d\n", ret); - return ret; - } - - ret = sunxi_nand_ecc_init(mtd, &nand->ecc); - if (ret) { - dev_err(dev, "ECC init failed: %d\n", ret); - return ret; - } - - ret = nand_scan_tail(mtd); - if (ret) { - dev_err(dev, "nand_scan_tail failed: %d\n", ret); - return ret; - } - - ret = nand_register(devnum, mtd); - if (ret) { - dev_err(dev, "failed to register mtd device: %d\n", ret); - return ret; - } - - list_add_tail(&chip->node, &nfc->chips); - - return 0; -} - -static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc) -{ - const void *blob = gd->fdt_blob; - int nand_node; - int ret, i = 0; - - for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0; - nand_node = fdt_next_subnode(blob, nand_node)) - i++; - - if (i > 8) { - dev_err(dev, "too many NAND chips: %d (max = 8)\n", i); - return -EINVAL; - } - - i = 0; - for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0; - nand_node = fdt_next_subnode(blob, nand_node)) { - ret = sunxi_nand_chip_init(nand_node, nfc, i++); - if (ret) - return ret; - } - - return 0; -} - -#ifndef __UBOOT__ -static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) -{ - struct sunxi_nand_chip *chip; - - while (!list_empty(&nfc->chips)) { - chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, - node); - nand_release(&chip->mtd); - sunxi_nand_ecc_cleanup(&chip->nand.ecc); - list_del(&chip->node); - kfree(chip); - } -} -#endif /* __UBOOT__ */ - -void sunxi_nand_init(void) -{ - const void *blob = gd->fdt_blob; - struct sunxi_nfc *nfc; - fdt_addr_t regs; - int node; - int ret; - - nfc = kzalloc(sizeof(*nfc), GFP_KERNEL); - if (!nfc) - return; - - spin_lock_init(&nfc->controller.lock); - init_waitqueue_head(&nfc->controller.wq); - INIT_LIST_HEAD(&nfc->chips); - - node = fdtdec_next_compatible(blob, 0, COMPAT_SUNXI_NAND); - if (node < 0) { - pr_err("unable to find nfc node in device tree\n"); - goto err; - } - - if (!fdtdec_get_is_enabled(blob, node)) { - pr_err("nfc disabled in device tree\n"); - goto err; - } - - regs = fdtdec_get_addr(blob, node, "reg"); - if (regs == FDT_ADDR_T_NONE) { - pr_err("unable to find nfc address in device tree\n"); - goto err; - } - - nfc->regs = (void *)regs; - - ret = sunxi_nfc_rst(nfc); - if (ret) - goto err; - - ret = sunxi_nand_chips_init(node, nfc); - if (ret) { - dev_err(dev, "failed to init nand chips\n"); - goto err; - } - - return; - -err: - kfree(nfc); -} - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Boris BREZILLON"); -MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c deleted file mode 100644 index 6cde9814c4..0000000000 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ /dev/null @@ -1,548 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2014-2015, Antmicro Ltd - * Copyright (c) 2015, AW-SOM Technologies - */ - -#include -#include -#include -#include -#include -#include - -/* registers */ -#define NFC_CTL 0x00000000 -#define NFC_ST 0x00000004 -#define NFC_INT 0x00000008 -#define NFC_TIMING_CTL 0x0000000C -#define NFC_TIMING_CFG 0x00000010 -#define NFC_ADDR_LOW 0x00000014 -#define NFC_ADDR_HIGH 0x00000018 -#define NFC_SECTOR_NUM 0x0000001C -#define NFC_CNT 0x00000020 -#define NFC_CMD 0x00000024 -#define NFC_RCMD_SET 0x00000028 -#define NFC_WCMD_SET 0x0000002C -#define NFC_IO_DATA 0x00000030 -#define NFC_ECC_CTL 0x00000034 -#define NFC_ECC_ST 0x00000038 -#define NFC_DEBUG 0x0000003C -#define NFC_ECC_CNT0 0x00000040 -#define NFC_ECC_CNT1 0x00000044 -#define NFC_ECC_CNT2 0x00000048 -#define NFC_ECC_CNT3 0x0000004C -#define NFC_USER_DATA_BASE 0x00000050 -#define NFC_EFNAND_STATUS 0x00000090 -#define NFC_SPARE_AREA 0x000000A0 -#define NFC_PATTERN_ID 0x000000A4 -#define NFC_RAM0_BASE 0x00000400 -#define NFC_RAM1_BASE 0x00000800 - -#define NFC_CTL_EN (1 << 0) -#define NFC_CTL_RESET (1 << 1) -#define NFC_CTL_RAM_METHOD (1 << 14) -#define NFC_CTL_PAGE_SIZE_MASK (0xf << 8) -#define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) - - -#define NFC_ECC_EN (1 << 0) -#define NFC_ECC_PIPELINE (1 << 3) -#define NFC_ECC_EXCEPTION (1 << 4) -#define NFC_ECC_BLOCK_SIZE (1 << 5) -#define NFC_ECC_RANDOM_EN (1 << 9) -#define NFC_ECC_RANDOM_DIRECTION (1 << 10) - - -#define NFC_ADDR_NUM_OFFSET 16 -#define NFC_SEND_ADDR (1 << 19) -#define NFC_ACCESS_DIR (1 << 20) -#define NFC_DATA_TRANS (1 << 21) -#define NFC_SEND_CMD1 (1 << 22) -#define NFC_WAIT_FLAG (1 << 23) -#define NFC_SEND_CMD2 (1 << 24) -#define NFC_SEQ (1 << 25) -#define NFC_DATA_SWAP_METHOD (1 << 26) -#define NFC_ROW_AUTO_INC (1 << 27) -#define NFC_SEND_CMD3 (1 << 28) -#define NFC_SEND_CMD4 (1 << 29) -#define NFC_RAW_CMD (0 << 30) -#define NFC_ECC_CMD (1 << 30) -#define NFC_PAGE_CMD (2 << 30) - -#define NFC_ST_CMD_INT_FLAG (1 << 1) -#define NFC_ST_DMA_INT_FLAG (1 << 2) -#define NFC_ST_CMD_FIFO_STAT (1 << 3) - -#define NFC_READ_CMD_OFFSET 0 -#define NFC_RANDOM_READ_CMD0_OFFSET 8 -#define NFC_RANDOM_READ_CMD1_OFFSET 16 - -#define NFC_CMD_RNDOUTSTART 0xE0 -#define NFC_CMD_RNDOUT 0x05 -#define NFC_CMD_READSTART 0x30 - -struct nfc_config { - int page_size; - int ecc_strength; - int ecc_size; - int addr_cycles; - int nseeds; - bool randomize; - bool valid; -}; - -/* minimal "boot0" style NAND support for Allwinner A20 */ - -/* random seed used by linux */ -const uint16_t random_seed[128] = { - 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, - 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, - 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, - 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, - 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, - 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, - 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, - 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, - 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, - 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, - 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, - 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, - 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, - 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, - 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, - 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, -}; - -#define DEFAULT_TIMEOUT_US 100000 - -static int check_value_inner(int offset, int expected_bits, - int timeout_us, int negation) -{ - do { - int val = readl(offset) & expected_bits; - if (negation ? !val : val) - return 1; - udelay(1); - } while (--timeout_us); - - return 0; -} - -static inline int check_value(int offset, int expected_bits, - int timeout_us) -{ - return check_value_inner(offset, expected_bits, timeout_us, 0); -} - -static inline int check_value_negated(int offset, int unexpected_bits, - int timeout_us) -{ - return check_value_inner(offset, unexpected_bits, timeout_us, 1); -} - -static int nand_wait_cmd_fifo_empty(void) -{ - if (!check_value_negated(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_FIFO_STAT, - DEFAULT_TIMEOUT_US)) { - printf("nand: timeout waiting for empty cmd FIFO\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int nand_wait_int(void) -{ - if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, - DEFAULT_TIMEOUT_US)) { - printf("nand: timeout waiting for interruption\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int nand_exec_cmd(u32 cmd) -{ - int ret; - - ret = nand_wait_cmd_fifo_empty(); - if (ret) - return ret; - - writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(cmd, SUNXI_NFC_BASE + NFC_CMD); - - return nand_wait_int(); -} - -void nand_init(void) -{ - uint32_t val; - - board_nand_init(); - - val = readl(SUNXI_NFC_BASE + NFC_CTL); - /* enable and reset CTL */ - writel(val | NFC_CTL_EN | NFC_CTL_RESET, - SUNXI_NFC_BASE + NFC_CTL); - - if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, - NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { - printf("Couldn't initialize nand\n"); - } - - /* reset NAND */ - nand_exec_cmd(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET); -} - -static void nand_apply_config(const struct nfc_config *conf) -{ - u32 val; - - nand_wait_cmd_fifo_empty(); - - val = readl(SUNXI_NFC_BASE + NFC_CTL); - val &= ~NFC_CTL_PAGE_SIZE_MASK; - writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size), - SUNXI_NFC_BASE + NFC_CTL); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); - writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA); -} - -static int nand_load_page(const struct nfc_config *conf, u32 offs) -{ - int page = offs / conf->page_size; - - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | - (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); - writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); - - return nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | - NFC_SEND_ADDR | NFC_WAIT_FLAG | - ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET)); -} - -static int nand_change_column(u16 column) -{ - int ret; - - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | - (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | - (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), - SUNXI_NFC_BASE + NFC_RCMD_SET); - writel(column, SUNXI_NFC_BASE + NFC_ADDR_LOW); - - ret = nand_exec_cmd(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | - (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADDR | - NFC_CMD_RNDOUT); - if (ret) - return ret; - - /* Ensure tCCS has passed before reading data */ - udelay(1); - - return 0; -} - -static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116}; - -static int nand_read_page(const struct nfc_config *conf, u32 offs, - void *dest, int len) -{ - int nsectors = len / conf->ecc_size; - u16 rand_seed = 0; - int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; - int page = offs / conf->page_size; - u32 ecc_st; - int i; - - if (offs % conf->page_size || len % conf->ecc_size || - len > conf->page_size || len < 0) - return -EINVAL; - - /* Choose correct seed if randomized */ - if (conf->randomize) - rand_seed = random_seed[page % conf->nseeds]; - - /* Retrieve data from SRAM (PIO) */ - for (i = 0; i < nsectors; i++) { - int data_off = i * conf->ecc_size; - int oob_off = conf->page_size + (i * oob_chunk_sz); - u8 *data = dest + data_off; - - /* Clear ECC status and restart ECC engine */ - writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); - writel((rand_seed << 16) | (conf->ecc_strength << 12) | - (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | - (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | - NFC_ECC_EN | NFC_ECC_EXCEPTION, - SUNXI_NFC_BASE + NFC_ECC_CTL); - - /* Move the data in SRAM */ - nand_change_column(data_off); - writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); - nand_exec_cmd(NFC_DATA_TRANS); - - /* - * Let the ECC engine consume the ECC bytes and possibly correct - * the data. - */ - nand_change_column(oob_off); - nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD); - - /* Get the ECC status */ - ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST); - - /* ECC error detected. */ - if (ecc_st & 0xffff) - return -EIO; - - /* - * Return 1 if the first chunk is empty (needed for - * configuration detection). - */ - if (!i && (ecc_st & 0x10000)) - return 1; - - /* Retrieve the data from SRAM */ - memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE, - conf->ecc_size); - - /* Stop the ECC engine */ - writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN, - SUNXI_NFC_BASE + NFC_ECC_CTL); - - if (data_off + conf->ecc_size >= len) - break; - } - - return 0; -} - -static int nand_max_ecc_strength(struct nfc_config *conf) -{ - int max_oobsize, max_ecc_bytes; - int nsectors = conf->page_size / conf->ecc_size; - int i; - - /* - * ECC strength is limited by the size of the OOB area which is - * correlated with the page size. - */ - switch (conf->page_size) { - case 2048: - max_oobsize = 64; - break; - case 4096: - max_oobsize = 256; - break; - case 8192: - max_oobsize = 640; - break; - case 16384: - max_oobsize = 1664; - break; - default: - return -EINVAL; - } - - max_ecc_bytes = max_oobsize / nsectors; - - for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { - if (ecc_bytes[i] > max_ecc_bytes) - break; - } - - if (!i) - return -EINVAL; - - return i - 1; -} - -static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, - void *dest) -{ - /* NAND with pages > 4k will likely require 1k sector size. */ - int min_ecc_size = conf->page_size > 4096 ? 1024 : 512; - int page = offs / conf->page_size; - int ret; - - /* - * In most cases, 1k sectors are preferred over 512b ones, start - * testing this config first. - */ - for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size; - conf->ecc_size >>= 1) { - int max_ecc_strength = nand_max_ecc_strength(conf); - - nand_apply_config(conf); - - /* - * We are starting from the maximum ECC strength because - * most of the time NAND vendors provide an OOB area that - * barely meets the ECC requirements. - */ - for (conf->ecc_strength = max_ecc_strength; - conf->ecc_strength >= 0; - conf->ecc_strength--) { - conf->randomize = false; - if (nand_change_column(0)) - return -EIO; - - /* - * Only read the first sector to speedup detection. - */ - ret = nand_read_page(conf, offs, dest, conf->ecc_size); - if (!ret) { - return 0; - } else if (ret > 0) { - /* - * If page is empty we can't deduce anything - * about the ECC config => stop the detection. - */ - return -EINVAL; - } - - conf->randomize = true; - conf->nseeds = ARRAY_SIZE(random_seed); - do { - if (nand_change_column(0)) - return -EIO; - - if (!nand_read_page(conf, offs, dest, - conf->ecc_size)) - return 0; - - /* - * Find the next ->nseeds value that would - * change the randomizer seed for the page - * we're trying to read. - */ - while (conf->nseeds >= 16) { - int seed = page % conf->nseeds; - - conf->nseeds >>= 1; - if (seed != page % conf->nseeds) - break; - } - } while (conf->nseeds >= 16); - } - } - - return -EINVAL; -} - -static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) -{ - if (conf->valid) - return 0; - - /* - * Modern NANDs are more likely than legacy ones, so we start testing - * with 5 address cycles. - */ - for (conf->addr_cycles = 5; - conf->addr_cycles >= 4; - conf->addr_cycles--) { - int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384; - - /* - * Ignoring 1k pages cause I'm not even sure this case exist - * in the real world. - */ - for (conf->page_size = 2048; conf->page_size <= max_page_size; - conf->page_size <<= 1) { - if (nand_load_page(conf, offs)) - return -1; - - if (!nand_detect_ecc_config(conf, offs, dest)) { - conf->valid = true; - return 0; - } - } - } - - return -EINVAL; -} - -static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, - unsigned int size, void *dest) -{ - int first_seed = 0, page, ret; - - size = ALIGN(size, conf->page_size); - page = offs / conf->page_size; - if (conf->randomize) - first_seed = page % conf->nseeds; - - for (; size; size -= conf->page_size) { - if (nand_load_page(conf, offs)) - return -1; - - ret = nand_read_page(conf, offs, dest, conf->page_size); - /* - * The ->nseeds value should be equal to the number of pages - * in an eraseblock. Since we don't know this information in - * advance we might have picked a wrong value. - */ - if (ret < 0 && conf->randomize) { - int cur_seed = page % conf->nseeds; - - /* - * We already tried all the seed values => we are - * facing a real corruption. - */ - if (cur_seed < first_seed) - return -EIO; - - /* Try to adjust ->nseeds and read the page again... */ - conf->nseeds = cur_seed; - - if (nand_change_column(0)) - return -EIO; - - /* ... it still fails => it's a real corruption. */ - if (nand_read_page(conf, offs, dest, conf->page_size)) - return -EIO; - } else if (ret && conf->randomize) { - memset(dest, 0xff, conf->page_size); - } - - page++; - offs += conf->page_size; - dest += conf->page_size; - } - - return 0; -} - -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) -{ - static struct nfc_config conf = { }; - int ret; - - ret = nand_detect_config(&conf, offs, dest); - if (ret) - return ret; - - return nand_read_buffer(&conf, offs, size, dest); -} - -void nand_deselect(void) -{ - struct sunxi_ccm_reg *const ccm = - (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; - - clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); -#ifdef CONFIG_MACH_SUN9I - clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); -#else - clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); -#endif - clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); -} diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c deleted file mode 100644 index 74acdfb308..0000000000 --- a/drivers/mtd/nand/tegra_nand.c +++ /dev/null @@ -1,1002 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2011 The Chromium OS Authors. - * (C) Copyright 2011 NVIDIA Corporation - * (C) Copyright 2006 Detlev Zundel, dzu@denx.de - * (C) Copyright 2006 DENX Software Engineering - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tegra_nand.h" - -DECLARE_GLOBAL_DATA_PTR; - -#define NAND_CMD_TIMEOUT_MS 10 - -#define SKIPPED_SPARE_BYTES 4 - -/* ECC bytes to be generated for tag data */ -#define TAG_ECC_BYTES 4 - -static const struct udevice_id tegra_nand_dt_ids[] = { - { - .compatible = "nvidia,tegra20-nand", - }, - { /* sentinel */ } -}; - -/* 64 byte oob block info for large page (== 2KB) device - * - * OOB flash layout for Tegra with Reed-Solomon 4 symbol correct ECC: - * Skipped bytes(4) - * Main area Ecc(36) - * Tag data(20) - * Tag data Ecc(4) - * - * Yaffs2 will use 16 tag bytes. - */ -static struct nand_ecclayout eccoob = { - .eccbytes = 36, - .eccpos = { - 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, - }, - .oobavail = 20, - .oobfree = { - { - .offset = 40, - .length = 20, - }, - } -}; - -enum { - ECC_OK, - ECC_TAG_ERROR = 1 << 0, - ECC_DATA_ERROR = 1 << 1 -}; - -/* Timing parameters */ -enum { - FDT_NAND_MAX_TRP_TREA, - FDT_NAND_TWB, - FDT_NAND_MAX_TCR_TAR_TRR, - FDT_NAND_TWHR, - FDT_NAND_MAX_TCS_TCH_TALS_TALH, - FDT_NAND_TWH, - FDT_NAND_TWP, - FDT_NAND_TRH, - FDT_NAND_TADL, - - FDT_NAND_TIMING_COUNT -}; - -/* Information about an attached NAND chip */ -struct fdt_nand { - struct nand_ctlr *reg; - int enabled; /* 1 to enable, 0 to disable */ - struct gpio_desc wp_gpio; /* write-protect GPIO */ - s32 width; /* bit width, normally 8 */ - u32 timing[FDT_NAND_TIMING_COUNT]; -}; - -struct nand_drv { - struct nand_ctlr *reg; - struct fdt_nand config; -}; - -struct tegra_nand_info { - struct udevice *dev; - struct nand_drv nand_ctrl; - struct nand_chip nand_chip; -}; - -/** - * Wait for command completion - * - * @param reg nand_ctlr structure - * @return - * 1 - Command completed - * 0 - Timeout - */ -static int nand_waitfor_cmd_completion(struct nand_ctlr *reg) -{ - u32 reg_val; - int running; - int i; - - for (i = 0; i < NAND_CMD_TIMEOUT_MS * 1000; i++) { - if ((readl(®->command) & CMD_GO) || - !(readl(®->status) & STATUS_RBSY0) || - !(readl(®->isr) & ISR_IS_CMD_DONE)) { - udelay(1); - continue; - } - reg_val = readl(®->dma_mst_ctrl); - /* - * If DMA_MST_CTRL_EN_A_ENABLE or DMA_MST_CTRL_EN_B_ENABLE - * is set, that means DMA engine is running. - * - * Then we have to wait until DMA_MST_CTRL_IS_DMA_DONE - * is cleared, indicating DMA transfer completion. - */ - running = reg_val & (DMA_MST_CTRL_EN_A_ENABLE | - DMA_MST_CTRL_EN_B_ENABLE); - if (!running || (reg_val & DMA_MST_CTRL_IS_DMA_DONE)) - return 1; - udelay(1); - } - return 0; -} - -/** - * Read one byte from the chip - * - * @param mtd MTD device structure - * @return data byte - * - * Read function for 8bit bus-width - */ -static uint8_t read_byte(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_drv *info; - - info = (struct nand_drv *)nand_get_controller_data(chip); - - writel(CMD_GO | CMD_PIO | CMD_RX | CMD_CE0 | CMD_A_VALID, - &info->reg->command); - if (!nand_waitfor_cmd_completion(info->reg)) - printf("Command timeout\n"); - - return (uint8_t)readl(&info->reg->resp); -} - -/** - * Read len bytes from the chip into a buffer - * - * @param mtd MTD device structure - * @param buf buffer to store data to - * @param len number of bytes to read - * - * Read function for 8bit bus-width - */ -static void read_buf(struct mtd_info *mtd, uint8_t *buf, int len) -{ - int i, s; - unsigned int reg; - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_drv *info = (struct nand_drv *)nand_get_controller_data(chip); - - for (i = 0; i < len; i += 4) { - s = (len - i) > 4 ? 4 : len - i; - writel(CMD_PIO | CMD_RX | CMD_A_VALID | CMD_CE0 | - ((s - 1) << CMD_TRANS_SIZE_SHIFT) | CMD_GO, - &info->reg->command); - if (!nand_waitfor_cmd_completion(info->reg)) - puts("Command timeout during read_buf\n"); - reg = readl(&info->reg->resp); - memcpy(buf + i, ®, s); - } -} - -/** - * Check NAND status to see if it is ready or not - * - * @param mtd MTD device structure - * @return - * 1 - ready - * 0 - not ready - */ -static int nand_dev_ready(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - int reg_val; - struct nand_drv *info; - - info = (struct nand_drv *)nand_get_controller_data(chip); - - reg_val = readl(&info->reg->status); - if (reg_val & STATUS_RBSY0) - return 1; - else - return 0; -} - -/* Dummy implementation: we don't support multiple chips */ -static void nand_select_chip(struct mtd_info *mtd, int chipnr) -{ - switch (chipnr) { - case -1: - case 0: - break; - - default: - BUG(); - } -} - -/** - * Clear all interrupt status bits - * - * @param reg nand_ctlr structure - */ -static void nand_clear_interrupt_status(struct nand_ctlr *reg) -{ - u32 reg_val; - - /* Clear interrupt status */ - reg_val = readl(®->isr); - writel(reg_val, ®->isr); -} - -/** - * Send command to NAND device - * - * @param mtd MTD device structure - * @param command the command to be sent - * @param column the column address for this command, -1 if none - * @param page_addr the page address for this command, -1 if none - */ -static void nand_command(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_drv *info; - - info = (struct nand_drv *)nand_get_controller_data(chip); - - /* - * Write out the command to the device. - * - * Only command NAND_CMD_RESET or NAND_CMD_READID will come - * here before mtd->writesize is initialized. - */ - - /* Emulate NAND_CMD_READOOB */ - if (command == NAND_CMD_READOOB) { - assert(mtd->writesize != 0); - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* Adjust columns for 16 bit bus-width */ - if (column != -1 && (chip->options & NAND_BUSWIDTH_16)) - column >>= 1; - - nand_clear_interrupt_status(info->reg); - - /* Stop DMA engine, clear DMA completion status */ - writel(DMA_MST_CTRL_EN_A_DISABLE - | DMA_MST_CTRL_EN_B_DISABLE - | DMA_MST_CTRL_IS_DMA_DONE, - &info->reg->dma_mst_ctrl); - - /* - * Program and erase have their own busy handlers - * status and sequential in needs no delay - */ - switch (command) { - case NAND_CMD_READID: - writel(NAND_CMD_READID, &info->reg->cmd_reg1); - writel(column & 0xFF, &info->reg->addr_reg1); - writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0, - &info->reg->command); - break; - case NAND_CMD_PARAM: - writel(NAND_CMD_PARAM, &info->reg->cmd_reg1); - writel(column & 0xFF, &info->reg->addr_reg1); - writel(CMD_GO | CMD_CLE | CMD_ALE | CMD_CE0, - &info->reg->command); - break; - case NAND_CMD_READ0: - writel(NAND_CMD_READ0, &info->reg->cmd_reg1); - writel(NAND_CMD_READSTART, &info->reg->cmd_reg2); - writel((page_addr << 16) | (column & 0xFFFF), - &info->reg->addr_reg1); - writel(page_addr >> 16, &info->reg->addr_reg2); - return; - case NAND_CMD_SEQIN: - writel(NAND_CMD_SEQIN, &info->reg->cmd_reg1); - writel(NAND_CMD_PAGEPROG, &info->reg->cmd_reg2); - writel((page_addr << 16) | (column & 0xFFFF), - &info->reg->addr_reg1); - writel(page_addr >> 16, - &info->reg->addr_reg2); - return; - case NAND_CMD_PAGEPROG: - return; - case NAND_CMD_ERASE1: - writel(NAND_CMD_ERASE1, &info->reg->cmd_reg1); - writel(NAND_CMD_ERASE2, &info->reg->cmd_reg2); - writel(page_addr, &info->reg->addr_reg1); - writel(CMD_GO | CMD_CLE | CMD_ALE | - CMD_SEC_CMD | CMD_CE0 | CMD_ALE_BYTES3, - &info->reg->command); - break; - case NAND_CMD_ERASE2: - return; - case NAND_CMD_STATUS: - writel(NAND_CMD_STATUS, &info->reg->cmd_reg1); - writel(CMD_GO | CMD_CLE | CMD_PIO | CMD_RX - | ((1 - 0) << CMD_TRANS_SIZE_SHIFT) - | CMD_CE0, - &info->reg->command); - break; - case NAND_CMD_RESET: - writel(NAND_CMD_RESET, &info->reg->cmd_reg1); - writel(CMD_GO | CMD_CLE | CMD_CE0, - &info->reg->command); - break; - case NAND_CMD_RNDOUT: - default: - printf("%s: Unsupported command %d\n", __func__, command); - return; - } - if (!nand_waitfor_cmd_completion(info->reg)) - printf("Command 0x%02X timeout\n", command); -} - -/** - * Check whether the pointed buffer are all 0xff (blank). - * - * @param buf data buffer for blank check - * @param len length of the buffer in byte - * @return - * 1 - blank - * 0 - non-blank - */ -static int blank_check(u8 *buf, int len) -{ - int i; - - for (i = 0; i < len; i++) - if (buf[i] != 0xFF) - return 0; - return 1; -} - -/** - * After a DMA transfer for read, we call this function to see whether there - * is any uncorrectable error on the pointed data buffer or oob buffer. - * - * @param reg nand_ctlr structure - * @param databuf data buffer - * @param a_len data buffer length - * @param oobbuf oob buffer - * @param b_len oob buffer length - * @return - * ECC_OK - no ECC error or correctable ECC error - * ECC_TAG_ERROR - uncorrectable tag ECC error - * ECC_DATA_ERROR - uncorrectable data ECC error - * ECC_DATA_ERROR + ECC_TAG_ERROR - uncorrectable data+tag ECC error - */ -static int check_ecc_error(struct nand_ctlr *reg, u8 *databuf, - int a_len, u8 *oobbuf, int b_len) -{ - int return_val = ECC_OK; - u32 reg_val; - - if (!(readl(®->isr) & ISR_IS_ECC_ERR)) - return ECC_OK; - - /* - * Area A is used for the data block (databuf). Area B is used for - * the spare block (oobbuf) - */ - reg_val = readl(®->dec_status); - if ((reg_val & DEC_STATUS_A_ECC_FAIL) && databuf) { - reg_val = readl(®->bch_dec_status_buf); - /* - * If uncorrectable error occurs on data area, then see whether - * they are all FF. If all are FF, it's a blank page. - * Not error. - */ - if ((reg_val & BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK) && - !blank_check(databuf, a_len)) - return_val |= ECC_DATA_ERROR; - } - - if ((reg_val & DEC_STATUS_B_ECC_FAIL) && oobbuf) { - reg_val = readl(®->bch_dec_status_buf); - /* - * If uncorrectable error occurs on tag area, then see whether - * they are all FF. If all are FF, it's a blank page. - * Not error. - */ - if ((reg_val & BCH_DEC_STATUS_FAIL_TAG_MASK) && - !blank_check(oobbuf, b_len)) - return_val |= ECC_TAG_ERROR; - } - - return return_val; -} - -/** - * Set GO bit to send command to device - * - * @param reg nand_ctlr structure - */ -static void start_command(struct nand_ctlr *reg) -{ - u32 reg_val; - - reg_val = readl(®->command); - reg_val |= CMD_GO; - writel(reg_val, ®->command); -} - -/** - * Clear command GO bit, DMA GO bit, and DMA completion status - * - * @param reg nand_ctlr structure - */ -static void stop_command(struct nand_ctlr *reg) -{ - /* Stop command */ - writel(0, ®->command); - - /* Stop DMA engine and clear DMA completion status */ - writel(DMA_MST_CTRL_GO_DISABLE - | DMA_MST_CTRL_IS_DMA_DONE, - ®->dma_mst_ctrl); -} - -/** - * Set up NAND bus width and page size - * - * @param info nand_info structure - * @param *reg_val address of reg_val - * @return 0 if ok, -1 on error - */ -static int set_bus_width_page_size(struct mtd_info *our_mtd, - struct fdt_nand *config, u32 *reg_val) -{ - if (config->width == 8) - *reg_val = CFG_BUS_WIDTH_8BIT; - else if (config->width == 16) - *reg_val = CFG_BUS_WIDTH_16BIT; - else { - debug("%s: Unsupported bus width %d\n", __func__, - config->width); - return -1; - } - - if (our_mtd->writesize == 512) - *reg_val |= CFG_PAGE_SIZE_512; - else if (our_mtd->writesize == 2048) - *reg_val |= CFG_PAGE_SIZE_2048; - else if (our_mtd->writesize == 4096) - *reg_val |= CFG_PAGE_SIZE_4096; - else { - debug("%s: Unsupported page size %d\n", __func__, - our_mtd->writesize); - return -1; - } - - return 0; -} - -/** - * Page read/write function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param buf data buffer - * @param page page number - * @param with_ecc 1 to enable ECC, 0 to disable ECC - * @param is_writing 0 for read, 1 for write - * @return 0 when successfully completed - * -EIO when command timeout - */ -static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page, int with_ecc, int is_writing) -{ - u32 reg_val; - int tag_size; - struct nand_oobfree *free = chip->ecc.layout->oobfree; - /* 4*128=512 (byte) is the value that our HW can support. */ - ALLOC_CACHE_ALIGN_BUFFER(u32, tag_buf, 128); - char *tag_ptr; - struct nand_drv *info; - struct fdt_nand *config; - unsigned int bbflags; - struct bounce_buffer bbstate, bbstate_oob; - - if ((uintptr_t)buf & 0x03) { - printf("buf %p has to be 4-byte aligned\n", buf); - return -EINVAL; - } - - info = (struct nand_drv *)nand_get_controller_data(chip); - config = &info->config; - if (set_bus_width_page_size(mtd, config, ®_val)) - return -EINVAL; - - /* Need to be 4-byte aligned */ - tag_ptr = (char *)tag_buf; - - stop_command(info->reg); - - if (is_writing) - bbflags = GEN_BB_READ; - else - bbflags = GEN_BB_WRITE; - - bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift, - bbflags); - writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a); - writel(virt_to_phys(bbstate.bounce_buffer), &info->reg->data_block_ptr); - - /* Set ECC selection, configure ECC settings */ - if (with_ecc) { - if (is_writing) - memcpy(tag_ptr, chip->oob_poi + free->offset, - chip->ecc.layout->oobavail + TAG_ECC_BYTES); - tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES; - reg_val |= (CFG_SKIP_SPARE_SEL_4 - | CFG_SKIP_SPARE_ENABLE - | CFG_HW_ECC_CORRECTION_ENABLE - | CFG_ECC_EN_TAG_DISABLE - | CFG_HW_ECC_SEL_RS - | CFG_HW_ECC_ENABLE - | CFG_TVAL4 - | (tag_size - 1)); - - if (!is_writing) - tag_size += SKIPPED_SPARE_BYTES; - bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size, - bbflags); - } else { - tag_size = mtd->oobsize; - reg_val |= (CFG_SKIP_SPARE_DISABLE - | CFG_HW_ECC_CORRECTION_DISABLE - | CFG_ECC_EN_TAG_DISABLE - | CFG_HW_ECC_DISABLE - | (tag_size - 1)); - bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, - tag_size, bbflags); - } - writel(reg_val, &info->reg->config); - writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr); - writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); - writel(tag_size - 1, &info->reg->dma_cfg_b); - - nand_clear_interrupt_status(info->reg); - - reg_val = CMD_CLE | CMD_ALE - | CMD_SEC_CMD - | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) - | CMD_A_VALID - | CMD_B_VALID - | (CMD_TRANS_SIZE_PAGE << CMD_TRANS_SIZE_SHIFT) - | CMD_CE0; - if (!is_writing) - reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); - else - reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); - writel(reg_val, &info->reg->command); - - /* Setup DMA engine */ - reg_val = DMA_MST_CTRL_GO_ENABLE - | DMA_MST_CTRL_BURST_8WORDS - | DMA_MST_CTRL_EN_A_ENABLE - | DMA_MST_CTRL_EN_B_ENABLE; - - if (!is_writing) - reg_val |= DMA_MST_CTRL_DIR_READ; - else - reg_val |= DMA_MST_CTRL_DIR_WRITE; - - writel(reg_val, &info->reg->dma_mst_ctrl); - - start_command(info->reg); - - if (!nand_waitfor_cmd_completion(info->reg)) { - if (!is_writing) - printf("Read Page 0x%X timeout ", page); - else - printf("Write Page 0x%X timeout ", page); - if (with_ecc) - printf("with ECC"); - else - printf("without ECC"); - printf("\n"); - return -EIO; - } - - bounce_buffer_stop(&bbstate_oob); - bounce_buffer_stop(&bbstate); - - if (with_ecc && !is_writing) { - memcpy(chip->oob_poi, tag_ptr, - SKIPPED_SPARE_BYTES); - memcpy(chip->oob_poi + free->offset, - tag_ptr + SKIPPED_SPARE_BYTES, - chip->ecc.layout->oobavail); - reg_val = (u32)check_ecc_error(info->reg, (u8 *)buf, - 1 << chip->page_shift, - (u8 *)(tag_ptr + SKIPPED_SPARE_BYTES), - chip->ecc.layout->oobavail); - if (reg_val & ECC_TAG_ERROR) - printf("Read Page 0x%X tag ECC error\n", page); - if (reg_val & ECC_DATA_ERROR) - printf("Read Page 0x%X data ECC error\n", - page); - if (reg_val & (ECC_DATA_ERROR | ECC_TAG_ERROR)) - return -EIO; - } - return 0; -} - -/** - * Hardware ecc based page read function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param buf buffer to store read data - * @param page page number to read - * @return 0 when successfully completed - * -EIO when command timeout - */ -static int nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - return nand_rw_page(mtd, chip, buf, page, 1, 0); -} - -/** - * Hardware ecc based page write function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param buf data buffer - */ -static int nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, int oob_required, - int page) -{ - nand_rw_page(mtd, chip, (uint8_t *)buf, page, 1, 1); - return 0; -} - - -/** - * Read raw page data without ecc - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param buf buffer to store read data - * @param page page number to read - * @return 0 when successfully completed - * -EINVAL when chip->oob_poi is not double-word aligned - * -EIO when command timeout - */ -static int nand_read_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int oob_required, int page) -{ - return nand_rw_page(mtd, chip, buf, page, 0, 0); -} - -/** - * Raw page write function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param buf data buffer - */ -static int nand_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - nand_rw_page(mtd, chip, (uint8_t *)buf, page, 0, 1); - return 0; -} - -/** - * OOB data read/write function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param page page number to read - * @param with_ecc 1 to enable ECC, 0 to disable ECC - * @param is_writing 0 for read, 1 for write - * @return 0 when successfully completed - * -EINVAL when chip->oob_poi is not double-word aligned - * -EIO when command timeout - */ -static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int with_ecc, int is_writing) -{ - u32 reg_val; - int tag_size; - struct nand_oobfree *free = chip->ecc.layout->oobfree; - struct nand_drv *info; - unsigned int bbflags; - struct bounce_buffer bbstate_oob; - - if (((int)chip->oob_poi) & 0x03) - return -EINVAL; - info = (struct nand_drv *)nand_get_controller_data(chip); - if (set_bus_width_page_size(mtd, &info->config, ®_val)) - return -EINVAL; - - stop_command(info->reg); - - /* Set ECC selection */ - tag_size = mtd->oobsize; - if (with_ecc) - reg_val |= CFG_ECC_EN_TAG_ENABLE; - else - reg_val |= (CFG_ECC_EN_TAG_DISABLE); - - reg_val |= ((tag_size - 1) | - CFG_SKIP_SPARE_DISABLE | - CFG_HW_ECC_CORRECTION_DISABLE | - CFG_HW_ECC_DISABLE); - writel(reg_val, &info->reg->config); - - if (is_writing && with_ecc) - tag_size -= TAG_ECC_BYTES; - - if (is_writing) - bbflags = GEN_BB_READ; - else - bbflags = GEN_BB_WRITE; - - bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size, - bbflags); - writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr); - - writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config); - - writel(tag_size - 1, &info->reg->dma_cfg_b); - - nand_clear_interrupt_status(info->reg); - - reg_val = CMD_CLE | CMD_ALE - | CMD_SEC_CMD - | (CMD_ALE_BYTES5 << CMD_ALE_BYTE_SIZE_SHIFT) - | CMD_B_VALID - | CMD_CE0; - if (!is_writing) - reg_val |= (CMD_AFT_DAT_DISABLE | CMD_RX); - else - reg_val |= (CMD_AFT_DAT_ENABLE | CMD_TX); - writel(reg_val, &info->reg->command); - - /* Setup DMA engine */ - reg_val = DMA_MST_CTRL_GO_ENABLE - | DMA_MST_CTRL_BURST_8WORDS - | DMA_MST_CTRL_EN_B_ENABLE; - if (!is_writing) - reg_val |= DMA_MST_CTRL_DIR_READ; - else - reg_val |= DMA_MST_CTRL_DIR_WRITE; - - writel(reg_val, &info->reg->dma_mst_ctrl); - - start_command(info->reg); - - if (!nand_waitfor_cmd_completion(info->reg)) { - if (!is_writing) - printf("Read OOB of Page 0x%X timeout\n", page); - else - printf("Write OOB of Page 0x%X timeout\n", page); - return -EIO; - } - - bounce_buffer_stop(&bbstate_oob); - - if (with_ecc && !is_writing) { - reg_val = (u32)check_ecc_error(info->reg, 0, 0, - (u8 *)(chip->oob_poi + free->offset), - chip->ecc.layout->oobavail); - if (reg_val & ECC_TAG_ERROR) - printf("Read OOB of Page 0x%X tag ECC error\n", page); - } - return 0; -} - -/** - * OOB data read function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param page page number to read - */ -static int nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - nand_rw_oob(mtd, chip, page, 0, 0); - return 0; -} - -/** - * OOB data write function - * - * @param mtd mtd info structure - * @param chip nand chip info structure - * @param page page number to write - * @return 0 when successfully completed - * -EINVAL when chip->oob_poi is not double-word aligned - * -EIO when command timeout - */ -static int nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - - return nand_rw_oob(mtd, chip, page, 0, 1); -} - -/** - * Set up NAND memory timings according to the provided parameters - * - * @param timing Timing parameters - * @param reg NAND controller register address - */ -static void setup_timing(unsigned timing[FDT_NAND_TIMING_COUNT], - struct nand_ctlr *reg) -{ - u32 reg_val, clk_rate, clk_period, time_val; - - clk_rate = (u32)clock_get_periph_rate(PERIPH_ID_NDFLASH, - CLOCK_ID_PERIPH) / 1000000; - clk_period = 1000 / clk_rate; - reg_val = ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << - TIMING_TRP_RESP_CNT_SHIFT) & TIMING_TRP_RESP_CNT_MASK; - reg_val |= ((timing[FDT_NAND_TWB] / clk_period) << - TIMING_TWB_CNT_SHIFT) & TIMING_TWB_CNT_MASK; - time_val = timing[FDT_NAND_MAX_TCR_TAR_TRR] / clk_period; - if (time_val > 2) - reg_val |= ((time_val - 2) << TIMING_TCR_TAR_TRR_CNT_SHIFT) & - TIMING_TCR_TAR_TRR_CNT_MASK; - reg_val |= ((timing[FDT_NAND_TWHR] / clk_period) << - TIMING_TWHR_CNT_SHIFT) & TIMING_TWHR_CNT_MASK; - time_val = timing[FDT_NAND_MAX_TCS_TCH_TALS_TALH] / clk_period; - if (time_val > 1) - reg_val |= ((time_val - 1) << TIMING_TCS_CNT_SHIFT) & - TIMING_TCS_CNT_MASK; - reg_val |= ((timing[FDT_NAND_TWH] / clk_period) << - TIMING_TWH_CNT_SHIFT) & TIMING_TWH_CNT_MASK; - reg_val |= ((timing[FDT_NAND_TWP] / clk_period) << - TIMING_TWP_CNT_SHIFT) & TIMING_TWP_CNT_MASK; - reg_val |= ((timing[FDT_NAND_TRH] / clk_period) << - TIMING_TRH_CNT_SHIFT) & TIMING_TRH_CNT_MASK; - reg_val |= ((timing[FDT_NAND_MAX_TRP_TREA] / clk_period) << - TIMING_TRP_CNT_SHIFT) & TIMING_TRP_CNT_MASK; - writel(reg_val, ®->timing); - - reg_val = 0; - time_val = timing[FDT_NAND_TADL] / clk_period; - if (time_val > 2) - reg_val = (time_val - 2) & TIMING2_TADL_CNT_MASK; - writel(reg_val, ®->timing2); -} - -/** - * Decode NAND parameters from the device tree - * - * @param dev Driver model device - * @param config Device tree NAND configuration - * @return 0 if ok, -ve on error (FDT_ERR_...) - */ -static int fdt_decode_nand(struct udevice *dev, struct fdt_nand *config) -{ - int err; - - config->reg = (struct nand_ctlr *)dev_read_addr(dev); - config->enabled = dev_read_enabled(dev); - config->width = dev_read_u32_default(dev, "nvidia,nand-width", 8); - err = gpio_request_by_name(dev, "nvidia,wp-gpios", 0, &config->wp_gpio, - GPIOD_IS_OUT); - if (err) - return err; - err = dev_read_u32_array(dev, "nvidia,timing", config->timing, - FDT_NAND_TIMING_COUNT); - if (err < 0) - return err; - - return 0; -} - -static int tegra_probe(struct udevice *dev) -{ - struct tegra_nand_info *tegra = dev_get_priv(dev); - struct nand_chip *nand = &tegra->nand_chip; - struct nand_drv *info = &tegra->nand_ctrl; - struct fdt_nand *config = &info->config; - struct mtd_info *our_mtd; - int ret; - - if (fdt_decode_nand(dev, config)) { - printf("Could not decode nand-flash in device tree\n"); - return -1; - } - if (!config->enabled) - return -1; - info->reg = config->reg; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &eccoob; - - nand->options = LP_OPTIONS; - nand->cmdfunc = nand_command; - nand->read_byte = read_byte; - nand->read_buf = read_buf; - nand->ecc.read_page = nand_read_page_hwecc; - nand->ecc.write_page = nand_write_page_hwecc; - nand->ecc.read_page_raw = nand_read_page_raw; - nand->ecc.write_page_raw = nand_write_page_raw; - nand->ecc.read_oob = nand_read_oob; - nand->ecc.write_oob = nand_write_oob; - nand->ecc.strength = 1; - nand->select_chip = nand_select_chip; - nand->dev_ready = nand_dev_ready; - nand_set_controller_data(nand, &tegra->nand_ctrl); - - /* Disable subpage writes as we do not provide ecc->hwctl */ - nand->options |= NAND_NO_SUBPAGE_WRITE; - - /* Adjust controller clock rate */ - clock_start_periph_pll(PERIPH_ID_NDFLASH, CLOCK_ID_PERIPH, 52000000); - - /* Adjust timing for NAND device */ - setup_timing(config->timing, info->reg); - - dm_gpio_set_value(&config->wp_gpio, 1); - - our_mtd = nand_to_mtd(nand); - ret = nand_scan_ident(our_mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL); - if (ret) - return ret; - - nand->ecc.size = our_mtd->writesize; - nand->ecc.bytes = our_mtd->oobsize; - - ret = nand_scan_tail(our_mtd); - if (ret) - return ret; - - ret = nand_register(0, our_mtd); - if (ret) { - dev_err(dev, "Failed to register MTD: %d\n", ret); - return ret; - } - - return 0; -} - -U_BOOT_DRIVER(tegra_nand) = { - .name = "tegra-nand", - .id = UCLASS_MTD, - .of_match = tegra_nand_dt_ids, - .probe = tegra_probe, - .priv_auto_alloc_size = sizeof(struct tegra_nand_info), -}; - -void board_nand_init(void) -{ - struct udevice *dev; - int ret; - - ret = uclass_get_device_by_driver(UCLASS_MTD, - DM_GET_DRIVER(tegra_nand), &dev); - if (ret && ret != -ENODEV) - pr_err("Failed to initialize %s. (error %d)\n", dev->name, - ret); -} diff --git a/drivers/mtd/nand/tegra_nand.h b/drivers/mtd/nand/tegra_nand.h deleted file mode 100644 index 7740160661..0000000000 --- a/drivers/mtd/nand/tegra_nand.h +++ /dev/null @@ -1,240 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2011 NVIDIA Corporation - */ - -/* register offset */ -#define COMMAND_0 0x00 -#define CMD_GO (1 << 31) -#define CMD_CLE (1 << 30) -#define CMD_ALE (1 << 29) -#define CMD_PIO (1 << 28) -#define CMD_TX (1 << 27) -#define CMD_RX (1 << 26) -#define CMD_SEC_CMD (1 << 25) -#define CMD_AFT_DAT_MASK (1 << 24) -#define CMD_AFT_DAT_DISABLE 0 -#define CMD_AFT_DAT_ENABLE (1 << 24) -#define CMD_TRANS_SIZE_SHIFT 20 -#define CMD_TRANS_SIZE_PAGE 8 -#define CMD_A_VALID (1 << 19) -#define CMD_B_VALID (1 << 18) -#define CMD_RD_STATUS_CHK (1 << 17) -#define CMD_R_BSY_CHK (1 << 16) -#define CMD_CE7 (1 << 15) -#define CMD_CE6 (1 << 14) -#define CMD_CE5 (1 << 13) -#define CMD_CE4 (1 << 12) -#define CMD_CE3 (1 << 11) -#define CMD_CE2 (1 << 10) -#define CMD_CE1 (1 << 9) -#define CMD_CE0 (1 << 8) -#define CMD_CLE_BYTE_SIZE_SHIFT 4 -enum { - CMD_CLE_BYTES1 = 0, - CMD_CLE_BYTES2, - CMD_CLE_BYTES3, - CMD_CLE_BYTES4, -}; -#define CMD_ALE_BYTE_SIZE_SHIFT 0 -enum { - CMD_ALE_BYTES1 = 0, - CMD_ALE_BYTES2, - CMD_ALE_BYTES3, - CMD_ALE_BYTES4, - CMD_ALE_BYTES5, - CMD_ALE_BYTES6, - CMD_ALE_BYTES7, - CMD_ALE_BYTES8 -}; - -#define STATUS_0 0x04 -#define STATUS_RBSY0 (1 << 8) - -#define ISR_0 0x08 -#define ISR_IS_CMD_DONE (1 << 5) -#define ISR_IS_ECC_ERR (1 << 4) - -#define IER_0 0x0C - -#define CFG_0 0x10 -#define CFG_HW_ECC_MASK (1 << 31) -#define CFG_HW_ECC_DISABLE 0 -#define CFG_HW_ECC_ENABLE (1 << 31) -#define CFG_HW_ECC_SEL_MASK (1 << 30) -#define CFG_HW_ECC_SEL_HAMMING 0 -#define CFG_HW_ECC_SEL_RS (1 << 30) -#define CFG_HW_ECC_CORRECTION_MASK (1 << 29) -#define CFG_HW_ECC_CORRECTION_DISABLE 0 -#define CFG_HW_ECC_CORRECTION_ENABLE (1 << 29) -#define CFG_PIPELINE_EN_MASK (1 << 28) -#define CFG_PIPELINE_EN_DISABLE 0 -#define CFG_PIPELINE_EN_ENABLE (1 << 28) -#define CFG_ECC_EN_TAG_MASK (1 << 27) -#define CFG_ECC_EN_TAG_DISABLE 0 -#define CFG_ECC_EN_TAG_ENABLE (1 << 27) -#define CFG_TVALUE_MASK (3 << 24) -enum { - CFG_TVAL4 = 0 << 24, - CFG_TVAL6 = 1 << 24, - CFG_TVAL8 = 2 << 24 -}; -#define CFG_SKIP_SPARE_MASK (1 << 23) -#define CFG_SKIP_SPARE_DISABLE 0 -#define CFG_SKIP_SPARE_ENABLE (1 << 23) -#define CFG_COM_BSY_MASK (1 << 22) -#define CFG_COM_BSY_DISABLE 0 -#define CFG_COM_BSY_ENABLE (1 << 22) -#define CFG_BUS_WIDTH_MASK (1 << 21) -#define CFG_BUS_WIDTH_8BIT 0 -#define CFG_BUS_WIDTH_16BIT (1 << 21) -#define CFG_LPDDR1_MODE_MASK (1 << 20) -#define CFG_LPDDR1_MODE_DISABLE 0 -#define CFG_LPDDR1_MODE_ENABLE (1 << 20) -#define CFG_EDO_MODE_MASK (1 << 19) -#define CFG_EDO_MODE_DISABLE 0 -#define CFG_EDO_MODE_ENABLE (1 << 19) -#define CFG_PAGE_SIZE_SEL_MASK (7 << 16) -enum { - CFG_PAGE_SIZE_256 = 0 << 16, - CFG_PAGE_SIZE_512 = 1 << 16, - CFG_PAGE_SIZE_1024 = 2 << 16, - CFG_PAGE_SIZE_2048 = 3 << 16, - CFG_PAGE_SIZE_4096 = 4 << 16 -}; -#define CFG_SKIP_SPARE_SEL_MASK (3 << 14) -enum { - CFG_SKIP_SPARE_SEL_4 = 0 << 14, - CFG_SKIP_SPARE_SEL_8 = 1 << 14, - CFG_SKIP_SPARE_SEL_12 = 2 << 14, - CFG_SKIP_SPARE_SEL_16 = 3 << 14 -}; -#define CFG_TAG_BYTE_SIZE_MASK 0x1FF - -#define TIMING_0 0x14 -#define TIMING_TRP_RESP_CNT_SHIFT 28 -#define TIMING_TRP_RESP_CNT_MASK (0xf << TIMING_TRP_RESP_CNT_SHIFT) -#define TIMING_TWB_CNT_SHIFT 24 -#define TIMING_TWB_CNT_MASK (0xf << TIMING_TWB_CNT_SHIFT) -#define TIMING_TCR_TAR_TRR_CNT_SHIFT 20 -#define TIMING_TCR_TAR_TRR_CNT_MASK (0xf << TIMING_TCR_TAR_TRR_CNT_SHIFT) -#define TIMING_TWHR_CNT_SHIFT 16 -#define TIMING_TWHR_CNT_MASK (0xf << TIMING_TWHR_CNT_SHIFT) -#define TIMING_TCS_CNT_SHIFT 14 -#define TIMING_TCS_CNT_MASK (3 << TIMING_TCS_CNT_SHIFT) -#define TIMING_TWH_CNT_SHIFT 12 -#define TIMING_TWH_CNT_MASK (3 << TIMING_TWH_CNT_SHIFT) -#define TIMING_TWP_CNT_SHIFT 8 -#define TIMING_TWP_CNT_MASK (0xf << TIMING_TWP_CNT_SHIFT) -#define TIMING_TRH_CNT_SHIFT 4 -#define TIMING_TRH_CNT_MASK (3 << TIMING_TRH_CNT_SHIFT) -#define TIMING_TRP_CNT_SHIFT 0 -#define TIMING_TRP_CNT_MASK (0xf << TIMING_TRP_CNT_SHIFT) - -#define RESP_0 0x18 - -#define TIMING2_0 0x1C -#define TIMING2_TADL_CNT_SHIFT 0 -#define TIMING2_TADL_CNT_MASK (0xf << TIMING2_TADL_CNT_SHIFT) - -#define CMD_REG1_0 0x20 -#define CMD_REG2_0 0x24 -#define ADDR_REG1_0 0x28 -#define ADDR_REG2_0 0x2C - -#define DMA_MST_CTRL_0 0x30 -#define DMA_MST_CTRL_GO_MASK (1 << 31) -#define DMA_MST_CTRL_GO_DISABLE 0 -#define DMA_MST_CTRL_GO_ENABLE (1 << 31) -#define DMA_MST_CTRL_DIR_MASK (1 << 30) -#define DMA_MST_CTRL_DIR_READ 0 -#define DMA_MST_CTRL_DIR_WRITE (1 << 30) -#define DMA_MST_CTRL_PERF_EN_MASK (1 << 29) -#define DMA_MST_CTRL_PERF_EN_DISABLE 0 -#define DMA_MST_CTRL_PERF_EN_ENABLE (1 << 29) -#define DMA_MST_CTRL_REUSE_BUFFER_MASK (1 << 27) -#define DMA_MST_CTRL_REUSE_BUFFER_DISABLE 0 -#define DMA_MST_CTRL_REUSE_BUFFER_ENABLE (1 << 27) -#define DMA_MST_CTRL_BURST_SIZE_SHIFT 24 -#define DMA_MST_CTRL_BURST_SIZE_MASK (7 << DMA_MST_CTRL_BURST_SIZE_SHIFT) -enum { - DMA_MST_CTRL_BURST_1WORDS = 2 << DMA_MST_CTRL_BURST_SIZE_SHIFT, - DMA_MST_CTRL_BURST_4WORDS = 3 << DMA_MST_CTRL_BURST_SIZE_SHIFT, - DMA_MST_CTRL_BURST_8WORDS = 4 << DMA_MST_CTRL_BURST_SIZE_SHIFT, - DMA_MST_CTRL_BURST_16WORDS = 5 << DMA_MST_CTRL_BURST_SIZE_SHIFT -}; -#define DMA_MST_CTRL_IS_DMA_DONE (1 << 20) -#define DMA_MST_CTRL_EN_A_MASK (1 << 2) -#define DMA_MST_CTRL_EN_A_DISABLE 0 -#define DMA_MST_CTRL_EN_A_ENABLE (1 << 2) -#define DMA_MST_CTRL_EN_B_MASK (1 << 1) -#define DMA_MST_CTRL_EN_B_DISABLE 0 -#define DMA_MST_CTRL_EN_B_ENABLE (1 << 1) - -#define DMA_CFG_A_0 0x34 -#define DMA_CFG_B_0 0x38 -#define FIFO_CTRL_0 0x3C -#define DATA_BLOCK_PTR_0 0x40 -#define TAG_PTR_0 0x44 -#define ECC_PTR_0 0x48 - -#define DEC_STATUS_0 0x4C -#define DEC_STATUS_A_ECC_FAIL (1 << 1) -#define DEC_STATUS_B_ECC_FAIL (1 << 0) - -#define BCH_CONFIG_0 0xCC -#define BCH_CONFIG_BCH_TVALUE_SHIFT 4 -#define BCH_CONFIG_BCH_TVALUE_MASK (3 << BCH_CONFIG_BCH_TVALUE_SHIFT) -enum { - BCH_CONFIG_BCH_TVAL4 = 0 << BCH_CONFIG_BCH_TVALUE_SHIFT, - BCH_CONFIG_BCH_TVAL8 = 1 << BCH_CONFIG_BCH_TVALUE_SHIFT, - BCH_CONFIG_BCH_TVAL14 = 2 << BCH_CONFIG_BCH_TVALUE_SHIFT, - BCH_CONFIG_BCH_TVAL16 = 3 << BCH_CONFIG_BCH_TVALUE_SHIFT -}; -#define BCH_CONFIG_BCH_ECC_MASK (1 << 0) -#define BCH_CONFIG_BCH_ECC_DISABLE 0 -#define BCH_CONFIG_BCH_ECC_ENABLE (1 << 0) - -#define BCH_DEC_RESULT_0 0xD0 -#define BCH_DEC_RESULT_CORRFAIL_ERR_MASK (1 << 8) -#define BCH_DEC_RESULT_PAGE_COUNT_MASK 0xFF - -#define BCH_DEC_STATUS_BUF_0 0xD4 -#define BCH_DEC_STATUS_FAIL_SEC_FLAG_MASK 0xFF000000 -#define BCH_DEC_STATUS_CORR_SEC_FLAG_MASK 0x00FF0000 -#define BCH_DEC_STATUS_FAIL_TAG_MASK (1 << 14) -#define BCH_DEC_STATUS_CORR_TAG_MASK (1 << 13) -#define BCH_DEC_STATUS_MAX_CORR_CNT_MASK (0x1f << 8) -#define BCH_DEC_STATUS_PAGE_NUMBER_MASK 0xFF - -#define LP_OPTIONS 0 - -struct nand_ctlr { - u32 command; /* offset 00h */ - u32 status; /* offset 04h */ - u32 isr; /* offset 08h */ - u32 ier; /* offset 0Ch */ - u32 config; /* offset 10h */ - u32 timing; /* offset 14h */ - u32 resp; /* offset 18h */ - u32 timing2; /* offset 1Ch */ - u32 cmd_reg1; /* offset 20h */ - u32 cmd_reg2; /* offset 24h */ - u32 addr_reg1; /* offset 28h */ - u32 addr_reg2; /* offset 2Ch */ - u32 dma_mst_ctrl; /* offset 30h */ - u32 dma_cfg_a; /* offset 34h */ - u32 dma_cfg_b; /* offset 38h */ - u32 fifo_ctrl; /* offset 3Ch */ - u32 data_block_ptr; /* offset 40h */ - u32 tag_ptr; /* offset 44h */ - u32 resv1; /* offset 48h */ - u32 dec_status; /* offset 4Ch */ - u32 hwstatus_cmd; /* offset 50h */ - u32 hwstatus_mask; /* offset 54h */ - u32 resv2[29]; - u32 bch_config; /* offset CCh */ - u32 bch_dec_result; /* offset D0h */ - u32 bch_dec_status_buf; - /* offset D4h */ -}; diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c deleted file mode 100644 index 619d0403e9..0000000000 --- a/drivers/mtd/nand/vf610_nfc.c +++ /dev/null @@ -1,768 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2009-2015 Freescale Semiconductor, Inc. and others - * - * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. - * Ported to U-Boot by Stefan Agner - * Based on RFC driver posted on Kernel Mailing list by Bill Pringlemeir - * Jason ported to M54418TWR and MVFA5. - * Authors: Stefan Agner - * Bill Pringlemeir - * Shaohui Xie - * Jason Jin - * - * Based on original driver mpc5121_nfc.c. - * - * Limitations: - * - Untested on MPC5125 and M54418. - * - DMA and pipelining not used. - * - 2K pages or less. - * - HW ECC: Only 2K page with 64+ OOB. - * - HW ECC: Only 24 and 32-bit error correction implemented. - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include - -/* Register Offsets */ -#define NFC_FLASH_CMD1 0x3F00 -#define NFC_FLASH_CMD2 0x3F04 -#define NFC_COL_ADDR 0x3F08 -#define NFC_ROW_ADDR 0x3F0c -#define NFC_ROW_ADDR_INC 0x3F14 -#define NFC_FLASH_STATUS1 0x3F18 -#define NFC_FLASH_STATUS2 0x3F1c -#define NFC_CACHE_SWAP 0x3F28 -#define NFC_SECTOR_SIZE 0x3F2c -#define NFC_FLASH_CONFIG 0x3F30 -#define NFC_IRQ_STATUS 0x3F38 - -/* Addresses for NFC MAIN RAM BUFFER areas */ -#define NFC_MAIN_AREA(n) ((n) * 0x1000) - -#define PAGE_2K 0x0800 -#define OOB_64 0x0040 -#define OOB_MAX 0x0100 - -/* - * NFC_CMD2[CODE] values. See section: - * - 31.4.7 Flash Command Code Description, Vybrid manual - * - 23.8.6 Flash Command Sequencer, MPC5125 manual - * - * Briefly these are bitmasks of controller cycles. - */ -#define READ_PAGE_CMD_CODE 0x7EE0 -#define READ_ONFI_PARAM_CMD_CODE 0x4860 -#define PROGRAM_PAGE_CMD_CODE 0x7FC0 -#define ERASE_CMD_CODE 0x4EC0 -#define READ_ID_CMD_CODE 0x4804 -#define RESET_CMD_CODE 0x4040 -#define STATUS_READ_CMD_CODE 0x4068 - -/* NFC ECC mode define */ -#define ECC_BYPASS 0 -#define ECC_45_BYTE 6 -#define ECC_60_BYTE 7 - -/*** Register Mask and bit definitions */ - -/* NFC_FLASH_CMD1 Field */ -#define CMD_BYTE2_MASK 0xFF000000 -#define CMD_BYTE2_SHIFT 24 - -/* NFC_FLASH_CM2 Field */ -#define CMD_BYTE1_MASK 0xFF000000 -#define CMD_BYTE1_SHIFT 24 -#define CMD_CODE_MASK 0x00FFFF00 -#define CMD_CODE_SHIFT 8 -#define BUFNO_MASK 0x00000006 -#define BUFNO_SHIFT 1 -#define START_BIT (1<<0) - -/* NFC_COL_ADDR Field */ -#define COL_ADDR_MASK 0x0000FFFF -#define COL_ADDR_SHIFT 0 - -/* NFC_ROW_ADDR Field */ -#define ROW_ADDR_MASK 0x00FFFFFF -#define ROW_ADDR_SHIFT 0 -#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 -#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 -#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 -#define ROW_ADDR_CHIP_SEL_SHIFT 24 - -/* NFC_FLASH_STATUS2 Field */ -#define STATUS_BYTE1_MASK 0x000000FF - -/* NFC_FLASH_CONFIG Field */ -#define CONFIG_ECC_SRAM_ADDR_MASK 0x7FC00000 -#define CONFIG_ECC_SRAM_ADDR_SHIFT 22 -#define CONFIG_ECC_SRAM_REQ_BIT (1<<21) -#define CONFIG_DMA_REQ_BIT (1<<20) -#define CONFIG_ECC_MODE_MASK 0x000E0000 -#define CONFIG_ECC_MODE_SHIFT 17 -#define CONFIG_FAST_FLASH_BIT (1<<16) -#define CONFIG_16BIT (1<<7) -#define CONFIG_BOOT_MODE_BIT (1<<6) -#define CONFIG_ADDR_AUTO_INCR_BIT (1<<5) -#define CONFIG_BUFNO_AUTO_INCR_BIT (1<<4) -#define CONFIG_PAGE_CNT_MASK 0xF -#define CONFIG_PAGE_CNT_SHIFT 0 - -/* NFC_IRQ_STATUS Field */ -#define IDLE_IRQ_BIT (1<<29) -#define IDLE_EN_BIT (1<<20) -#define CMD_DONE_CLEAR_BIT (1<<18) -#define IDLE_CLEAR_BIT (1<<17) - -#define NFC_TIMEOUT (1000) - -/* - * ECC status - seems to consume 8 bytes (double word). The documented - * status byte is located in the lowest byte of the second word (which is - * the 4th or 7th byte depending on endianness). - * Calculate an offset to store the ECC status at the end of the buffer. - */ -#define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8) - -#define ECC_STATUS 0x4 -#define ECC_STATUS_MASK 0x80 -#define ECC_STATUS_ERR_COUNT 0x3F - -enum vf610_nfc_alt_buf { - ALT_BUF_DATA = 0, - ALT_BUF_ID = 1, - ALT_BUF_STAT = 2, - ALT_BUF_ONFI = 3, -}; - -struct vf610_nfc { - struct nand_chip chip; - void __iomem *regs; - uint buf_offset; - int write_sz; - /* Status and ID are in alternate locations. */ - enum vf610_nfc_alt_buf alt_buf; -}; - -#define mtd_to_nfc(_mtd) nand_get_controller_data(mtd_to_nand(_mtd)) - -#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) -#define ECC_HW_MODE ECC_45_BYTE - -static struct nand_ecclayout vf610_nfc_ecc = { - .eccbytes = 45, - .eccpos = {19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 17} } -}; -#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) -#define ECC_HW_MODE ECC_60_BYTE - -static struct nand_ecclayout vf610_nfc_ecc = { - .eccbytes = 60, - .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63 }, - .oobfree = { - {.offset = 2, - .length = 2} } -}; -#endif - -static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - return readl(nfc->regs + reg); -} - -static inline void vf610_nfc_write(struct mtd_info *mtd, uint reg, u32 val) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - writel(val, nfc->regs + reg); -} - -static inline void vf610_nfc_set(struct mtd_info *mtd, uint reg, u32 bits) -{ - vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) | bits); -} - -static inline void vf610_nfc_clear(struct mtd_info *mtd, uint reg, u32 bits) -{ - vf610_nfc_write(mtd, reg, vf610_nfc_read(mtd, reg) & ~bits); -} - -static inline void vf610_nfc_set_field(struct mtd_info *mtd, u32 reg, - u32 mask, u32 shift, u32 val) -{ - vf610_nfc_write(mtd, reg, - (vf610_nfc_read(mtd, reg) & (~mask)) | val << shift); -} - -static inline void vf610_nfc_memcpy(void *dst, const void *src, size_t n) -{ - /* - * Use this accessor for the internal SRAM buffers. On the ARM - * Freescale Vybrid SoC it's known that the driver can treat - * the SRAM buffer as if it's memory. Other platform might need - * to treat the buffers differently. - * - * For the time being, use memcpy - */ - memcpy(dst, src, n); -} - -/* Clear flags for upcoming command */ -static inline void vf610_nfc_clear_status(void __iomem *regbase) -{ - void __iomem *reg = regbase + NFC_IRQ_STATUS; - u32 tmp = __raw_readl(reg); - tmp |= CMD_DONE_CLEAR_BIT | IDLE_CLEAR_BIT; - __raw_writel(tmp, reg); -} - -/* Wait for complete operation */ -static void vf610_nfc_done(struct mtd_info *mtd) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint start; - - /* - * Barrier is needed after this write. This write need - * to be done before reading the next register the first - * time. - * vf610_nfc_set implicates such a barrier by using writel - * to write to the register. - */ - vf610_nfc_set(mtd, NFC_FLASH_CMD2, START_BIT); - - start = get_timer(0); - - while (!(vf610_nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) { - if (get_timer(start) > NFC_TIMEOUT) { - printf("Timeout while waiting for IDLE.\n"); - return; - } - } - vf610_nfc_clear_status(nfc->regs); -} - -static u8 vf610_nfc_get_id(struct mtd_info *mtd, int col) -{ - u32 flash_id; - - if (col < 4) { - flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS1); - flash_id >>= (3 - col) * 8; - } else { - flash_id = vf610_nfc_read(mtd, NFC_FLASH_STATUS2); - flash_id >>= 24; - } - - return flash_id & 0xff; -} - -static u8 vf610_nfc_get_status(struct mtd_info *mtd) -{ - return vf610_nfc_read(mtd, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; -} - -/* Single command */ -static void vf610_nfc_send_command(void __iomem *regbase, u32 cmd_byte1, - u32 cmd_code) -{ - void __iomem *reg = regbase + NFC_FLASH_CMD2; - u32 tmp; - vf610_nfc_clear_status(regbase); - - tmp = __raw_readl(reg); - tmp &= ~(CMD_BYTE1_MASK | CMD_CODE_MASK | BUFNO_MASK); - tmp |= cmd_byte1 << CMD_BYTE1_SHIFT; - tmp |= cmd_code << CMD_CODE_SHIFT; - __raw_writel(tmp, reg); -} - -/* Two commands */ -static void vf610_nfc_send_commands(void __iomem *regbase, u32 cmd_byte1, - u32 cmd_byte2, u32 cmd_code) -{ - void __iomem *reg = regbase + NFC_FLASH_CMD1; - u32 tmp; - vf610_nfc_send_command(regbase, cmd_byte1, cmd_code); - - tmp = __raw_readl(reg); - tmp &= ~CMD_BYTE2_MASK; - tmp |= cmd_byte2 << CMD_BYTE2_SHIFT; - __raw_writel(tmp, reg); -} - -static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) -{ - if (column != -1) { - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - if (nfc->chip.options & NAND_BUSWIDTH_16) - column = column / 2; - vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, - COL_ADDR_SHIFT, column); - } - if (page != -1) - vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, - ROW_ADDR_SHIFT, page); -} - -static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode) -{ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ecc_mode); -} - -static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size) -{ - __raw_writel(size, regbase + NFC_SECTOR_SIZE); -} - -/* Send command to NAND chip */ -static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, - int column, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - int trfr_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; - - nfc->buf_offset = max(column, 0); - nfc->alt_buf = ALT_BUF_DATA; - - switch (command) { - case NAND_CMD_SEQIN: - /* Use valid column/page from preread... */ - vf610_nfc_addr_cycle(mtd, column, page); - nfc->buf_offset = 0; - - /* - * SEQIN => data => PAGEPROG sequence is done by the controller - * hence we do not need to issue the command here... - */ - return; - case NAND_CMD_PAGEPROG: - trfr_sz += nfc->write_sz; - vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); - vf610_nfc_transfer_size(nfc->regs, trfr_sz); - vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN, - command, PROGRAM_PAGE_CMD_CODE); - break; - - case NAND_CMD_RESET: - vf610_nfc_transfer_size(nfc->regs, 0); - vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE); - break; - - case NAND_CMD_READOOB: - trfr_sz += mtd->oobsize; - column = mtd->writesize; - vf610_nfc_transfer_size(nfc->regs, trfr_sz); - vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(mtd, column, page); - vf610_nfc_ecc_mode(mtd, ECC_BYPASS); - break; - - case NAND_CMD_READ0: - trfr_sz += mtd->writesize + mtd->oobsize; - vf610_nfc_transfer_size(nfc->regs, trfr_sz); - vf610_nfc_ecc_mode(mtd, ECC_HW_MODE); - vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, - NAND_CMD_READSTART, READ_PAGE_CMD_CODE); - vf610_nfc_addr_cycle(mtd, column, page); - break; - - case NAND_CMD_PARAM: - nfc->alt_buf = ALT_BUF_ONFI; - trfr_sz = 3 * sizeof(struct nand_onfi_params); - vf610_nfc_transfer_size(nfc->regs, trfr_sz); - vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM, - READ_ONFI_PARAM_CMD_CODE); - vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, - ROW_ADDR_SHIFT, column); - vf610_nfc_ecc_mode(mtd, ECC_BYPASS); - break; - - case NAND_CMD_ERASE1: - vf610_nfc_transfer_size(nfc->regs, 0); - vf610_nfc_send_commands(nfc->regs, command, - NAND_CMD_ERASE2, ERASE_CMD_CODE); - vf610_nfc_addr_cycle(mtd, column, page); - break; - - case NAND_CMD_READID: - nfc->alt_buf = ALT_BUF_ID; - nfc->buf_offset = 0; - vf610_nfc_transfer_size(nfc->regs, 0); - vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE); - vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, - ROW_ADDR_SHIFT, column); - break; - - case NAND_CMD_STATUS: - nfc->alt_buf = ALT_BUF_STAT; - vf610_nfc_transfer_size(nfc->regs, 0); - vf610_nfc_send_command(nfc->regs, command, STATUS_READ_CMD_CODE); - break; - default: - return; - } - - vf610_nfc_done(mtd); - - nfc->write_sz = 0; -} - -/* Read data from NFC buffers */ -static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; - - /* Alternate buffers are only supported through read_byte */ - if (nfc->alt_buf) - return; - - vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); - - nfc->buf_offset += len; -} - -/* Write data to NFC buffers */ -static void vf610_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, - int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - uint c = nfc->buf_offset; - uint l; - - l = min_t(uint, len, mtd->writesize + mtd->oobsize - c); - vf610_nfc_memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); - - nfc->write_sz += l; - nfc->buf_offset += l; -} - -/* Read byte from NFC buffers */ -static uint8_t vf610_nfc_read_byte(struct mtd_info *mtd) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u8 tmp; - uint c = nfc->buf_offset; - - switch (nfc->alt_buf) { - case ALT_BUF_ID: - tmp = vf610_nfc_get_id(mtd, c); - break; - case ALT_BUF_STAT: - tmp = vf610_nfc_get_status(mtd); - break; -#ifdef __LITTLE_ENDIAN - case ALT_BUF_ONFI: - /* Reverse byte since the controller uses big endianness */ - c = nfc->buf_offset ^ 0x3; - /* fall-through */ -#endif - default: - tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c)); - break; - } - nfc->buf_offset++; - return tmp; -} - -/* Read word from NFC buffers */ -static u16 vf610_nfc_read_word(struct mtd_info *mtd) -{ - u16 tmp; - - vf610_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); - return tmp; -} - -/* If not provided, upper layers apply a fixed delay. */ -static int vf610_nfc_dev_ready(struct mtd_info *mtd) -{ - /* NFC handles R/B internally; always ready. */ - return 1; -} - -/* - * This function supports Vybrid only (MPC5125 would have full RB and four CS) - */ -static void vf610_nfc_select_chip(struct mtd_info *mtd, int chip) -{ -#ifdef CONFIG_VF610 - u32 tmp = vf610_nfc_read(mtd, NFC_ROW_ADDR); - tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK); - - if (chip >= 0) { - tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT; - tmp |= (1 << chip) << ROW_ADDR_CHIP_SEL_SHIFT; - } - - vf610_nfc_write(mtd, NFC_ROW_ADDR, tmp); -#endif -} - -/* Count the number of 0's in buff upto max_bits */ -static inline int count_written_bits(uint8_t *buff, int size, int max_bits) -{ - uint32_t *buff32 = (uint32_t *)buff; - int k, written_bits = 0; - - for (k = 0; k < (size / 4); k++) { - written_bits += hweight32(~buff32[k]); - if (written_bits > max_bits) - break; - } - - return written_bits; -} - -static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat, - uint8_t *oob, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS; - u8 ecc_status; - u8 ecc_count; - int flips; - int flips_threshold = nfc->chip.ecc.strength / 2; - - ecc_status = vf610_nfc_read(mtd, ecc_status_off) & 0xff; - ecc_count = ecc_status & ECC_STATUS_ERR_COUNT; - - if (!(ecc_status & ECC_STATUS_MASK)) - return ecc_count; - - /* Read OOB without ECC unit enabled */ - vf610_nfc_command(mtd, NAND_CMD_READOOB, 0, page); - vf610_nfc_read_buf(mtd, oob, mtd->oobsize); - - /* - * On an erased page, bit count (including OOB) should be zero or - * at least less then half of the ECC strength. - */ - flips = count_written_bits(dat, nfc->chip.ecc.size, flips_threshold); - flips += count_written_bits(oob, mtd->oobsize, flips_threshold); - - if (unlikely(flips > flips_threshold)) - return -EINVAL; - - /* Erased page. */ - memset(dat, 0xff, nfc->chip.ecc.size); - memset(oob, 0xff, mtd->oobsize); - return flips; -} - -static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int oob_required, int page) -{ - int eccsize = chip->ecc.size; - int stat; - - vf610_nfc_read_buf(mtd, buf, eccsize); - if (oob_required) - vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); - - stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page); - - if (stat < 0) { - mtd->ecc_stats.failed++; - return 0; - } else { - mtd->ecc_stats.corrected += stat; - return stat; - } -} - -/* - * ECC will be calculated automatically - */ -static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int oob_required, int page) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - vf610_nfc_write_buf(mtd, buf, mtd->writesize); - if (oob_required) - vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Always write whole page including OOB due to HW ECC */ - nfc->write_sz = mtd->writesize + mtd->oobsize; - - return 0; -} - -struct vf610_nfc_config { - int hardware_ecc; - int width; - int flash_bbt; -}; - -static int vf610_nfc_nand_init(int devnum, void __iomem *addr) -{ - struct mtd_info *mtd; - struct nand_chip *chip; - struct vf610_nfc *nfc; - int err = 0; - struct vf610_nfc_config cfg = { - .hardware_ecc = 1, -#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT - .width = 16, -#else - .width = 8, -#endif - .flash_bbt = 1, - }; - - nfc = malloc(sizeof(*nfc)); - if (!nfc) { - printf(KERN_ERR "%s: Memory exhausted!\n", __func__); - return -ENOMEM; - } - - chip = &nfc->chip; - nfc->regs = addr; - - mtd = nand_to_mtd(chip); - nand_set_controller_data(chip, nfc); - - if (cfg.width == 16) - chip->options |= NAND_BUSWIDTH_16; - - chip->dev_ready = vf610_nfc_dev_ready; - chip->cmdfunc = vf610_nfc_command; - chip->read_byte = vf610_nfc_read_byte; - chip->read_word = vf610_nfc_read_word; - chip->read_buf = vf610_nfc_read_buf; - chip->write_buf = vf610_nfc_write_buf; - chip->select_chip = vf610_nfc_select_chip; - - chip->options |= NAND_NO_SUBPAGE_WRITE; - - chip->ecc.size = PAGE_2K; - - /* Set configuration register. */ - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); - vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); - vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); - - /* Disable virtual pages, only one elementary transfer unit */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, - CONFIG_PAGE_CNT_SHIFT, 1); - - /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) { - err = -ENXIO; - goto error; - } - - if (cfg.width == 16) - vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); - - /* Bad block options. */ - if (cfg.flash_bbt) - chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB | - NAND_BBT_CREATE; - - /* Single buffer only, max 256 OOB minus ECC status */ - if (mtd->writesize + mtd->oobsize > PAGE_2K + OOB_MAX - 8) { - dev_err(nfc->dev, "Unsupported flash page size\n"); - err = -ENXIO; - goto error; - } - - if (cfg.hardware_ecc) { - if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { - dev_err(nfc->dev, "Unsupported flash with hwecc\n"); - err = -ENXIO; - goto error; - } - - if (chip->ecc.size != mtd->writesize) { - dev_err(nfc->dev, "ecc size: %d\n", chip->ecc.size); - dev_err(nfc->dev, "Step size needs to be page size\n"); - err = -ENXIO; - goto error; - } - - /* Current HW ECC layouts only use 64 bytes of OOB */ - if (mtd->oobsize > 64) - mtd->oobsize = 64; - - /* propagate ecc.layout to mtd_info */ - mtd->ecclayout = chip->ecc.layout; - chip->ecc.read_page = vf610_nfc_read_page; - chip->ecc.write_page = vf610_nfc_write_page; - chip->ecc.mode = NAND_ECC_HW; - - chip->ecc.size = PAGE_2K; - chip->ecc.layout = &vf610_nfc_ecc; -#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES) - chip->ecc.strength = 24; - chip->ecc.bytes = 45; -#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES) - chip->ecc.strength = 32; - chip->ecc.bytes = 60; -#endif - - /* Set ECC_STATUS offset */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_SRAM_ADDR_MASK, - CONFIG_ECC_SRAM_ADDR_SHIFT, - ECC_SRAM_ADDR >> 3); - - /* Enable ECC status in SRAM */ - vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); - } - - /* second phase scan */ - err = nand_scan_tail(mtd); - if (err) - return err; - - err = nand_register(devnum, mtd); - if (err) - return err; - - return 0; - -error: - return err; -} - -void board_nand_init(void) -{ - int err = vf610_nfc_nand_init(0, (void __iomem *)CONFIG_SYS_NAND_BASE); - if (err) - printf("VF610 NAND init failed (err %d)\n", err); -} diff --git a/drivers/mtd/nand/zynq_nand.c b/drivers/mtd/nand/zynq_nand.c deleted file mode 100644 index e932a58bf6..0000000000 --- a/drivers/mtd/nand/zynq_nand.c +++ /dev/null @@ -1,1254 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2016 Xilinx, Inc. - * - * Xilinx Zynq NAND Flash Controller Driver - * This driver is based on plat_nand.c and mxc_nand.c drivers - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* The NAND flash driver defines */ -#define ZYNQ_NAND_CMD_PHASE 1 -#define ZYNQ_NAND_DATA_PHASE 2 -#define ZYNQ_NAND_ECC_SIZE 512 -#define ZYNQ_NAND_SET_OPMODE_8BIT (0 << 0) -#define ZYNQ_NAND_SET_OPMODE_16BIT (1 << 0) -#define ZYNQ_NAND_ECC_STATUS (1 << 6) -#define ZYNQ_MEMC_CLRCR_INT_CLR1 (1 << 4) -#define ZYNQ_MEMC_SR_RAW_INT_ST1 (1 << 6) -#define ZYNQ_MEMC_SR_INT_ST1 (1 << 4) -#define ZYNQ_MEMC_NAND_ECC_MODE_MASK 0xC - -/* Flash memory controller operating parameters */ -#define ZYNQ_NAND_CLR_CONFIG ((0x1 << 1) | /* Disable interrupt */ \ - (0x1 << 4) | /* Clear interrupt */ \ - (0x1 << 6)) /* Disable ECC interrupt */ - -#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS - -/* Assuming 50MHz clock (20ns cycle time) and 3V operation */ -#define ZYNQ_NAND_SET_CYCLES ((0x2 << 20) | /* t_rr from nand_cycles */ \ - (0x2 << 17) | /* t_ar from nand_cycles */ \ - (0x1 << 14) | /* t_clr from nand_cycles */ \ - (0x3 << 11) | /* t_wp from nand_cycles */ \ - (0x2 << 8) | /* t_rea from nand_cycles */ \ - (0x5 << 4) | /* t_wc from nand_cycles */ \ - (0x5 << 0)) /* t_rc from nand_cycles */ -#endif - - -#define ZYNQ_NAND_DIRECT_CMD ((0x4 << 23) | /* Chip 0 from interface 1 */ \ - (0x2 << 21)) /* UpdateRegs operation */ - -#define ZYNQ_NAND_ECC_CONFIG ((0x1 << 2) | /* ECC available on APB */ \ - (0x1 << 4) | /* ECC read at end of page */ \ - (0x0 << 5)) /* No Jumping */ - -#define ZYNQ_NAND_ECC_CMD1 ((0x80) | /* Write command */ \ - (0x00 << 8) | /* Read command */ \ - (0x30 << 16) | /* Read End command */ \ - (0x1 << 24)) /* Read End command calid */ - -#define ZYNQ_NAND_ECC_CMD2 ((0x85) | /* Write col change cmd */ \ - (0x05 << 8) | /* Read col change cmd */ \ - (0xE0 << 16) | /* Read col change end cmd */ \ - (0x1 << 24)) /* Read col change - end cmd valid */ -/* AXI Address definitions */ -#define START_CMD_SHIFT 3 -#define END_CMD_SHIFT 11 -#define END_CMD_VALID_SHIFT 20 -#define ADDR_CYCLES_SHIFT 21 -#define CLEAR_CS_SHIFT 21 -#define ECC_LAST_SHIFT 10 -#define COMMAND_PHASE (0 << 19) -#define DATA_PHASE (1 << 19) -#define ONDIE_ECC_FEATURE_ADDR 0x90 -#define ONDIE_ECC_FEATURE_ENABLE 0x08 - -#define ZYNQ_NAND_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */ -#define ZYNQ_NAND_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */ - -/* ECC block registers bit position and bit mask */ -#define ZYNQ_NAND_ECC_BUSY (1 << 6) /* ECC block is busy */ -#define ZYNQ_NAND_ECC_MASK 0x00FFFFFF /* ECC value mask */ - -#define ZYNQ_NAND_ROW_ADDR_CYCL_MASK 0x0F -#define ZYNQ_NAND_COL_ADDR_CYCL_MASK 0xF0 - -#define ZYNQ_NAND_MIO_NUM_NAND_8BIT 13 -#define ZYNQ_NAND_MIO_NUM_NAND_16BIT 8 - -enum zynq_nand_bus_width { - NAND_BW_UNKNOWN = -1, - NAND_BW_8BIT, - NAND_BW_16BIT, -}; - -#ifndef NAND_CMD_LOCK_TIGHT -#define NAND_CMD_LOCK_TIGHT 0x2c -#endif - -#ifndef NAND_CMD_LOCK_STATUS -#define NAND_CMD_LOCK_STATUS 0x7a -#endif - -/* SMC register set */ -struct zynq_nand_smc_regs { - u32 csr; /* 0x00 */ - u32 reserved0[2]; - u32 cfr; /* 0x0C */ - u32 dcr; /* 0x10 */ - u32 scr; /* 0x14 */ - u32 sor; /* 0x18 */ - u32 reserved1[249]; - u32 esr; /* 0x400 */ - u32 emcr; /* 0x404 */ - u32 emcmd1r; /* 0x408 */ - u32 emcmd2r; /* 0x40C */ - u32 reserved2[2]; - u32 eval0r; /* 0x418 */ -}; -#define zynq_nand_smc_base ((struct zynq_nand_smc_regs __iomem *)\ - ZYNQ_SMC_BASEADDR) - -/* - * struct zynq_nand_info - Defines the NAND flash driver instance - * @parts: Pointer to the mtd_partition structure - * @nand_base: Virtual address of the NAND flash device - * @end_cmd_pending: End command is pending - * @end_cmd: End command - */ -struct zynq_nand_info { - void __iomem *nand_base; - u8 end_cmd_pending; - u8 end_cmd; -}; - -/* - * struct zynq_nand_command_format - Defines NAND flash command format - * @start_cmd: First cycle command (Start command) - * @end_cmd: Second cycle command (Last command) - * @addr_cycles: Number of address cycles required to send the address - * @end_cmd_valid: The second cycle command is valid for cmd or data phase - */ -struct zynq_nand_command_format { - u8 start_cmd; - u8 end_cmd; - u8 addr_cycles; - u8 end_cmd_valid; -}; - -/* The NAND flash operations command format */ -static const struct zynq_nand_command_format zynq_nand_commands[] = { - {NAND_CMD_READ0, NAND_CMD_READSTART, 5, ZYNQ_NAND_CMD_PHASE}, - {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, ZYNQ_NAND_CMD_PHASE}, - {NAND_CMD_READID, NAND_CMD_NONE, 1, 0}, - {NAND_CMD_STATUS, NAND_CMD_NONE, 0, 0}, - {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, ZYNQ_NAND_DATA_PHASE}, - {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, 0}, - {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, ZYNQ_NAND_CMD_PHASE}, - {NAND_CMD_RESET, NAND_CMD_NONE, 0, 0}, - {NAND_CMD_PARAM, NAND_CMD_NONE, 1, 0}, - {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, 0}, - {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, 0}, - {NAND_CMD_LOCK, NAND_CMD_NONE, 0, 0}, - {NAND_CMD_LOCK_TIGHT, NAND_CMD_NONE, 0, 0}, - {NAND_CMD_UNLOCK1, NAND_CMD_NONE, 3, 0}, - {NAND_CMD_UNLOCK2, NAND_CMD_NONE, 3, 0}, - {NAND_CMD_LOCK_STATUS, NAND_CMD_NONE, 3, 0}, - {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0}, - /* Add all the flash commands supported by the flash device */ -}; - -/* Define default oob placement schemes for large and small page devices */ -static struct nand_ecclayout nand_oob_16 = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - { .offset = 8, .length = 8 } - } -}; - -static struct nand_ecclayout nand_oob_64 = { - .eccbytes = 12, - .eccpos = { - 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63}, - .oobfree = { - { .offset = 2, .length = 50 } - } -}; - -static struct nand_ecclayout ondie_nand_oob_64 = { - .eccbytes = 32, - - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 24, 25, 26, 27, 28, 29, 30, 31, - 40, 41, 42, 43, 44, 45, 46, 47, - 56, 57, 58, 59, 60, 61, 62, 63 - }, - - .oobfree = { - { .offset = 4, .length = 4 }, - { .offset = 20, .length = 4 }, - { .offset = 36, .length = 4 }, - { .offset = 52, .length = 4 } - } -}; - -/* bbt decriptors for chips with on-die ECC and - chips with 64-byte OOB */ -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 4, - .len = 4, - .veroffs = 20, - .maxblocks = 4, - .pattern = bbt_pattern -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | - NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 4, - .len = 4, - .veroffs = 20, - .maxblocks = 4, - .pattern = mirror_pattern -}; - -/* - * zynq_nand_waitfor_ecc_completion - Wait for ECC completion - * - * returns: status for command completion, -1 for Timeout - */ -static int zynq_nand_waitfor_ecc_completion(void) -{ - unsigned long timeout; - u32 status; - - /* Wait max 10us */ - timeout = 10; - status = readl(&zynq_nand_smc_base->esr); - while (status & ZYNQ_NAND_ECC_BUSY) { - status = readl(&zynq_nand_smc_base->esr); - if (timeout == 0) - return -1; - timeout--; - udelay(1); - } - - return status; -} - -/* - * zynq_nand_init_nand_flash - Initialize NAND controller - * @option: Device property flags - * - * This function initializes the NAND flash interface on the NAND controller. - * - * returns: 0 on success or error value on failure - */ -static int zynq_nand_init_nand_flash(int option) -{ - u32 status; - - /* disable interrupts */ - writel(ZYNQ_NAND_CLR_CONFIG, &zynq_nand_smc_base->cfr); -#ifndef CONFIG_NAND_ZYNQ_USE_BOOTLOADER1_TIMINGS - /* Initialize the NAND interface by setting cycles and operation mode */ - writel(ZYNQ_NAND_SET_CYCLES, &zynq_nand_smc_base->scr); -#endif - if (option & NAND_BUSWIDTH_16) - writel(ZYNQ_NAND_SET_OPMODE_16BIT, &zynq_nand_smc_base->sor); - else - writel(ZYNQ_NAND_SET_OPMODE_8BIT, &zynq_nand_smc_base->sor); - - writel(ZYNQ_NAND_DIRECT_CMD, &zynq_nand_smc_base->dcr); - - /* Wait till the ECC operation is complete */ - status = zynq_nand_waitfor_ecc_completion(); - if (status < 0) { - printf("%s: Timeout\n", __func__); - return status; - } - - /* Set the command1 and command2 register */ - writel(ZYNQ_NAND_ECC_CMD1, &zynq_nand_smc_base->emcmd1r); - writel(ZYNQ_NAND_ECC_CMD2, &zynq_nand_smc_base->emcmd2r); - - return 0; -} - -/* - * zynq_nand_calculate_hwecc - Calculate Hardware ECC - * @mtd: Pointer to the mtd_info structure - * @data: Pointer to the page data - * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored - * - * This function retrieves the Hardware ECC data from the controller and returns - * ECC data back to the MTD subsystem. - * - * returns: 0 on success or error value on failure - */ -static int zynq_nand_calculate_hwecc(struct mtd_info *mtd, const u8 *data, - u8 *ecc_code) -{ - u32 ecc_value = 0; - u8 ecc_reg, ecc_byte; - u32 ecc_status; - - /* Wait till the ECC operation is complete */ - ecc_status = zynq_nand_waitfor_ecc_completion(); - if (ecc_status < 0) { - printf("%s: Timeout\n", __func__); - return ecc_status; - } - - for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) { - /* Read ECC value for each block */ - ecc_value = readl(&zynq_nand_smc_base->eval0r + ecc_reg); - - /* Get the ecc status from ecc read value */ - ecc_status = (ecc_value >> 24) & 0xFF; - - /* ECC value valid */ - if (ecc_status & ZYNQ_NAND_ECC_STATUS) { - for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) { - /* Copy ECC bytes to MTD buffer */ - *ecc_code = ecc_value & 0xFF; - ecc_value = ecc_value >> 8; - ecc_code++; - } - } else { - debug("%s: ecc status failed\n", __func__); - } - } - - return 0; -} - -/* - * onehot - onehot function - * @value: value to check for onehot - * - * This function checks whether a value is onehot or not. - * onehot is if and only if one bit is set. - * - * FIXME: Try to move this in common.h - */ -static bool onehot(unsigned short value) -{ - bool onehot; - - onehot = value && !(value & (value - 1)); - return onehot; -} - -/* - * zynq_nand_correct_data - ECC correction function - * @mtd: Pointer to the mtd_info structure - * @buf: Pointer to the page data - * @read_ecc: Pointer to the ECC value read from spare data area - * @calc_ecc: Pointer to the calculated ECC value - * - * This function corrects the ECC single bit errors & detects 2-bit errors. - * - * returns: 0 if no ECC errors found - * 1 if single bit error found and corrected. - * -1 if multiple ECC errors found. - */ -static int zynq_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - unsigned char bit_addr; - unsigned int byte_addr; - unsigned short ecc_odd, ecc_even; - unsigned short read_ecc_lower, read_ecc_upper; - unsigned short calc_ecc_lower, calc_ecc_upper; - - read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff; - read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff; - - calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff; - calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff; - - ecc_odd = read_ecc_lower ^ calc_ecc_lower; - ecc_even = read_ecc_upper ^ calc_ecc_upper; - - if ((ecc_odd == 0) && (ecc_even == 0)) - return 0; /* no error */ - - if (ecc_odd == (~ecc_even & 0xfff)) { - /* bits [11:3] of error code is byte offset */ - byte_addr = (ecc_odd >> 3) & 0x1ff; - /* bits [2:0] of error code is bit offset */ - bit_addr = ecc_odd & 0x7; - /* Toggling error bit */ - buf[byte_addr] ^= (1 << bit_addr); - return 1; - } - - if (onehot(ecc_odd | ecc_even)) - return 1; /* one error in parity */ - - return -1; /* Uncorrectable error */ -} - -/* - * zynq_nand_read_oob - [REPLACABLE] the most common OOB data read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * @sndcmd: flag whether to issue read command or not - */ -static int zynq_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - unsigned long data_phase_addr = 0; - int data_width = 4; - u8 *p; - - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - - p = chip->oob_poi; - chip->read_buf(mtd, p, (mtd->oobsize - data_width)); - p += mtd->oobsize - data_width; - - data_phase_addr = (unsigned long)chip->IO_ADDR_R; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - chip->read_buf(mtd, p, data_width); - - return 0; -} - -/* - * zynq_nand_write_oob - [REPLACABLE] the most common OOB data write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -static int zynq_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int status = 0, data_width = 4; - const u8 *buf = chip->oob_poi; - unsigned long data_phase_addr = 0; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - - chip->write_buf(mtd, buf, (mtd->oobsize - data_width)); - buf += mtd->oobsize - data_width; - - data_phase_addr = (unsigned long)chip->IO_ADDR_W; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - data_phase_addr |= (1 << END_CMD_VALID_SHIFT); - chip->IO_ADDR_W = (void __iomem *)data_phase_addr; - chip->write_buf(mtd, buf, data_width); - - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/* - * zynq_nand_read_page_raw - [Intern] read raw page data without ecc - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to read - */ -static int zynq_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - u8 *buf, int oob_required, int page) -{ - unsigned long data_width = 4; - unsigned long data_phase_addr = 0; - u8 *p; - - chip->read_buf(mtd, buf, mtd->writesize); - - p = chip->oob_poi; - chip->read_buf(mtd, p, (mtd->oobsize - data_width)); - p += (mtd->oobsize - data_width); - - data_phase_addr = (unsigned long)chip->IO_ADDR_R; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - - chip->read_buf(mtd, p, data_width); - return 0; -} - -static int zynq_nand_read_page_raw_nooob(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, int oob_required, int page) -{ - chip->read_buf(mtd, buf, mtd->writesize); - return 0; -} - -static int zynq_nand_read_subpage_raw(struct mtd_info *mtd, - struct nand_chip *chip, u32 data_offs, - u32 readlen, u8 *buf, int page) -{ - if (data_offs != 0) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_offs, -1); - buf += data_offs; - } - chip->read_buf(mtd, buf, readlen); - - return 0; -} - -/* - * zynq_nand_write_page_raw - [Intern] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - */ -static int zynq_nand_write_page_raw(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, int oob_required, int page) -{ - unsigned long data_width = 4; - unsigned long data_phase_addr = 0; - u8 *p; - - chip->write_buf(mtd, buf, mtd->writesize); - - p = chip->oob_poi; - chip->write_buf(mtd, p, (mtd->oobsize - data_width)); - p += (mtd->oobsize - data_width); - - data_phase_addr = (unsigned long)chip->IO_ADDR_W; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - data_phase_addr |= (1 << END_CMD_VALID_SHIFT); - chip->IO_ADDR_W = (void __iomem *)data_phase_addr; - - chip->write_buf(mtd, p, data_width); - - return 0; -} - -/* - * nand_write_page_hwecc - Hardware ECC based page write function - * @mtd: Pointer to the mtd info structure - * @chip: Pointer to the NAND chip info structure - * @buf: Pointer to the data buffer - * @oob_required: must write chip->oob_poi to OOB - * - * This functions writes data and hardware generated ECC values in to the page. - */ -static int zynq_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, int oob_required, int page) -{ - int i, eccsteps, eccsize = chip->ecc.size; - u8 *ecc_calc = chip->buffers->ecccalc; - const u8 *p = buf; - u32 *eccpos = chip->ecc.layout->eccpos; - unsigned long data_phase_addr = 0; - unsigned long data_width = 4; - u8 *oob_ptr; - - for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { - chip->write_buf(mtd, p, eccsize); - p += eccsize; - } - chip->write_buf(mtd, p, (eccsize - data_width)); - p += eccsize - data_width; - - /* Set ECC Last bit to 1 */ - data_phase_addr = (unsigned long) chip->IO_ADDR_W; - data_phase_addr |= ZYNQ_NAND_ECC_LAST; - chip->IO_ADDR_W = (void __iomem *)data_phase_addr; - chip->write_buf(mtd, p, data_width); - - /* Wait for ECC to be calculated and read the error values */ - p = buf; - chip->ecc.calculate(mtd, p, &ecc_calc[0]); - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]); - - /* Clear ECC last bit */ - data_phase_addr = (unsigned long)chip->IO_ADDR_W; - data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; - chip->IO_ADDR_W = (void __iomem *)data_phase_addr; - - /* Write the spare area with ECC bytes */ - oob_ptr = chip->oob_poi; - chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); - - data_phase_addr = (unsigned long)chip->IO_ADDR_W; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - data_phase_addr |= (1 << END_CMD_VALID_SHIFT); - chip->IO_ADDR_W = (void __iomem *)data_phase_addr; - oob_ptr += (mtd->oobsize - data_width); - chip->write_buf(mtd, oob_ptr, data_width); - - return 0; -} - -/* - * zynq_nand_write_page_swecc - [REPLACABLE] software ecc based page - * write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * @oob_required: must write chip->oob_poi to OOB - */ -static int zynq_nand_write_page_swecc(struct mtd_info *mtd, - struct nand_chip *chip, const u8 *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - u8 *ecc_calc = chip->buffers->ecccalc; - const u8 *p = buf; - u32 *eccpos = chip->ecc.layout->eccpos; - - /* Software ecc calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); -} - -/* - * nand_read_page_hwecc - Hardware ECC based page read function - * @mtd: Pointer to the mtd info structure - * @chip: Pointer to the NAND chip info structure - * @buf: Pointer to the buffer to store read data - * @oob_required: must write chip->oob_poi to OOB - * @page: page number to read - * - * This functions reads data and checks the data integrity by comparing hardware - * generated ECC values and read ECC values from spare area. - * - * returns: 0 always and updates ECC operation status in to MTD structure - */ -static int zynq_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, int oob_required, int page) -{ - int i, stat, eccsteps, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - u8 *p = buf; - u8 *ecc_calc = chip->buffers->ecccalc; - u8 *ecc_code = chip->buffers->ecccode; - u32 *eccpos = chip->ecc.layout->eccpos; - unsigned long data_phase_addr = 0; - unsigned long data_width = 4; - u8 *oob_ptr; - - for (eccsteps = chip->ecc.steps; (eccsteps - 1); eccsteps--) { - chip->read_buf(mtd, p, eccsize); - p += eccsize; - } - chip->read_buf(mtd, p, (eccsize - data_width)); - p += eccsize - data_width; - - /* Set ECC Last bit to 1 */ - data_phase_addr = (unsigned long)chip->IO_ADDR_R; - data_phase_addr |= ZYNQ_NAND_ECC_LAST; - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - chip->read_buf(mtd, p, data_width); - - /* Read the calculated ECC value */ - p = buf; - chip->ecc.calculate(mtd, p, &ecc_calc[0]); - - /* Clear ECC last bit */ - data_phase_addr = (unsigned long)chip->IO_ADDR_R; - data_phase_addr &= ~ZYNQ_NAND_ECC_LAST; - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - - /* Read the stored ECC value */ - oob_ptr = chip->oob_poi; - chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width)); - - /* de-assert chip select */ - data_phase_addr = (unsigned long)chip->IO_ADDR_R; - data_phase_addr |= ZYNQ_NAND_CLEAR_CS; - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - - oob_ptr += (mtd->oobsize - data_width); - chip->read_buf(mtd, oob_ptr, data_width); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = ~(chip->oob_poi[eccpos[i]]); - - eccsteps = chip->ecc.steps; - p = buf; - - /* Check ECC error for all blocks and correct if it is correctable */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - return 0; -} - -/* - * zynq_nand_read_page_swecc - [REPLACABLE] software ecc based page - * read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * @page: page number to read - */ -static int zynq_nand_read_page_swecc(struct mtd_info *mtd, - struct nand_chip *chip, u8 *buf, int oob_required, int page) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - u8 *p = buf; - u8 *ecc_calc = chip->buffers->ecccalc; - u8 *ecc_code = chip->buffers->ecccode; - u32 *eccpos = chip->ecc.layout->eccpos; - - chip->ecc.read_page_raw(mtd, chip, buf, 1, page); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - return 0; -} - -/* - * zynq_nand_select_chip - Select the flash device - * @mtd: Pointer to the mtd_info structure - * @chip: Chip number to be selected - * - * This function is empty as the NAND controller handles chip select line - * internally based on the chip address passed in command and data phase. - */ -static void zynq_nand_select_chip(struct mtd_info *mtd, int chip) -{ - /* Not support multiple chips yet */ -} - -/* - * zynq_nand_cmd_function - Send command to NAND device - * @mtd: Pointer to the mtd_info structure - * @command: The command to be sent to the flash device - * @column: The column address for this command, -1 if none - * @page_addr: The page address for this command, -1 if none - */ -static void zynq_nand_cmd_function(struct mtd_info *mtd, unsigned int command, - int column, int page_addr) -{ - struct nand_chip *chip = mtd->priv; - const struct zynq_nand_command_format *curr_cmd = NULL; - u8 addr_cycles = 0; - struct zynq_nand_info *xnand = (struct zynq_nand_info *)chip->priv; - void *cmd_addr; - unsigned long cmd_data = 0; - unsigned long cmd_phase_addr = 0; - unsigned long data_phase_addr = 0; - u8 end_cmd = 0; - u8 end_cmd_valid = 0; - u32 index; - - if (xnand->end_cmd_pending) { - /* Check for end command if this command request is same as the - * pending command then return - */ - if (xnand->end_cmd == command) { - xnand->end_cmd = 0; - xnand->end_cmd_pending = 0; - return; - } - } - - /* Emulate NAND_CMD_READOOB for large page device */ - if ((mtd->writesize > ZYNQ_NAND_ECC_SIZE) && - (command == NAND_CMD_READOOB)) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - /* Get the command format */ - for (index = 0; index < ARRAY_SIZE(zynq_nand_commands); index++) - if (command == zynq_nand_commands[index].start_cmd) - break; - - if (index == ARRAY_SIZE(zynq_nand_commands)) { - printf("%s: Unsupported start cmd %02x\n", __func__, command); - return; - } - curr_cmd = &zynq_nand_commands[index]; - - /* Clear interrupt */ - writel(ZYNQ_MEMC_CLRCR_INT_CLR1, &zynq_nand_smc_base->cfr); - - /* Get the command phase address */ - if (curr_cmd->end_cmd_valid == ZYNQ_NAND_CMD_PHASE) - end_cmd_valid = 1; - - if (curr_cmd->end_cmd == NAND_CMD_NONE) - end_cmd = 0x0; - else - end_cmd = curr_cmd->end_cmd; - - if (command == NAND_CMD_READ0 || - command == NAND_CMD_SEQIN) { - addr_cycles = chip->onfi_params.addr_cycles & - ZYNQ_NAND_ROW_ADDR_CYCL_MASK; - addr_cycles += ((chip->onfi_params.addr_cycles & - ZYNQ_NAND_COL_ADDR_CYCL_MASK) >> 4); - } else { - addr_cycles = curr_cmd->addr_cycles; - } - - cmd_phase_addr = (unsigned long)xnand->nand_base | - (addr_cycles << ADDR_CYCLES_SHIFT) | - (end_cmd_valid << END_CMD_VALID_SHIFT) | - (COMMAND_PHASE) | - (end_cmd << END_CMD_SHIFT) | - (curr_cmd->start_cmd << START_CMD_SHIFT); - - cmd_addr = (void __iomem *)cmd_phase_addr; - - /* Get the data phase address */ - end_cmd_valid = 0; - - data_phase_addr = (unsigned long)xnand->nand_base | - (0x0 << CLEAR_CS_SHIFT) | - (end_cmd_valid << END_CMD_VALID_SHIFT) | - (DATA_PHASE) | - (end_cmd << END_CMD_SHIFT) | - (0x0 << ECC_LAST_SHIFT); - - chip->IO_ADDR_R = (void __iomem *)data_phase_addr; - chip->IO_ADDR_W = chip->IO_ADDR_R; - - /* Command phase AXI Read & Write */ - if (column != -1 && page_addr != -1) { - /* Adjust columns for 16 bit bus width */ - if (chip->options & NAND_BUSWIDTH_16) - column >>= 1; - cmd_data = column; - if (mtd->writesize > ZYNQ_NAND_ECC_SIZE) { - cmd_data |= page_addr << 16; - /* Another address cycle for devices > 128MiB */ - if (chip->chipsize > (128 << 20)) { - writel(cmd_data, cmd_addr); - cmd_data = (page_addr >> 16); - } - } else { - cmd_data |= page_addr << 8; - } - } else if (page_addr != -1) { /* Erase */ - cmd_data = page_addr; - } else if (column != -1) { /* Change read/write column, read id etc */ - /* Adjust columns for 16 bit bus width */ - if ((chip->options & NAND_BUSWIDTH_16) && - ((command == NAND_CMD_READ0) || - (command == NAND_CMD_SEQIN) || - (command == NAND_CMD_RNDOUT) || - (command == NAND_CMD_RNDIN))) - column >>= 1; - cmd_data = column; - } - - writel(cmd_data, cmd_addr); - - if (curr_cmd->end_cmd_valid) { - xnand->end_cmd = curr_cmd->end_cmd; - xnand->end_cmd_pending = 1; - } - - ndelay(100); - - if ((command == NAND_CMD_READ0) || - (command == NAND_CMD_RESET) || - (command == NAND_CMD_PARAM) || - (command == NAND_CMD_GET_FEATURES)) - /* wait until command is processed */ - nand_wait_ready(mtd); -} - -/* - * zynq_nand_read_buf - read chip data into buffer - * @mtd: MTD device structure - * @buf: buffer to store date - * @len: number of bytes to read - */ -static void zynq_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) -{ - struct nand_chip *chip = mtd->priv; - - /* Make sure that buf is 32 bit aligned */ - if (((unsigned long)buf & 0x3) != 0) { - if (((unsigned long)buf & 0x1) != 0) { - if (len) { - *buf = readb(chip->IO_ADDR_R); - buf += 1; - len--; - } - } - - if (((unsigned long)buf & 0x3) != 0) { - if (len >= 2) { - *(u16 *)buf = readw(chip->IO_ADDR_R); - buf += 2; - len -= 2; - } - } - } - - /* copy aligned data */ - while (len >= 4) { - *(u32 *)buf = readl(chip->IO_ADDR_R); - buf += 4; - len -= 4; - } - - /* mop up any remaining bytes */ - if (len) { - if (len >= 2) { - *(u16 *)buf = readw(chip->IO_ADDR_R); - buf += 2; - len -= 2; - } - if (len) - *buf = readb(chip->IO_ADDR_R); - } -} - -/* - * zynq_nand_write_buf - write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - */ -static void zynq_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) -{ - struct nand_chip *chip = mtd->priv; - const u32 *nand = chip->IO_ADDR_W; - - /* Make sure that buf is 32 bit aligned */ - if (((unsigned long)buf & 0x3) != 0) { - if (((unsigned long)buf & 0x1) != 0) { - if (len) { - writeb(*buf, nand); - buf += 1; - len--; - } - } - - if (((unsigned long)buf & 0x3) != 0) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - } - } - - /* copy aligned data */ - while (len >= 4) { - writel(*(u32 *)buf, nand); - buf += 4; - len -= 4; - } - - /* mop up any remaining bytes */ - if (len) { - if (len >= 2) { - writew(*(u16 *)buf, nand); - buf += 2; - len -= 2; - } - - if (len) - writeb(*buf, nand); - } -} - -/* - * zynq_nand_device_ready - Check device ready/busy line - * @mtd: Pointer to the mtd_info structure - * - * returns: 0 on busy or 1 on ready state - */ -static int zynq_nand_device_ready(struct mtd_info *mtd) -{ - u32 csr_val; - - csr_val = readl(&zynq_nand_smc_base->csr); - /* Check the raw_int_status1 bit */ - if (csr_val & ZYNQ_MEMC_SR_RAW_INT_ST1) { - /* Clear the interrupt condition */ - writel(ZYNQ_MEMC_SR_INT_ST1, &zynq_nand_smc_base->cfr); - return 1; - } - - return 0; -} - -static int zynq_nand_check_is_16bit_bw_flash(void) -{ - int is_16bit_bw = NAND_BW_UNKNOWN; - int mio_num_8bit = 0, mio_num_16bit = 0; - - mio_num_8bit = zynq_slcr_get_mio_pin_status("nand8"); - if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT) - is_16bit_bw = NAND_BW_8BIT; - - mio_num_16bit = zynq_slcr_get_mio_pin_status("nand16"); - if (mio_num_8bit == ZYNQ_NAND_MIO_NUM_NAND_8BIT && - mio_num_16bit == ZYNQ_NAND_MIO_NUM_NAND_16BIT) - is_16bit_bw = NAND_BW_16BIT; - - return is_16bit_bw; -} - -static int zynq_nand_init(struct nand_chip *nand_chip, int devnum) -{ - struct zynq_nand_info *xnand; - struct mtd_info *mtd; - unsigned long ecc_page_size; - u8 maf_id, dev_id, i; - u8 get_feature[4]; - u8 set_feature[4] = {ONDIE_ECC_FEATURE_ENABLE, 0x00, 0x00, 0x00}; - unsigned long ecc_cfg; - int ondie_ecc_enabled = 0; - int err = -1; - int is_16bit_bw; - - xnand = calloc(1, sizeof(struct zynq_nand_info)); - if (!xnand) { - printf("%s: failed to allocate\n", __func__); - goto fail; - } - - xnand->nand_base = (void __iomem *)ZYNQ_NAND_BASEADDR; - mtd = nand_to_mtd(nand_chip); - - nand_chip->priv = xnand; - mtd->priv = nand_chip; - - /* Set address of NAND IO lines */ - nand_chip->IO_ADDR_R = xnand->nand_base; - nand_chip->IO_ADDR_W = xnand->nand_base; - - /* Set the driver entry points for MTD */ - nand_chip->cmdfunc = zynq_nand_cmd_function; - nand_chip->dev_ready = zynq_nand_device_ready; - nand_chip->select_chip = zynq_nand_select_chip; - - /* If we don't set this delay driver sets 20us by default */ - nand_chip->chip_delay = 30; - - /* Buffer read/write routines */ - nand_chip->read_buf = zynq_nand_read_buf; - nand_chip->write_buf = zynq_nand_write_buf; - - is_16bit_bw = zynq_nand_check_is_16bit_bw_flash(); - if (is_16bit_bw == NAND_BW_UNKNOWN) { - printf("%s: Unable detect NAND based on MIO settings\n", - __func__); - goto fail; - } - - if (is_16bit_bw == NAND_BW_16BIT) - nand_chip->options = NAND_BUSWIDTH_16; - - nand_chip->bbt_options = NAND_BBT_USE_FLASH; - - /* Initialize the NAND flash interface on NAND controller */ - if (zynq_nand_init_nand_flash(nand_chip->options) < 0) { - printf("%s: nand flash init failed\n", __func__); - goto fail; - } - - /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { - printf("%s: nand_scan_ident failed\n", __func__); - goto fail; - } - /* Send the command for reading device ID */ - nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - /* Read manufacturer and device IDs */ - maf_id = nand_chip->read_byte(mtd); - dev_id = nand_chip->read_byte(mtd); - - if ((maf_id == 0x2c) && ((dev_id == 0xf1) || - (dev_id == 0xa1) || (dev_id == 0xb1) || - (dev_id == 0xaa) || (dev_id == 0xba) || - (dev_id == 0xda) || (dev_id == 0xca) || - (dev_id == 0xac) || (dev_id == 0xbc) || - (dev_id == 0xdc) || (dev_id == 0xcc) || - (dev_id == 0xa3) || (dev_id == 0xb3) || - (dev_id == 0xd3) || (dev_id == 0xc3))) { - nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, - ONDIE_ECC_FEATURE_ADDR, -1); - for (i = 0; i < 4; i++) - writeb(set_feature[i], nand_chip->IO_ADDR_W); - - /* Wait for 1us after writing data with SET_FEATURES command */ - ndelay(1000); - - nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, - ONDIE_ECC_FEATURE_ADDR, -1); - nand_chip->read_buf(mtd, get_feature, 4); - - if (get_feature[0] & ONDIE_ECC_FEATURE_ENABLE) { - debug("%s: OnDie ECC flash\n", __func__); - ondie_ecc_enabled = 1; - } else { - printf("%s: Unable to detect OnDie ECC\n", __func__); - } - } - - if (ondie_ecc_enabled) { - /* Bypass the controller ECC block */ - ecc_cfg = readl(&zynq_nand_smc_base->emcr); - ecc_cfg &= ~ZYNQ_MEMC_NAND_ECC_MODE_MASK; - writel(ecc_cfg, &zynq_nand_smc_base->emcr); - - /* The software ECC routines won't work - * with the SMC controller - */ - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.strength = 1; - nand_chip->ecc.read_page = zynq_nand_read_page_raw_nooob; - nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw; - nand_chip->ecc.write_page = zynq_nand_write_page_raw; - nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; - nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; - nand_chip->ecc.read_oob = zynq_nand_read_oob; - nand_chip->ecc.write_oob = zynq_nand_write_oob; - nand_chip->ecc.size = mtd->writesize; - nand_chip->ecc.bytes = 0; - - /* NAND with on-die ECC supports subpage reads */ - nand_chip->options |= NAND_SUBPAGE_READ; - - /* On-Die ECC spare bytes offset 8 is used for ECC codes */ - if (ondie_ecc_enabled) { - nand_chip->ecc.layout = &ondie_nand_oob_64; - /* Use the BBT pattern descriptors */ - nand_chip->bbt_td = &bbt_main_descr; - nand_chip->bbt_md = &bbt_mirror_descr; - } - } else { - /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */ - nand_chip->ecc.mode = NAND_ECC_HW; - nand_chip->ecc.strength = 1; - nand_chip->ecc.size = ZYNQ_NAND_ECC_SIZE; - nand_chip->ecc.bytes = 3; - nand_chip->ecc.calculate = zynq_nand_calculate_hwecc; - nand_chip->ecc.correct = zynq_nand_correct_data; - nand_chip->ecc.hwctl = NULL; - nand_chip->ecc.read_page = zynq_nand_read_page_hwecc; - nand_chip->ecc.write_page = zynq_nand_write_page_hwecc; - nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw; - nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw; - nand_chip->ecc.read_oob = zynq_nand_read_oob; - nand_chip->ecc.write_oob = zynq_nand_write_oob; - - switch (mtd->writesize) { - case 512: - ecc_page_size = 0x1; - /* Set the ECC memory config register */ - writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), - &zynq_nand_smc_base->emcr); - break; - case 1024: - ecc_page_size = 0x2; - /* Set the ECC memory config register */ - writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), - &zynq_nand_smc_base->emcr); - break; - case 2048: - ecc_page_size = 0x3; - /* Set the ECC memory config register */ - writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size), - &zynq_nand_smc_base->emcr); - break; - default: - nand_chip->ecc.mode = NAND_ECC_SOFT; - nand_chip->ecc.calculate = nand_calculate_ecc; - nand_chip->ecc.correct = nand_correct_data; - nand_chip->ecc.read_page = zynq_nand_read_page_swecc; - nand_chip->ecc.write_page = zynq_nand_write_page_swecc; - nand_chip->ecc.size = 256; - break; - } - - if (mtd->oobsize == 16) - nand_chip->ecc.layout = &nand_oob_16; - else if (mtd->oobsize == 64) - nand_chip->ecc.layout = &nand_oob_64; - else - printf("%s: No oob layout found\n", __func__); - } - - /* Second phase scan */ - if (nand_scan_tail(mtd)) { - printf("%s: nand_scan_tail failed\n", __func__); - goto fail; - } - if (nand_register(devnum, mtd)) - goto fail; - return 0; -fail: - free(xnand); - return err; -} - -static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE]; - -void board_nand_init(void) -{ - struct nand_chip *nand = &nand_chip[0]; - - if (zynq_nand_init(nand, 0)) - puts("ZYNQ NAND init failed\n"); -} diff --git a/include/configs/MPC8313ERDB.h b/include/configs/MPC8313ERDB.h index 884dd4125e..5b5c38f2c9 100644 --- a/include/configs/MPC8313ERDB.h +++ b/include/configs/MPC8313ERDB.h @@ -240,7 +240,7 @@ /* LB refresh timer prescal, 266MHz/32 */ #define CONFIG_SYS_LBC_MRTPR 0x20000000 /*TODO */ -/* drivers/mtd/nand/nand.c */ +/* drivers/mtd/nand/raw/nand.c */ #if defined(CONFIG_NAND) && defined(CONFIG_SPL_BUILD) #define CONFIG_SYS_NAND_BASE 0xFFF00000 #else -- cgit v1.2.3 From 3657b2f4a309ed2e527c805333f9485757912397 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:08 +0200 Subject: mtd: rename nand into rawnand in Kconfig prompt Sync the Kconfig raw NAND entry title with the code architecture. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 1e4ea7bdd4..008f7b4b4b 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -1,6 +1,6 @@ menuconfig NAND - bool "NAND Device Support" + bool "Raw NAND Device Support" if NAND config SYS_NAND_SELF_INIT -- cgit v1.2.3 From b95db8d33a1e920801816e47ffc5c6f18acce024 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:09 +0200 Subject: mtd: nand: Add core infrastructure to deal with NAND devices Add an intermediate layer to abstract NAND device interface so that some logic can be shared between SPI NANDs, parallel/raw NANDs, OneNANDs, ... Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/Kconfig | 3 + drivers/mtd/nand/Makefile | 2 + drivers/mtd/nand/bbt.c | 132 +++++++++ drivers/mtd/nand/core.c | 243 +++++++++++++++ include/linux/mtd/nand.h | 731 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1111 insertions(+) create mode 100644 drivers/mtd/nand/bbt.c create mode 100644 drivers/mtd/nand/core.c create mode 100644 include/linux/mtd/nand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6d53734718..1c1a1f487e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1 +1,4 @@ +config MTD_NAND_CORE + tristate + source "drivers/mtd/nand/raw/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 69f40d1563..cd492dbc14 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0+ +nandcore-objs := core.o bbt.o +obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c new file mode 100644 index 0000000000..7e0ad3190c --- /dev/null +++ b/drivers/mtd/nand/bbt.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand-bbt: " fmt + +#include +#ifndef __UBOOT__ +#include +#endif + +/** + * nanddev_bbt_init() - Initialize the BBT (Bad Block Table) + * @nand: NAND device + * + * Initialize the in-memory BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_init(struct nand_device *nand) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned int nblocks = nanddev_neraseblocks(nand); + unsigned int nwords = DIV_ROUND_UP(nblocks * bits_per_block, + BITS_PER_LONG); + + nand->bbt.cache = kzalloc(nwords, GFP_KERNEL); + if (!nand->bbt.cache) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_init); + +/** + * nanddev_bbt_cleanup() - Cleanup the BBT (Bad Block Table) + * @nand: NAND device + * + * Undoes what has been done in nanddev_bbt_init() + */ +void nanddev_bbt_cleanup(struct nand_device *nand) +{ + kfree(nand->bbt.cache); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_cleanup); + +/** + * nanddev_bbt_update() - Update a BBT + * @nand: nand device + * + * Update the BBT. Currently a NOP function since on-flash bbt is not yet + * supported. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_bbt_update(struct nand_device *nand) +{ + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_update); + +/** + * nanddev_bbt_get_block_status() - Return the status of an eraseblock + * @nand: nand device + * @entry: the BBT entry + * + * Return: a positive number nand_bbt_block_status status or -%ERANGE if @entry + * is bigger than the BBT size. + */ +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long status; + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + status = pos[0] >> offs; + if (bits_per_block + offs > BITS_PER_LONG) + status |= pos[1] << (BITS_PER_LONG - offs); + + return status & GENMASK(bits_per_block - 1, 0); +} +EXPORT_SYMBOL_GPL(nanddev_bbt_get_block_status); + +/** + * nanddev_bbt_set_block_status() - Update the status of an eraseblock in the + * in-memory BBT + * @nand: nand device + * @entry: the BBT entry to update + * @status: the new status + * + * Update an entry of the in-memory BBT. If you want to push the updated BBT + * the NAND you should call nanddev_bbt_update(). + * + * Return: 0 in case of success or -%ERANGE if @entry is bigger than the BBT + * size. + */ +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status) +{ + unsigned int bits_per_block = fls(NAND_BBT_BLOCK_NUM_STATUS); + unsigned long *pos = nand->bbt.cache + + ((entry * bits_per_block) / BITS_PER_LONG); + unsigned int offs = (entry * bits_per_block) % BITS_PER_LONG; + unsigned long val = status & GENMASK(bits_per_block - 1, 0); + + if (entry >= nanddev_neraseblocks(nand)) + return -ERANGE; + + pos[0] &= ~GENMASK(offs + bits_per_block - 1, offs); + pos[0] |= val << offs; + + if (bits_per_block + offs > BITS_PER_LONG) { + unsigned int rbits = bits_per_block + offs - BITS_PER_LONG; + + pos[1] &= ~GENMASK(rbits - 1, 0); + pos[1] |= val >> rbits; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_bbt_set_block_status); diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c new file mode 100644 index 0000000000..0b793695cc --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#define pr_fmt(fmt) "nand: " fmt + +#ifndef __UBOOT__ +#include +#endif +#include + +/** + * nanddev_isbad() - Check if a block is bad + * @nand: NAND device + * @pos: position pointing to the block we want to check + * + * Return: true if the block is bad, false otherwise. + */ +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_bbt_is_initialized(nand)) { + unsigned int entry; + int status; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + /* Lazy block status retrieval */ + if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { + if (nand->ops->isbad(nand, pos)) + status = NAND_BBT_BLOCK_FACTORY_BAD; + else + status = NAND_BBT_BLOCK_GOOD; + + nanddev_bbt_set_block_status(nand, entry, status); + } + + if (status == NAND_BBT_BLOCK_WORN || + status == NAND_BBT_BLOCK_FACTORY_BAD) + return true; + + return false; + } + + return nand->ops->isbad(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_isbad); + +/** + * nanddev_markbad() - Mark a block as bad + * @nand: NAND device + * @pos: position of the block to mark bad + * + * Mark a block bad. This function is updating the BBT if available and + * calls the low-level markbad hook (nand->ops->markbad()). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + unsigned int entry; + int ret = 0; + + if (nanddev_isbad(nand, pos)) + return 0; + + ret = nand->ops->markbad(nand, pos); + if (ret) + pr_warn("failed to write BBM to block @%llx (err = %d)\n", + nanddev_pos_to_offs(nand, pos), ret); + + if (!nanddev_bbt_is_initialized(nand)) + goto out; + + entry = nanddev_bbt_pos_to_entry(nand, pos); + ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); + if (ret) + goto out; + + ret = nanddev_bbt_update(nand); + +out: + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} +EXPORT_SYMBOL_GPL(nanddev_markbad); + +/** + * nanddev_isreserved() - Check whether an eraseblock is reserved or not + * @nand: NAND device + * @pos: NAND position to test + * + * Checks whether the eraseblock pointed by @pos is reserved or not. + * + * Return: true if the eraseblock is reserved, false otherwise. + */ +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) +{ + unsigned int entry; + int status; + + if (!nanddev_bbt_is_initialized(nand)) + return false; + + /* Return info from the table */ + entry = nanddev_bbt_pos_to_entry(nand, pos); + status = nanddev_bbt_get_block_status(nand, entry); + return status == NAND_BBT_BLOCK_RESERVED; +} +EXPORT_SYMBOL_GPL(nanddev_isreserved); + +/** + * nanddev_erase() - Erase a NAND portion + * @nand: NAND device + * @pos: position of the block to erase + * + * Erases the block if it's not bad. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { + pr_warn("attempt to erase a bad/reserved block @%llx\n", + nanddev_pos_to_offs(nand, pos)); + return -EIO; + } + + return nand->ops->erase(nand, pos); +} +EXPORT_SYMBOL_GPL(nanddev_erase); + +/** + * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices + * @mtd: MTD device + * @einfo: erase request + * + * This is a simple mtd->_erase() implementation iterating over all blocks + * concerned by @einfo and calling nand->ops->erase() on each of them. + * + * Note that mtd->_erase should not be directly assigned to this helper, + * because there's no locking here. NAND specialized layers should instead + * implement there own wrapper around nanddev_mtd_erase() taking the + * appropriate lock before calling nanddev_mtd_erase(). + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos, last; + int ret; + + nanddev_offs_to_pos(nand, einfo->addr, &pos); + nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); + while (nanddev_pos_cmp(&pos, &last) <= 0) { + ret = nanddev_erase(nand, &pos); + if (ret) { + einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); + + return ret; + } + + nanddev_pos_next_eraseblock(nand, &pos); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_mtd_erase); + +/** + * nanddev_init() - Initialize a NAND device + * @nand: NAND device + * @ops: NAND device operations + * @owner: NAND device owner + * + * Initializes a NAND device object. Consistency checks are done on @ops and + * @nand->memorg. Also takes care of initializing the BBT. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_memory_organization *memorg = nanddev_get_memorg(nand); + + if (!nand || !ops) + return -EINVAL; + + if (!ops->erase || !ops->markbad || !ops->isbad) + return -EINVAL; + + if (!memorg->bits_per_cell || !memorg->pagesize || + !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || + !memorg->planes_per_lun || !memorg->luns_per_target || + !memorg->ntargets) + return -EINVAL; + + nand->rowconv.eraseblock_addr_shift = + fls(memorg->pages_per_eraseblock - 1); + nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + + nand->rowconv.eraseblock_addr_shift; + + nand->ops = ops; + + mtd->type = memorg->bits_per_cell == 1 ? + MTD_NANDFLASH : MTD_MLCNANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; + mtd->writesize = memorg->pagesize; + mtd->writebufsize = memorg->pagesize; + mtd->oobsize = memorg->oobsize; + mtd->size = nanddev_size(nand); + mtd->owner = owner; + + return nanddev_bbt_init(nand); +} +EXPORT_SYMBOL_GPL(nanddev_init); + +/** + * nanddev_cleanup() - Release resources allocated in nanddev_init() + * @nand: NAND device + * + * Basically undoes what has been done in nanddev_init(). + */ +void nanddev_cleanup(struct nand_device *nand) +{ + if (nanddev_bbt_is_initialized(nand)) + nanddev_bbt_cleanup(nand); +} +EXPORT_SYMBOL_GPL(nanddev_cleanup); + +MODULE_DESCRIPTION("Generic NAND framework"); +MODULE_AUTHOR("Boris Brezillon "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h new file mode 100644 index 0000000000..ada7af4a41 --- /dev/null +++ b/include/linux/mtd/nand.h @@ -0,0 +1,731 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2017 - Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H + +#include + +/** + * struct nand_memory_organization - Memory organization structure + * @bits_per_cell: number of bits per NAND cell + * @pagesize: page size + * @oobsize: OOB area size + * @pages_per_eraseblock: number of pages per eraseblock + * @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number) + * @planes_per_lun: number of planes per LUN + * @luns_per_target: number of LUN per target (target is a synonym for die) + * @ntargets: total number of targets exposed by the NAND device + */ +struct nand_memory_organization { + unsigned int bits_per_cell; + unsigned int pagesize; + unsigned int oobsize; + unsigned int pages_per_eraseblock; + unsigned int eraseblocks_per_lun; + unsigned int planes_per_lun; + unsigned int luns_per_target; + unsigned int ntargets; +}; + +#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \ + { \ + .bits_per_cell = (bpc), \ + .pagesize = (ps), \ + .oobsize = (os), \ + .pages_per_eraseblock = (ppe), \ + .eraseblocks_per_lun = (epl), \ + .planes_per_lun = (ppl), \ + .luns_per_target = (lpt), \ + .ntargets = (nt), \ + } + +/** + * struct nand_row_converter - Information needed to convert an absolute offset + * into a row address + * @lun_addr_shift: position of the LUN identifier in the row address + * @eraseblock_addr_shift: position of the eraseblock identifier in the row + * address + */ +struct nand_row_converter { + unsigned int lun_addr_shift; + unsigned int eraseblock_addr_shift; +}; + +/** + * struct nand_pos - NAND position object + * @target: the NAND target/die + * @lun: the LUN identifier + * @plane: the plane within the LUN + * @eraseblock: the eraseblock within the LUN + * @page: the page within the LUN + * + * These information are usually used by specific sub-layers to select the + * appropriate target/die and generate a row address to pass to the device. + */ +struct nand_pos { + unsigned int target; + unsigned int lun; + unsigned int plane; + unsigned int eraseblock; + unsigned int page; +}; + +/** + * struct nand_page_io_req - NAND I/O request object + * @pos: the position this I/O request is targeting + * @dataoffs: the offset within the page + * @datalen: number of data bytes to read from/write to this page + * @databuf: buffer to store data in or get data from + * @ooboffs: the OOB offset within the page + * @ooblen: the number of OOB bytes to read from/write to this page + * @oobbuf: buffer to store OOB data in or get OOB data from + * + * This object is used to pass per-page I/O requests to NAND sub-layers. This + * way all useful information are already formatted in a useful way and + * specific NAND layers can focus on translating these information into + * specific commands/operations. + */ +struct nand_page_io_req { + struct nand_pos pos; + unsigned int dataoffs; + unsigned int datalen; + union { + const void *out; + void *in; + } databuf; + unsigned int ooboffs; + unsigned int ooblen; + union { + const void *out; + void *in; + } oobbuf; +}; + +/** + * struct nand_ecc_req - NAND ECC requirements + * @strength: ECC strength + * @step_size: ECC step/block size + */ +struct nand_ecc_req { + unsigned int strength; + unsigned int step_size; +}; + +#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) } + +/** + * struct nand_bbt - bad block table object + * @cache: in memory BBT cache + */ +struct nand_bbt { + unsigned long *cache; +}; + +struct nand_device; + +/** + * struct nand_ops - NAND operations + * @erase: erase a specific block. No need to check if the block is bad before + * erasing, this has been taken care of by the generic NAND layer + * @markbad: mark a specific block bad. No need to check if the block is + * already marked bad, this has been taken care of by the generic + * NAND layer. This method should just write the BBM (Bad Block + * Marker) so that future call to struct_nand_ops->isbad() return + * true + * @isbad: check whether a block is bad or not. This method should just read + * the BBM and return whether the block is bad or not based on what it + * reads + * + * These are all low level operations that should be implemented by specialized + * NAND layers (SPI NAND, raw NAND, ...). + */ +struct nand_ops { + int (*erase)(struct nand_device *nand, const struct nand_pos *pos); + int (*markbad)(struct nand_device *nand, const struct nand_pos *pos); + bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos); +}; + +/** + * struct nand_device - NAND device + * @mtd: MTD instance attached to the NAND device + * @memorg: memory layout + * @eccreq: ECC requirements + * @rowconv: position to row address converter + * @bbt: bad block table info + * @ops: NAND operations attached to the NAND device + * + * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND) + * should declare their own NAND object embedding a nand_device struct (that's + * how inheritance is done). + * struct_nand_device->memorg and struct_nand_device->eccreq should be filled + * at device detection time to reflect the NAND device + * capabilities/requirements. Once this is done nanddev_init() can be called. + * It will take care of converting NAND information into MTD ones, which means + * the specialized NAND layers should never manually tweak + * struct_nand_device->mtd except for the ->_read/write() hooks. + */ +struct nand_device { + struct mtd_info *mtd; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct nand_row_converter rowconv; + struct nand_bbt bbt; + const struct nand_ops *ops; +}; + +/** + * struct nand_io_iter - NAND I/O iterator + * @req: current I/O request + * @oobbytes_per_page: maximum number of OOB bytes per page + * @dataleft: remaining number of data bytes to read/write + * @oobleft: remaining number of OOB bytes to read/write + * + * Can be used by specialized NAND layers to iterate over all pages covered + * by an MTD I/O request, which should greatly simplifies the boiler-plate + * code needed to read/write data from/to a NAND device. + */ +struct nand_io_iter { + struct nand_page_io_req req; + unsigned int oobbytes_per_page; + unsigned int dataleft; + unsigned int oobleft; +}; + +/** + * mtd_to_nanddev() - Get the NAND device attached to the MTD instance + * @mtd: MTD instance + * + * Return: the NAND device embedding @mtd. + */ +static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/** + * nanddev_to_mtd() - Get the MTD device attached to a NAND device + * @nand: NAND device + * + * Return: the MTD device embedded in @nand. + */ +static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand) +{ + return nand->mtd; +} + +/* + * nanddev_bits_per_cell() - Get the number of bits per cell + * @nand: NAND device + * + * Return: the number of bits per cell. + */ +static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand) +{ + return nand->memorg.bits_per_cell; +} + +/** + * nanddev_page_size() - Get NAND page size + * @nand: NAND device + * + * Return: the page size. + */ +static inline size_t nanddev_page_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize; +} + +/** + * nanddev_per_page_oobsize() - Get NAND OOB size + * @nand: NAND device + * + * Return: the OOB size. + */ +static inline unsigned int +nanddev_per_page_oobsize(const struct nand_device *nand) +{ + return nand->memorg.oobsize; +} + +/** + * nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock + * @nand: NAND device + * + * Return: the number of pages per eraseblock. + */ +static inline unsigned int +nanddev_pages_per_eraseblock(const struct nand_device *nand) +{ + return nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_per_page_oobsize() - Get NAND erase block size + * @nand: NAND device + * + * Return: the eraseblock size. + */ +static inline size_t nanddev_eraseblock_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN + * @nand: NAND device + * + * Return: the number of eraseblocks per LUN. + */ +static inline unsigned int +nanddev_eraseblocks_per_lun(const struct nand_device *nand) +{ + return nand->memorg.eraseblocks_per_lun; +} + +/** + * nanddev_target_size() - Get the total size provided by a single target/die + * @nand: NAND device + * + * Return: the total size exposed by a single target/die in bytes. + */ +static inline u64 nanddev_target_size(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock * + nand->memorg.pagesize; +} + +/** + * nanddev_ntarget() - Get the total of targets + * @nand: NAND device + * + * Return: the number of targets/dies exposed by @nand. + */ +static inline unsigned int nanddev_ntargets(const struct nand_device *nand) +{ + return nand->memorg.ntargets; +} + +/** + * nanddev_neraseblocks() - Get the total number of erasablocks + * @nand: NAND device + * + * Return: the total number of eraseblocks exposed by @nand. + */ +static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_size() - Get NAND size + * @nand: NAND device + * + * Return: the total size (in bytes) exposed by @nand. + */ +static inline u64 nanddev_size(const struct nand_device *nand) +{ + return nanddev_target_size(nand) * nanddev_ntargets(nand); +} + +/** + * nanddev_get_memorg() - Extract memory organization info from a NAND device + * @nand: NAND device + * + * This can be used by the upper layer to fill the memorg info before calling + * nanddev_init(). + * + * Return: the memorg object embedded in the NAND device. + */ +static inline struct nand_memory_organization * +nanddev_get_memorg(struct nand_device *nand) +{ + return &nand->memorg; +} + +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner); +void nanddev_cleanup(struct nand_device *nand); + +/** + * nanddev_register() - Register a NAND device + * @nand: NAND device + * + * Register a NAND device. + * This function is just a wrapper around mtd_device_register() + * registering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_register(struct nand_device *nand) +{ + return mtd_device_register(nand->mtd, NULL, 0); +} + +/** + * nanddev_unregister() - Unregister a NAND device + * @nand: NAND device + * + * Unregister a NAND device. + * This function is just a wrapper around mtd_device_unregister() + * unregistering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_unregister(struct nand_device *nand) +{ + return mtd_device_unregister(nand->mtd); +} + +/** + * nanddev_set_of_node() - Attach a DT node to a NAND device + * @nand: NAND device + * @np: DT node + * + * Attach a DT node to a NAND device. + */ +static inline void nanddev_set_of_node(struct nand_device *nand, + const struct device_node *np) +{ + mtd_set_of_node(nand->mtd, np); +} + +/** + * nanddev_get_of_node() - Retrieve the DT node attached to a NAND device + * @nand: NAND device + * + * Return: the DT node attached to @nand. + */ +static inline const struct device_node *nanddev_get_of_node(struct nand_device *nand) +{ + return mtd_get_of_node(nand->mtd); +} + +/** + * nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position + * @nand: NAND device + * @offs: absolute NAND offset (usually passed by the MTD layer) + * @pos: a NAND position object to fill in + * + * Converts @offs into a nand_pos representation. + * + * Return: the offset within the NAND page pointed by @pos. + */ +static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand, + loff_t offs, + struct nand_pos *pos) +{ + unsigned int pageoffs; + u64 tmp = offs; + + pageoffs = do_div(tmp, nand->memorg.pagesize); + pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock); + pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun); + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; + pos->lun = do_div(tmp, nand->memorg.luns_per_target); + pos->target = tmp; + + return pageoffs; +} + +/** + * nanddev_pos_cmp() - Compare two NAND positions + * @a: First NAND position + * @b: Second NAND position + * + * Compares two NAND positions. + * + * Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b. + */ +static inline int nanddev_pos_cmp(const struct nand_pos *a, + const struct nand_pos *b) +{ + if (a->target != b->target) + return a->target < b->target ? -1 : 1; + + if (a->lun != b->lun) + return a->lun < b->lun ? -1 : 1; + + if (a->eraseblock != b->eraseblock) + return a->eraseblock < b->eraseblock ? -1 : 1; + + if (a->page != b->page) + return a->page < b->page ? -1 : 1; + + return 0; +} + +/** + * nanddev_pos_to_offs() - Convert a NAND position into an absolute offset + * @nand: NAND device + * @pos: the NAND position to convert + * + * Converts @pos NAND position into an absolute offset. + * + * Return: the absolute offset. Note that @pos points to the beginning of a + * page, if one wants to point to a specific offset within this page + * the returned offset has to be adjusted manually. + */ +static inline loff_t nanddev_pos_to_offs(struct nand_device *nand, + const struct nand_pos *pos) +{ + unsigned int npages; + + npages = pos->page + + ((pos->eraseblock + + (pos->lun + + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun) * + nand->memorg.pages_per_eraseblock); + + return (loff_t)npages * nand->memorg.pagesize; +} + +/** + * nanddev_pos_to_row() - Extract a row address from a NAND position + * @nand: NAND device + * @pos: the position to convert + * + * Converts a NAND position into a row address that can then be passed to the + * device. + * + * Return: the row address extracted from @pos. + */ +static inline unsigned int nanddev_pos_to_row(struct nand_device *nand, + const struct nand_pos *pos) +{ + return (pos->lun << nand->rowconv.lun_addr_shift) | + (pos->eraseblock << nand->rowconv.eraseblock_addr_shift) | + pos->page; +} + +/** + * nanddev_pos_next_target() - Move a position to the next target/die + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next target/die. Useful when you + * want to iterate over all targets/dies of a NAND device. + */ +static inline void nanddev_pos_next_target(struct nand_device *nand, + struct nand_pos *pos) +{ + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; + pos->lun = 0; + pos->target++; +} + +/** + * nanddev_pos_next_lun() - Move a position to the next LUN + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next LUN. Useful when you want to + * iterate over all LUNs of a NAND device. + */ +static inline void nanddev_pos_next_lun(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->lun >= nand->memorg.luns_per_target - 1) + return nanddev_pos_next_target(nand, pos); + + pos->lun++; + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next eraseblock + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next eraseblock. Useful when you + * want to iterate over all eraseblocks of a NAND device. + */ +static inline void nanddev_pos_next_eraseblock(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1) + return nanddev_pos_next_lun(nand, pos); + + pos->eraseblock++; + pos->page = 0; + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next page + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next page. Useful when you want to + * iterate over all pages of a NAND device. + */ +static inline void nanddev_pos_next_page(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->page >= nand->memorg.pages_per_eraseblock - 1) + return nanddev_pos_next_eraseblock(nand, pos); + + pos->page++; +} + +/** + * nand_io_iter_init - Initialize a NAND I/O iterator + * @nand: NAND device + * @offs: absolute offset + * @req: MTD request + * @iter: NAND I/O iterator + * + * Initializes a NAND iterator based on the information passed by the MTD + * layer. + */ +static inline void nanddev_io_iter_init(struct nand_device *nand, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + + iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); + iter->req.ooboffs = req->ooboffs; + iter->oobbytes_per_page = mtd_oobavail(mtd, req); + iter->dataleft = req->len; + iter->oobleft = req->ooblen; + iter->req.databuf.in = req->datbuf; + iter->req.datalen = min_t(unsigned int, + nand->memorg.pagesize - iter->req.dataoffs, + iter->dataleft); + iter->req.oobbuf.in = req->oobbuf; + iter->req.ooblen = min_t(unsigned int, + iter->oobbytes_per_page - iter->req.ooboffs, + iter->oobleft); +} + +/** + * nand_io_iter_next_page - Move to the next page + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Updates the @iter to point to the next page. + */ +static inline void nanddev_io_iter_next_page(struct nand_device *nand, + struct nand_io_iter *iter) +{ + nanddev_pos_next_page(nand, &iter->req.pos); + iter->dataleft -= iter->req.datalen; + iter->req.databuf.in += iter->req.datalen; + iter->oobleft -= iter->req.ooblen; + iter->req.oobbuf.in += iter->req.ooblen; + iter->req.dataoffs = 0; + iter->req.ooboffs = 0; + iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize, + iter->dataleft); + iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page, + iter->oobleft); +} + +/** + * nand_io_iter_end - Should end iteration or not + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Check whether @iter has reached the end of the NAND portion it was asked to + * iterate on or not. + * + * Return: true if @iter has reached the end of the iteration request, false + * otherwise. + */ +static inline bool nanddev_io_iter_end(struct nand_device *nand, + const struct nand_io_iter *iter) +{ + if (iter->dataleft || iter->oobleft) + return false; + + return true; +} + +/** + * nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O + * request + * @nand: NAND device + * @start: start address to read/write from + * @req: MTD I/O request + * @iter: NAND I/O iterator + * + * Should be used for iterate over pages that are contained in an MTD request. + */ +#define nanddev_io_for_each_page(nand, start, req, iter) \ + for (nanddev_io_iter_init(nand, start, req, iter); \ + !nanddev_io_iter_end(nand, iter); \ + nanddev_io_iter_next_page(nand, iter)) + +bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos); +bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); + +/* BBT related functions */ +enum nand_bbt_block_status { + NAND_BBT_BLOCK_STATUS_UNKNOWN, + NAND_BBT_BLOCK_GOOD, + NAND_BBT_BLOCK_WORN, + NAND_BBT_BLOCK_RESERVED, + NAND_BBT_BLOCK_FACTORY_BAD, + NAND_BBT_BLOCK_NUM_STATUS, +}; + +int nanddev_bbt_init(struct nand_device *nand); +void nanddev_bbt_cleanup(struct nand_device *nand); +int nanddev_bbt_update(struct nand_device *nand); +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry); +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status); +int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block); + +/** + * nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry + * @nand: NAND device + * @pos: the NAND position we want to get BBT entry for + * + * Return the BBT entry used to store information about the eraseblock pointed + * by @pos. + * + * Return: the BBT entry storing information about eraseblock pointed by @pos. + */ +static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand, + const struct nand_pos *pos) +{ + return pos->eraseblock + + ((pos->lun + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun); +} + +/** + * nanddev_bbt_is_initialized() - Check if the BBT has been initialized + * @nand: NAND device + * + * Return: true if the BBT has been initialized, false otherwise. + */ +static inline bool nanddev_bbt_is_initialized(struct nand_device *nand) +{ + return !!nand->bbt.cache; +} + +/* MTD -> NAND helper functions. */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); + +#endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From f86787280b37e381f8d82f48583434d62dd16e27 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:10 +0200 Subject: mtd: nand: Pass mode information to nand_page_io_req The NAND sub-layers are likely to need the MTD_OPS_XXX mode information in order to decide if they should enable/disable ECC or how they should place the OOB bytes in the provided OOB buffer. Add a field to nand_page_io_req to pass this information. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- include/linux/mtd/nand.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ada7af4a41..13e8dd1103 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -86,6 +86,7 @@ struct nand_pos { * @ooboffs: the OOB offset within the page * @ooblen: the number of OOB bytes to read from/write to this page * @oobbuf: buffer to store OOB data in or get OOB data from + * @mode: one of the %MTD_OPS_XXX mode * * This object is used to pass per-page I/O requests to NAND sub-layers. This * way all useful information are already formatted in a useful way and @@ -106,6 +107,7 @@ struct nand_page_io_req { const void *out; void *in; } oobbuf; + int mode; }; /** @@ -599,6 +601,7 @@ static inline void nanddev_io_iter_init(struct nand_device *nand, { struct mtd_info *mtd = nanddev_to_mtd(nand); + iter->req.mode = req->mode; iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); iter->req.ooboffs = req->ooboffs; iter->oobbytes_per_page = mtd_oobavail(mtd, req); -- cgit v1.2.3 From d13f5b254a43e292814a618f60a2696ba01267a7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:11 +0200 Subject: spi: Extend the core to ease integration of SPI memory controllers Some controllers are exposing high-level interfaces to access various kind of SPI memories. Unfortunately they do not fit in the current spi_controller model and usually have drivers placed in drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI memories in general. This is an attempt at defining a SPI memory interface which works for all kinds of SPI memories (NORs, NANDs, SRAMs). Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-mem.c | 501 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/spi-mem.h | 258 ++++++++++++++++++++++++++ include/spi.h | 11 ++ 5 files changed, 778 insertions(+) create mode 100644 drivers/spi/spi-mem.c create mode 100644 include/spi-mem.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index dcd719ff0a..9fbd26740d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -18,6 +18,13 @@ config DM_SPI if DM_SPI +config SPI_MEM + bool "SPI memory extension" + help + Enable this option if you want to enable the SPI memory extension. + This extension is meant to simplify interaction with SPI memories + by providing an high-level interface to send memory-like commands. + config ALTERA_SPI bool "Altera SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 728e30c538..bdb5b5a02f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DM_SPI obj-y += spi-uclass.o obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o obj-$(CONFIG_SOFT_SPI) += soft_spi.o +obj-$(CONFIG_SPI_MEM) += spi-mem.o else obj-y += spi.o obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c new file mode 100644 index 0000000000..af9aef009a --- /dev/null +++ b/drivers/spi/spi-mem.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Exceet Electronics GmbH + * Copyright (C) 2018 Bootlin + * + * Author: Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#include "internals.h" +#else +#include +#include +#endif + +#ifndef __UBOOT__ +/** + * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a + * memory operation + * @ctlr: the SPI controller requesting this dma_map() + * @op: the memory operation containing the buffer to map + * @sgt: a pointer to a non-initialized sg_table that will be filled by this + * function + * + * Some controllers might want to do DMA on the data buffer embedded in @op. + * This helper prepares everything for you and provides a ready-to-use + * sg_table. This function is not intended to be called from spi drivers. + * Only SPI controller drivers should use it. + * Note that the caller must ensure the memory region pointed by + * op->data.buf.{in,out} is DMA-able before calling this function. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sgt) +{ + struct device *dmadev; + + if (!op->data.nbytes) + return -EINVAL; + + if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) + dmadev = ctlr->dma_tx->device->dev; + else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) + dmadev = ctlr->dma_rx->device->dev; + else + dmadev = ctlr->dev.parent; + + if (!dmadev) + return -EINVAL; + + return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes, + op->data.dir == SPI_MEM_DATA_IN ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +} +EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data); + +/** + * spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a + * memory operation + * @ctlr: the SPI controller requesting this dma_unmap() + * @op: the memory operation containing the buffer to unmap + * @sgt: a pointer to an sg_table previously initialized by + * spi_controller_dma_map_mem_op_data() + * + * Some controllers might want to do DMA on the data buffer embedded in @op. + * This helper prepares things so that the CPU can access the + * op->data.buf.{in,out} buffer again. + * + * This function is not intended to be called from SPI drivers. Only SPI + * controller drivers should use it. + * + * This function should be called after the DMA operation has finished and is + * only valid if the previous spi_controller_dma_map_mem_op_data() call + * returned 0. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sgt) +{ + struct device *dmadev; + + if (!op->data.nbytes) + return; + + if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx) + dmadev = ctlr->dma_tx->device->dev; + else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx) + dmadev = ctlr->dma_rx->device->dev; + else + dmadev = ctlr->dev.parent; + + spi_unmap_buf(ctlr, dmadev, sgt, + op->data.dir == SPI_MEM_DATA_IN ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +} +EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data); +#endif /* __UBOOT__ */ + +static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx) +{ + u32 mode = slave->mode; + + switch (buswidth) { + case 1: + return 0; + + case 2: + if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || + (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + return 0; + + break; + + case 4: + if ((tx && (mode & SPI_TX_QUAD)) || + (!tx && (mode & SPI_RX_QUAD))) + return 0; + + break; + + default: + break; + } + + return -ENOTSUPP; +} + +bool spi_mem_default_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (spi_check_buswidth_req(slave, op->cmd.buswidth, true)) + return false; + + if (op->addr.nbytes && + spi_check_buswidth_req(slave, op->addr.buswidth, true)) + return false; + + if (op->dummy.nbytes && + spi_check_buswidth_req(slave, op->dummy.buswidth, true)) + return false; + + if (op->data.nbytes && + spi_check_buswidth_req(slave, op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(spi_mem_default_supports_op); + +/** + * spi_mem_supports_op() - Check if a memory device and the controller it is + * connected to support a specific memory operation + * @slave: the SPI device + * @op: the memory operation to check + * + * Some controllers are only supporting Single or Dual IOs, others might only + * support specific opcodes, or it can even be that the controller and device + * both support Quad IOs but the hardware prevents you from using it because + * only 2 IO lines are connected. + * + * This function checks whether a specific operation is supported. + * + * Return: true if @op is supported, false otherwise. + */ +bool spi_mem_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->mem_ops && ops->mem_ops->supports_op) + return ops->mem_ops->supports_op(slave, op); + + return spi_mem_default_supports_op(slave, op); +} +EXPORT_SYMBOL_GPL(spi_mem_supports_op); + +/** + * spi_mem_exec_op() - Execute a memory operation + * @slave: the SPI device + * @op: the memory operation to execute + * + * Executes a memory operation. + * + * This function first checks that @op is supported and then tries to execute + * it. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + unsigned int pos = 0; + const u8 *tx_buf = NULL; + u8 *rx_buf = NULL; + u8 *op_buf; + int op_len; + u32 flag; + int ret; + int i; + + if (!spi_mem_supports_op(slave, op)) + return -ENOTSUPP; + + if (ops->mem_ops) { +#ifndef __UBOOT__ + /* + * Flush the message queue before executing our SPI memory + * operation to prevent preemption of regular SPI transfers. + */ + spi_flush_queue(ctlr); + + if (ctlr->auto_runtime_pm) { + ret = pm_runtime_get_sync(ctlr->dev.parent); + if (ret < 0) { + dev_err(&ctlr->dev, + "Failed to power device: %d\n", + ret); + return ret; + } + } + + mutex_lock(&ctlr->bus_lock_mutex); + mutex_lock(&ctlr->io_mutex); +#endif + ret = ops->mem_ops->exec_op(slave, op); +#ifndef __UBOOT__ + mutex_unlock(&ctlr->io_mutex); + mutex_unlock(&ctlr->bus_lock_mutex); + + if (ctlr->auto_runtime_pm) + pm_runtime_put(ctlr->dev.parent); +#endif + + /* + * Some controllers only optimize specific paths (typically the + * read path) and expect the core to use the regular SPI + * interface in other cases. + */ + if (!ret || ret != -ENOTSUPP) + return ret; + } + +#ifndef __UBOOT__ + tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes + + op->dummy.nbytes; + + /* + * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so + * we're guaranteed that this buffer is DMA-able, as required by the + * SPI layer. + */ + tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA); + if (!tmpbuf) + return -ENOMEM; + + spi_message_init(&msg); + + tmpbuf[0] = op->cmd.opcode; + xfers[xferpos].tx_buf = tmpbuf; + xfers[xferpos].len = sizeof(op->cmd.opcode); + xfers[xferpos].tx_nbits = op->cmd.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen++; + + if (op->addr.nbytes) { + int i; + + for (i = 0; i < op->addr.nbytes; i++) + tmpbuf[i + 1] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + xfers[xferpos].tx_buf = tmpbuf + 1; + xfers[xferpos].len = op->addr.nbytes; + xfers[xferpos].tx_nbits = op->addr.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->addr.nbytes; + } + + if (op->dummy.nbytes) { + memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes); + xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1; + xfers[xferpos].len = op->dummy.nbytes; + xfers[xferpos].tx_nbits = op->dummy.buswidth; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->dummy.nbytes; + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) { + xfers[xferpos].rx_buf = op->data.buf.in; + xfers[xferpos].rx_nbits = op->data.buswidth; + } else { + xfers[xferpos].tx_buf = op->data.buf.out; + xfers[xferpos].tx_nbits = op->data.buswidth; + } + + xfers[xferpos].len = op->data.nbytes; + spi_message_add_tail(&xfers[xferpos], &msg); + xferpos++; + totalxferlen += op->data.nbytes; + } + + ret = spi_sync(slave, &msg); + + kfree(tmpbuf); + + if (ret) + return ret; + + if (msg.actual_length != totalxferlen) + return -EIO; +#else + + /* U-Boot does not support parallel SPI data lanes */ + if ((op->cmd.buswidth != 1) || + (op->addr.nbytes && op->addr.buswidth != 1) || + (op->dummy.nbytes && op->dummy.buswidth != 1) || + (op->data.nbytes && op->data.buswidth != 1)) { + printf("Dual/Quad raw SPI transfers not supported\n"); + return -ENOTSUPP; + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + rx_buf = op->data.buf.in; + else + tx_buf = op->data.buf.out; + } + + op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes; + op_buf = calloc(1, op_len); + + ret = spi_claim_bus(slave); + if (ret < 0) + return ret; + + op_buf[pos++] = op->cmd.opcode; + + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) + op_buf[pos + i] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + + pos += op->addr.nbytes; + } + + if (op->dummy.nbytes) + memset(op_buf + pos, 0xff, op->dummy.nbytes); + + /* 1st transfer: opcode + address + dummy cycles */ + flag = SPI_XFER_BEGIN; + /* Make sure to set END bit if no tx or rx data messages follow */ + if (!tx_buf && !rx_buf) + flag |= SPI_XFER_END; + + ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag); + if (ret) + return ret; + + /* 2nd transfer: rx or tx data path */ + if (tx_buf || rx_buf) { + ret = spi_xfer(slave, op->data.nbytes * 8, tx_buf, + rx_buf, SPI_XFER_END); + if (ret) + return ret; + } + + spi_release_bus(slave); + + for (i = 0; i < pos; i++) + debug("%02x ", op_buf[i]); + debug("| [%dB %s] ", + tx_buf || rx_buf ? op->data.nbytes : 0, + tx_buf || rx_buf ? (tx_buf ? "out" : "in") : "-"); + for (i = 0; i < op->data.nbytes; i++) + debug("%02x ", tx_buf ? tx_buf[i] : rx_buf[i]); + debug("[ret %d]\n", ret); + + free(op_buf); + + if (ret < 0) + return ret; +#endif /* __UBOOT__ */ + + return 0; +} +EXPORT_SYMBOL_GPL(spi_mem_exec_op); + +/** + * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to + * match controller limitations + * @slave: the SPI device + * @op: the operation to adjust + * + * Some controllers have FIFO limitations and must split a data transfer + * operation into multiple ones, others require a specific alignment for + * optimized accesses. This function allows SPI mem drivers to split a single + * operation into multiple sub-operations when required. + * + * Return: a negative error code if the controller can't properly adjust @op, + * 0 otherwise. Note that @op->data.nbytes will be updated if @op + * can't be handled in a single step. + */ +int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op) +{ + struct udevice *bus = slave->dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->mem_ops && ops->mem_ops->adjust_op_size) + return ops->mem_ops->adjust_op_size(slave, op); + + return 0; +} +EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); + +#ifndef __UBOOT__ +static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) +{ + return container_of(drv, struct spi_mem_driver, spidrv.driver); +} + +static int spi_mem_probe(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem; + + mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mem->spi = spi; + spi_set_drvdata(spi, mem); + + return memdrv->probe(mem); +} + +static int spi_mem_remove(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem = spi_get_drvdata(spi); + + if (memdrv->remove) + return memdrv->remove(mem); + + return 0; +} + +static void spi_mem_shutdown(struct spi_device *spi) +{ + struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_mem *mem = spi_get_drvdata(spi); + + if (memdrv->shutdown) + memdrv->shutdown(mem); +} + +/** + * spi_mem_driver_register_with_owner() - Register a SPI memory driver + * @memdrv: the SPI memory driver to register + * @owner: the owner of this driver + * + * Registers a SPI memory driver. + * + * Return: 0 in case of success, a negative error core otherwise. + */ + +int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv, + struct module *owner) +{ + memdrv->spidrv.probe = spi_mem_probe; + memdrv->spidrv.remove = spi_mem_remove; + memdrv->spidrv.shutdown = spi_mem_shutdown; + + return __spi_register_driver(owner, &memdrv->spidrv); +} +EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner); + +/** + * spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver + * @memdrv: the SPI memory driver to unregister + * + * Unregisters a SPI memory driver. + */ +void spi_mem_driver_unregister(struct spi_mem_driver *memdrv) +{ + spi_unregister_driver(&memdrv->spidrv); +} +EXPORT_SYMBOL_GPL(spi_mem_driver_unregister); +#endif /* __UBOOT__ */ diff --git a/include/spi-mem.h b/include/spi-mem.h new file mode 100644 index 0000000000..36814efa86 --- /dev/null +++ b/include/spi-mem.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Exceet Electronics GmbH + * Copyright (C) 2018 Bootlin + * + * Author: + * Peter Pan + * Boris Brezillon + */ + +#ifndef __UBOOT_SPI_MEM_H +#define __UBOOT_SPI_MEM_H + +#include +#include +#include +#include + +#define SPI_MEM_OP_CMD(__opcode, __buswidth) \ + { \ + .buswidth = __buswidth, \ + .opcode = __opcode, \ + } + +#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .val = __val, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_ADDR { } + +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DUMMY { } + +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .nbytes = __nbytes, \ + .buf.in = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_OUT, \ + .nbytes = __nbytes, \ + .buf.out = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DATA { } + +/** + * enum spi_mem_data_dir - describes the direction of a SPI memory data + * transfer from the controller perspective + * @SPI_MEM_DATA_IN: data coming from the SPI memory + * @SPI_MEM_DATA_OUT: data sent the SPI memory + */ +enum spi_mem_data_dir { + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, +}; + +/** + * struct spi_mem_op - describes a SPI memory operation + * @cmd.buswidth: number of IO lines used to transmit the command + * @cmd.opcode: operation opcode + * @addr.nbytes: number of address bytes to send. Can be zero if the operation + * does not need to send an address + * @addr.buswidth: number of IO lines used to transmit the address cycles + * @addr.val: address value. This value is always sent MSB first on the bus. + * Note that only @addr.nbytes are taken into account in this + * address value, so users should make sure the value fits in the + * assigned number of bytes. + * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes + * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes + * @data.buswidth: number of IO lanes used to send/receive the data + * @data.dir: direction of the transfer + * @data.buf.in: input buffer + * @data.buf.out: output buffer + */ +struct spi_mem_op { + struct { + u8 buswidth; + u8 opcode; + } cmd; + + struct { + u8 nbytes; + u8 buswidth; + u64 val; + } addr; + + struct { + u8 nbytes; + u8 buswidth; + } dummy; + + struct { + u8 buswidth; + enum spi_mem_data_dir dir; + unsigned int nbytes; + /* buf.{in,out} must be DMA-able. */ + union { + void *in; + const void *out; + } buf; + } data; +}; + +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \ + { \ + .cmd = __cmd, \ + .addr = __addr, \ + .dummy = __dummy, \ + .data = __data, \ + } + +#ifndef __UBOOT__ +/** + * struct spi_mem - describes a SPI memory device + * @spi: the underlying SPI device + * @drvpriv: spi_mem_driver private data + * + * Extra information that describe the SPI memory device and may be needed by + * the controller to properly handle this device should be placed here. + * + * One example would be the device size since some controller expose their SPI + * mem devices through a io-mapped region. + */ +struct spi_mem { + struct udevice *dev; + void *drvpriv; +}; + +/** + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem + * device + * @mem: memory device + * @data: data to attach to the memory device + */ +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data) +{ + mem->drvpriv = data; +} + +/** + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem + * device + * @mem: memory device + * + * Return: the data attached to the mem device. + */ +static inline void *spi_mem_get_drvdata(struct spi_mem *mem) +{ + return mem->drvpriv; +} +#endif /* __UBOOT__ */ + +/** + * struct spi_controller_mem_ops - SPI memory operations + * @adjust_op_size: shrink the data xfer of an operation to match controller's + * limitations (can be alignment of max RX/TX size + * limitations) + * @supports_op: check if an operation is supported by the controller + * @exec_op: execute a SPI memory operation + * + * This interface should be implemented by SPI controllers providing an + * high-level interface to execute SPI memory operation, which is usually the + * case for QSPI controllers. + */ +struct spi_controller_mem_ops { + int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op); + bool (*supports_op)(struct spi_slave *slave, + const struct spi_mem_op *op); + int (*exec_op)(struct spi_slave *slave, + const struct spi_mem_op *op); +}; + +#ifndef __UBOOT__ +/** + * struct spi_mem_driver - SPI memory driver + * @spidrv: inherit from a SPI driver + * @probe: probe a SPI memory. Usually where detection/initialization takes + * place + * @remove: remove a SPI memory + * @shutdown: take appropriate action when the system is shutdown + * + * This is just a thin wrapper around a spi_driver. The core takes care of + * allocating the spi_mem object and forwarding the probe/remove/shutdown + * request to the spi_mem_driver. The reason we use this wrapper is because + * we might have to stuff more information into the spi_mem struct to let + * SPI controllers know more about the SPI memory they interact with, and + * having this intermediate layer allows us to do that without adding more + * useless fields to the spi_device object. + */ +struct spi_mem_driver { + struct spi_driver spidrv; + int (*probe)(struct spi_mem *mem); + int (*remove)(struct spi_mem *mem); + void (*shutdown)(struct spi_mem *mem); +}; + +#if IS_ENABLED(CONFIG_SPI_MEM) +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); + +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); +#else +static inline int +spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ + return -ENOTSUPP; +} + +static inline void +spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ +} +#endif /* CONFIG_SPI_MEM */ +#endif /* __UBOOT__ */ + +int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op); + +bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op); + +int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op); + +#ifndef __UBOOT__ +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, + struct module *owner); + +void spi_mem_driver_unregister(struct spi_mem_driver *drv); + +#define spi_mem_driver_register(__drv) \ + spi_mem_driver_register_with_owner(__drv, THIS_MODULE) + +#define module_spi_mem_driver(__drv) \ + module_driver(__drv, spi_mem_driver_register, \ + spi_mem_driver_unregister) +#endif + +#endif /* __LINUX_SPI_MEM_H */ diff --git a/include/spi.h b/include/spi.h index 9754c53aa1..938627bc01 100644 --- a/include/spi.h +++ b/include/spi.h @@ -9,6 +9,8 @@ #ifndef _SPI_H_ #define _SPI_H_ +#include + /* SPI mode flags */ #define SPI_CPHA BIT(0) /* clock phase */ #define SPI_CPOL BIT(1) /* clock polarity */ @@ -402,6 +404,15 @@ struct dm_spi_ops { int (*xfer)(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags); + /** + * Optimized handlers for SPI memory-like operations. + * + * Optimized/dedicated operations for interactions with SPI memory. This + * field is optional and should only be implemented if the controller + * has native support for memory like operations. + */ + const struct spi_controller_mem_ops *mem_ops; + /** * Set transfer speed. * This sets a new speed to be applied for next spi_xfer(). -- cgit v1.2.3 From 0a6d6bae03864938f073cc114992c40f2338a155 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 16 Aug 2018 17:30:12 +0200 Subject: mtd: nand: Add core infrastructure to support SPI NANDs Add a SPI NAND framework based on the generic NAND framework and the spi-mem infrastructure. In its current state, this framework supports the following features: - single/dual/quad IO modes - on-die ECC Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/Kconfig | 2 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/spi/Kconfig | 7 + drivers/mtd/nand/spi/Makefile | 4 + drivers/mtd/nand/spi/core.c | 1235 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 427 ++++++++++++++ 6 files changed, 1676 insertions(+) create mode 100644 drivers/mtd/nand/spi/Kconfig create mode 100644 drivers/mtd/nand/spi/Makefile create mode 100644 drivers/mtd/nand/spi/core.c create mode 100644 include/linux/mtd/spinand.h diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1c1a1f487e..78ae04bdcb 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -2,3 +2,5 @@ config MTD_NAND_CORE tristate source "drivers/mtd/nand/raw/Kconfig" + +source "drivers/mtd/nand/spi/Kconfig" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index cd492dbc14..a358bc680e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,3 +2,4 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o +obj-$(CONFIG_MTD_SPI_NAND) += spi/ diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig new file mode 100644 index 0000000000..2197cb531f --- /dev/null +++ b/drivers/mtd/nand/spi/Kconfig @@ -0,0 +1,7 @@ +menuconfig MTD_SPI_NAND + bool "SPI NAND device Support" + depends on MTD && DM_SPI + select MTD_NAND_CORE + select SPI_MEM + help + This is the framework for the SPI NAND device drivers. diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile new file mode 100644 index 0000000000..f0c6e69d2e --- /dev/null +++ b/drivers/mtd/nand/spi/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +spinand-objs := core.o +obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c new file mode 100644 index 0000000000..08f853ae11 --- /dev/null +++ b/drivers/mtd/nand/spi/core.c @@ -0,0 +1,1235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + * Boris Brezillon + */ + +#define pr_fmt(fmt) "spi-nand: " fmt + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +/* SPI NAND index visible in MTD names */ +static int spi_nand_idx; + +static void spinand_cache_op_adjust_colum(struct spinand_device *spinand, + const struct nand_page_io_req *req, + u16 *column) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int shift; + + if (nand->memorg.planes_per_lun < 2) + return; + + /* The plane number is passed in MSB just above the column address */ + shift = fls(nand->memorg.pagesize); + *column |= req->pos.plane << shift; +} + +static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) +{ + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg, + spinand->scratchbuf); + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + *val = *spinand->scratchbuf; + return 0; +} + +static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) +{ + struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg, + spinand->scratchbuf); + + *spinand->scratchbuf = val; + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_read_status(struct spinand_device *spinand, u8 *status) +{ + return spinand_read_reg_op(spinand, REG_STATUS, status); +} + +static int spinand_get_cfg(struct spinand_device *spinand, u8 *cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + *cfg = spinand->cfg_cache[spinand->cur_target]; + return 0; +} + +static int spinand_set_cfg(struct spinand_device *spinand, u8 cfg) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(spinand->cur_target < 0 || + spinand->cur_target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cfg_cache[spinand->cur_target] == cfg) + return 0; + + ret = spinand_write_reg_op(spinand, REG_CFG, cfg); + if (ret) + return ret; + + spinand->cfg_cache[spinand->cur_target] = cfg; + return 0; +} + +/** + * spinand_upd_cfg() - Update the configuration register + * @spinand: the spinand device + * @mask: the mask encoding the bits to update in the config reg + * @val: the new value to apply + * + * Update the configuration register. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val) +{ + int ret; + u8 cfg; + + ret = spinand_get_cfg(spinand, &cfg); + if (ret) + return ret; + + cfg &= ~mask; + cfg |= val; + + return spinand_set_cfg(spinand, cfg); +} + +/** + * spinand_select_target() - Select a specific NAND target/die + * @spinand: the spinand device + * @target: the target/die to select + * + * Select a new target/die. If chip only has one die, this function is a NOOP. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_select_target(struct spinand_device *spinand, unsigned int target) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + if (WARN_ON(target >= nand->memorg.ntargets)) + return -EINVAL; + + if (spinand->cur_target == target) + return 0; + + if (nand->memorg.ntargets == 1) { + spinand->cur_target = target; + return 0; + } + + ret = spinand->select_target(spinand, target); + if (ret) + return ret; + + spinand->cur_target = target; + return 0; +} + +static int spinand_init_cfg_cache(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + struct udevice *dev = spinand->slave->dev; + unsigned int target; + int ret; + + spinand->cfg_cache = devm_kzalloc(dev, + sizeof(*spinand->cfg_cache) * + nand->memorg.ntargets, + GFP_KERNEL); + if (!spinand->cfg_cache) + return -ENOMEM; + + for (target = 0; target < nand->memorg.ntargets; target++) { + ret = spinand_select_target(spinand, target); + if (ret) + return ret; + + /* + * We use spinand_read_reg_op() instead of spinand_get_cfg() + * here to bypass the config cache. + */ + ret = spinand_read_reg_op(spinand, REG_CFG, + &spinand->cfg_cache[target]); + if (ret) + return ret; + } + + return 0; +} + +static int spinand_init_quad_enable(struct spinand_device *spinand) +{ + bool enable = false; + + if (!(spinand->flags & SPINAND_HAS_QE_BIT)) + return 0; + + if (spinand->op_templates.read_cache->data.buswidth == 4 || + spinand->op_templates.write_cache->data.buswidth == 4 || + spinand->op_templates.update_cache->data.buswidth == 4) + enable = true; + + return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, + enable ? CFG_QUAD_ENABLE : 0); +} + +static int spinand_ecc_enable(struct spinand_device *spinand, + bool enable) +{ + return spinand_upd_cfg(spinand, CFG_ECC_ENABLE, + enable ? CFG_ECC_ENABLE : 0); +} + +static int spinand_write_enable_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_load_page_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PAGE_READ_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_read_from_cache_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct spi_mem_op op = *spinand->op_templates.read_cache; + struct nand_device *nand = spinand_to_nand(spinand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_page_io_req adjreq = *req; + unsigned int nbytes = 0; + void *buf = NULL; + u16 column = 0; + int ret; + + if (req->datalen) { + adjreq.datalen = nanddev_page_size(nand); + adjreq.dataoffs = 0; + adjreq.databuf.in = spinand->databuf; + buf = spinand->databuf; + nbytes = adjreq.datalen; + } + + if (req->ooblen) { + adjreq.ooblen = nanddev_per_page_oobsize(nand); + adjreq.ooboffs = 0; + adjreq.oobbuf.in = spinand->oobbuf; + nbytes += nanddev_per_page_oobsize(nand); + if (!buf) { + buf = spinand->oobbuf; + column = nanddev_page_size(nand); + } + } + + spinand_cache_op_adjust_colum(spinand, &adjreq, &column); + op.addr.val = column; + + /* + * Some controllers are limited in term of max RX data size. In this + * case, just repeat the READ_CACHE operation after updating the + * column. + */ + while (nbytes) { + op.data.buf.in = buf; + op.data.nbytes = nbytes; + ret = spi_mem_adjust_op_size(spinand->slave, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + buf += op.data.nbytes; + nbytes -= op.data.nbytes; + op.addr.val += op.data.nbytes; + } + + if (req->datalen) + memcpy(req->databuf.in, spinand->databuf + req->dataoffs, + req->datalen); + + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_get_databytes(mtd, req->oobbuf.in, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs, + req->ooblen); + } + + return 0; +} + +static int spinand_write_to_cache_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct spi_mem_op op = *spinand->op_templates.write_cache; + struct nand_device *nand = spinand_to_nand(spinand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_page_io_req adjreq = *req; + unsigned int nbytes = 0; + void *buf = NULL; + u16 column = 0; + int ret; + + memset(spinand->databuf, 0xff, + nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand)); + + if (req->datalen) { + memcpy(spinand->databuf + req->dataoffs, req->databuf.out, + req->datalen); + adjreq.dataoffs = 0; + adjreq.datalen = nanddev_page_size(nand); + adjreq.databuf.out = spinand->databuf; + nbytes = adjreq.datalen; + buf = spinand->databuf; + } + + if (req->ooblen) { + if (req->mode == MTD_OPS_AUTO_OOB) + mtd_ooblayout_set_databytes(mtd, req->oobbuf.out, + spinand->oobbuf, + req->ooboffs, + req->ooblen); + else + memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out, + req->ooblen); + + adjreq.ooblen = nanddev_per_page_oobsize(nand); + adjreq.ooboffs = 0; + nbytes += nanddev_per_page_oobsize(nand); + if (!buf) { + buf = spinand->oobbuf; + column = nanddev_page_size(nand); + } + } + + spinand_cache_op_adjust_colum(spinand, &adjreq, &column); + + op = *spinand->op_templates.write_cache; + op.addr.val = column; + + /* + * Some controllers are limited in term of max TX data size. In this + * case, split the operation into one LOAD CACHE and one or more + * LOAD RANDOM CACHE. + */ + while (nbytes) { + op.data.buf.out = buf; + op.data.nbytes = nbytes; + + ret = spi_mem_adjust_op_size(spinand->slave, &op); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + buf += op.data.nbytes; + nbytes -= op.data.nbytes; + op.addr.val += op.data.nbytes; + + /* + * We need to use the RANDOM LOAD CACHE operation if there's + * more than one iteration, because the LOAD operation resets + * the cache to 0xff. + */ + if (nbytes) { + column = op.addr.val; + op = *spinand->op_templates.update_cache; + op.addr.val = column; + } + } + + return 0; +} + +static int spinand_program_op(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int row = nanddev_pos_to_row(nand, &req->pos); + struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_erase_op(struct spinand_device *spinand, + const struct nand_pos *pos) +{ + struct nand_device *nand = &spinand->base; + unsigned int row = nanddev_pos_to_row(nand, pos); + struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int spinand_wait(struct spinand_device *spinand, u8 *s) +{ + unsigned long start, stop; + u8 status; + int ret; + + start = get_timer(0); + stop = 400; + do { + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + + if (!(status & STATUS_BUSY)) + goto out; + } while (get_timer(start) < stop); + + /* + * Extra read, just in case the STATUS_READY bit has changed + * since our last check + */ + ret = spinand_read_status(spinand, &status); + if (ret) + return ret; + +out: + if (s) + *s = status; + + return status & STATUS_BUSY ? -ETIMEDOUT : 0; +} + +static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +{ + struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, + SPINAND_MAX_ID_LEN); + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (!ret) + memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + + return ret; +} + +static int spinand_reset_op(struct spinand_device *spinand) +{ + struct spi_mem_op op = SPINAND_RESET_OP; + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + return spinand_wait(spinand, NULL); +} + +static int spinand_lock_block(struct spinand_device *spinand, u8 lock) +{ + return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); +} + +static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (spinand->eccinfo.get_status) + return spinand->eccinfo.get_status(spinand, status); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * We have no way to know exactly how many bitflips have been + * fixed, so let's return the maximum possible value so that + * wear-leveling layers move the data immediately. + */ + return nand->eccreq.strength; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int spinand_read_page(struct spinand_device *spinand, + const struct nand_page_io_req *req, + bool ecc_enabled) +{ + u8 status; + int ret; + + ret = spinand_load_page_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (ret < 0) + return ret; + + ret = spinand_read_from_cache_op(spinand, req); + if (ret) + return ret; + + if (!ecc_enabled) + return 0; + + return spinand_check_ecc_status(spinand, status); +} + +static int spinand_write_page(struct spinand_device *spinand, + const struct nand_page_io_req *req) +{ + u8 status; + int ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_write_to_cache_op(spinand, req); + if (ret) + return ret; + + ret = spinand_program_op(spinand, req); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_PROG_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int max_bitflips = 0; + struct nand_io_iter iter; + bool enable_ecc = false; + bool ecc_failed = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) + enable_ecc = true; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + + nanddev_io_for_each_page(nand, from, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_read_page(spinand, &iter.req, enable_ecc); + if (ret < 0 && ret != -EBADMSG) + break; + + if (ret == -EBADMSG) { + ecc_failed = true; + mtd->ecc_stats.failed++; + ret = 0; + } else { + mtd->ecc_stats.corrected += ret; + max_bitflips = max_t(unsigned int, max_bitflips, ret); + } + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + if (ecc_failed && !ret) + ret = -EBADMSG; + + return ret ? ret : max_bitflips; +} + +static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_io_iter iter; + bool enable_ecc = false; + int ret = 0; + + if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) + enable_ecc = true; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + + nanddev_io_for_each_page(nand, to, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + break; + + ret = spinand_ecc_enable(spinand, enable_ecc); + if (ret) + break; + + ret = spinand_write_page(spinand, &iter.req); + if (ret) + break; + + ops->retlen += iter.req.datalen; + ops->oobretlen += iter.req.ooblen; + } + +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooblen = 2, + .ooboffs = 0, + .oobbuf.in = spinand->oobbuf, + .mode = MTD_OPS_RAW, + }; + int ret; + + memset(spinand->oobbuf, 0, 2); + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_read_page(spinand, &req, false); + if (ret) + return ret; + + if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff) + return true; + + return false; +} + +static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); +#ifndef __UBOOT__ + struct spinand_device *spinand = nand_to_spinand(nand); +#endif + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_isbad(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + return ret; +} + +static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_page_io_req req = { + .pos = *pos, + .ooboffs = 0, + .ooblen = 2, + .oobbuf.out = spinand->oobbuf, + }; + int ret; + + /* Erase block before marking it bad. */ + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_erase_op(spinand, pos); + if (ret) + return ret; + + memset(spinand->oobbuf, 0, 2); + return spinand_write_page(spinand, &req); +} + +static int spinand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); +#ifndef __UBOOT__ + struct spinand_device *spinand = nand_to_spinand(nand); +#endif + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_markbad(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + return ret; +} + +static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + u8 status; + int ret; + + ret = spinand_select_target(spinand, pos->target); + if (ret) + return ret; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spinand_erase_op(spinand, pos); + if (ret) + return ret; + + ret = spinand_wait(spinand, &status); + if (!ret && (status & STATUS_ERASE_FAILED)) + ret = -EIO; + + return ret; +} + +static int spinand_mtd_erase(struct mtd_info *mtd, + struct erase_info *einfo) +{ +#ifndef __UBOOT__ + struct spinand_device *spinand = mtd_to_spinand(mtd); +#endif + int ret; + +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_mtd_erase(mtd, einfo); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) +{ +#ifndef __UBOOT__ + struct spinand_device *spinand = mtd_to_spinand(mtd); +#endif + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_pos pos; + int ret; + + nanddev_offs_to_pos(nand, offs, &pos); +#ifndef __UBOOT__ + mutex_lock(&spinand->lock); +#endif + ret = nanddev_isreserved(nand, &pos); +#ifndef __UBOOT__ + mutex_unlock(&spinand->lock); +#endif + + return ret; +} + +const struct spi_mem_op * +spinand_find_supported_op(struct spinand_device *spinand, + const struct spi_mem_op *ops, + unsigned int nops) +{ + unsigned int i; + + for (i = 0; i < nops; i++) { + if (spi_mem_supports_op(spinand->slave, &ops[i])) + return &ops[i]; + } + + return NULL; +} + +static const struct nand_ops spinand_ops = { + .erase = spinand_erase, + .markbad = spinand_markbad, + .isbad = spinand_isbad, +}; + +static int spinand_manufacturer_detect(struct spinand_device *spinand) +{ + return -ENOTSUPP; +} + +static int spinand_manufacturer_init(struct spinand_device *spinand) +{ + if (spinand->manufacturer->ops->init) + return spinand->manufacturer->ops->init(spinand); + + return 0; +} + +static void spinand_manufacturer_cleanup(struct spinand_device *spinand) +{ + /* Release manufacturer private data */ + if (spinand->manufacturer->ops->cleanup) + return spinand->manufacturer->ops->cleanup(spinand); +} + +static const struct spi_mem_op * +spinand_select_op_variant(struct spinand_device *spinand, + const struct spinand_op_variants *variants) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < variants->nops; i++) { + struct spi_mem_op op = variants->ops[i]; + unsigned int nbytes; + int ret; + + nbytes = nanddev_per_page_oobsize(nand) + + nanddev_page_size(nand); + + while (nbytes) { + op.data.nbytes = nbytes; + ret = spi_mem_adjust_op_size(spinand->slave, &op); + if (ret) + break; + + if (!spi_mem_supports_op(spinand->slave, &op)) + break; + + nbytes -= op.data.nbytes; + } + + if (!nbytes) + return &variants->ops[i]; + } + + return NULL; +} + +/** + * spinand_match_and_init() - Try to find a match between a device ID and an + * entry in a spinand_info table + * @spinand: SPI NAND object + * @table: SPI NAND device description table + * @table_size: size of the device description table + * + * Should be used by SPI NAND manufacturer drivers when they want to find a + * match between a device ID retrieved through the READ_ID command and an + * entry in the SPI NAND description table. If a match is found, the spinand + * object will be initialized with information provided by the matching + * spinand_info entry. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, + unsigned int table_size, u8 devid) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + for (i = 0; i < table_size; i++) { + const struct spinand_info *info = &table[i]; + const struct spi_mem_op *op; + + if (devid != info->devid) + continue; + + nand->memorg = table[i].memorg; + nand->eccreq = table[i].eccreq; + spinand->eccinfo = table[i].eccinfo; + spinand->flags = table[i].flags; + spinand->select_target = table[i].select_target; + + op = spinand_select_op_variant(spinand, + info->op_variants.read_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.read_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.write_cache); + if (!op) + return -ENOTSUPP; + + spinand->op_templates.write_cache = op; + + op = spinand_select_op_variant(spinand, + info->op_variants.update_cache); + spinand->op_templates.update_cache = op; + + return 0; + } + + return -ENOTSUPP; +} + +static int spinand_detect(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + + ret = spinand_reset_op(spinand); + if (ret) + return ret; + + ret = spinand_read_id_op(spinand, spinand->id.data); + if (ret) + return ret; + + spinand->id.len = SPINAND_MAX_ID_LEN; + + ret = spinand_manufacturer_detect(spinand); + if (ret) { + dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, + spinand->id.data); + return ret; + } + + if (nand->memorg.ntargets > 1 && !spinand->select_target) { + dev_err(dev, + "SPI NANDs with more than one die must implement ->select_target()\n"); + return -EINVAL; + } + + dev_info(spinand->slave->dev, + "%s SPI NAND was found.\n", spinand->manufacturer->name); + dev_info(spinand->slave->dev, + "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n", + nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10, + nanddev_page_size(nand), nanddev_per_page_oobsize(nand)); + + return 0; +} + +static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { + .ecc = spinand_noecc_ooblayout_ecc, + .free = spinand_noecc_ooblayout_free, +}; + +static int spinand_init(struct spinand_device *spinand) +{ + struct mtd_info *mtd = spinand_to_mtd(spinand); + struct nand_device *nand = mtd_to_nanddev(mtd); + int ret, i; + + /* + * We need a scratch buffer because the spi_mem interface requires that + * buf passed in spi_mem_op->data.buf be DMA-able. + */ + spinand->scratchbuf = kzalloc(SPINAND_MAX_ID_LEN, GFP_KERNEL); + if (!spinand->scratchbuf) + return -ENOMEM; + + ret = spinand_detect(spinand); + if (ret) + goto err_free_bufs; + + /* + * Use kzalloc() instead of devm_kzalloc() here, because some drivers + * may use this buffer for DMA access. + * Memory allocated by devm_ does not guarantee DMA-safe alignment. + */ + spinand->databuf = kzalloc(nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand), + GFP_KERNEL); + if (!spinand->databuf) { + ret = -ENOMEM; + goto err_free_bufs; + } + + spinand->oobbuf = spinand->databuf + nanddev_page_size(nand); + + ret = spinand_init_cfg_cache(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_init_quad_enable(spinand); + if (ret) + goto err_free_bufs; + + ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0); + if (ret) + goto err_free_bufs; + + ret = spinand_manufacturer_init(spinand); + if (ret) { + dev_err(dev, + "Failed to initialize the SPI NAND chip (err = %d)\n", + ret); + goto err_free_bufs; + } + + /* After power up, all blocks are locked, so unlock them here. */ + for (i = 0; i < nand->memorg.ntargets; i++) { + ret = spinand_select_target(spinand, i); + if (ret) + goto err_free_bufs; + + ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); + if (ret) + goto err_free_bufs; + } + + ret = nanddev_init(nand, &spinand_ops, THIS_MODULE); + if (ret) + goto err_manuf_cleanup; + + /* + * Right now, we don't support ECC, so let the whole oob + * area is available for user. + */ + mtd->_read_oob = spinand_mtd_read; + mtd->_write_oob = spinand_mtd_write; + mtd->_block_isbad = spinand_mtd_block_isbad; + mtd->_block_markbad = spinand_mtd_block_markbad; + mtd->_block_isreserved = spinand_mtd_block_isreserved; + mtd->_erase = spinand_mtd_erase; + + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); + + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_nanddev; + + mtd->oobavail = ret; + + return 0; + +err_cleanup_nanddev: + nanddev_cleanup(nand); + +err_manuf_cleanup: + spinand_manufacturer_cleanup(spinand); + +err_free_bufs: + kfree(spinand->databuf); + kfree(spinand->scratchbuf); + return ret; +} + +static void spinand_cleanup(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + nanddev_cleanup(nand); + spinand_manufacturer_cleanup(spinand); + kfree(spinand->databuf); + kfree(spinand->scratchbuf); +} + +static int spinand_probe(struct udevice *dev) +{ + struct spinand_device *spinand = dev_get_priv(dev); + struct spi_slave *slave = dev_get_parent_priv(dev); + struct mtd_info *mtd = dev_get_uclass_priv(dev); + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + +#ifndef __UBOOT__ + spinand = devm_kzalloc(&mem->spi->dev, sizeof(*spinand), + GFP_KERNEL); + if (!spinand) + return -ENOMEM; + + spinand->spimem = mem; + spi_mem_set_drvdata(mem, spinand); + spinand_set_of_node(spinand, mem->spi->dev.of_node); + mutex_init(&spinand->lock); + + mtd = spinand_to_mtd(spinand); + mtd->dev.parent = &mem->spi->dev; +#else + nand->mtd = mtd; + mtd->priv = nand; + mtd->dev = dev; + mtd->name = malloc(20); + if (!mtd->name) + return -ENOMEM; + sprintf(mtd->name, "spi-nand%d", spi_nand_idx++); + spinand->slave = slave; + spinand_set_of_node(spinand, dev->node.np); +#endif + + ret = spinand_init(spinand); + if (ret) + return ret; + +#ifndef __UBOOT__ + ret = mtd_device_register(mtd, NULL, 0); +#else + ret = add_mtd_device(mtd); +#endif + if (ret) + goto err_spinand_cleanup; + + return 0; + +err_spinand_cleanup: + spinand_cleanup(spinand); + + return ret; +} + +#ifndef __UBOOT__ +static int spinand_remove(struct udevice *slave) +{ + struct spinand_device *spinand; + struct mtd_info *mtd; + int ret; + + spinand = spi_mem_get_drvdata(slave); + mtd = spinand_to_mtd(spinand); + free(mtd->name); + + ret = mtd_device_unregister(mtd); + if (ret) + return ret; + + spinand_cleanup(spinand); + + return 0; +} + +static const struct spi_device_id spinand_ids[] = { + { .name = "spi-nand" }, + { /* sentinel */ }, +}; + +#ifdef CONFIG_OF +static const struct of_device_id spinand_of_ids[] = { + { .compatible = "spi-nand" }, + { /* sentinel */ }, +}; +#endif + +static struct spi_mem_driver spinand_drv = { + .spidrv = { + .id_table = spinand_ids, + .driver = { + .name = "spi-nand", + .of_match_table = of_match_ptr(spinand_of_ids), + }, + }, + .probe = spinand_probe, + .remove = spinand_remove, +}; +module_spi_mem_driver(spinand_drv); + +MODULE_DESCRIPTION("SPI NAND framework"); +MODULE_AUTHOR("Peter Pan"); +MODULE_LICENSE("GPL v2"); +#endif /* __UBOOT__ */ + +static const struct udevice_id spinand_ids[] = { + { .compatible = "spi-nand" }, + { /* sentinel */ }, +}; + +U_BOOT_DRIVER(spinand) = { + .name = "spi_nand", + .id = UCLASS_MTD, + .of_match = spinand_ids, + .priv_auto_alloc_size = sizeof(struct spinand_device), + .probe = spinand_probe, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h new file mode 100644 index 0000000000..ad59fc01ef --- /dev/null +++ b/include/linux/mtd/spinand.h @@ -0,0 +1,427 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ +#ifndef __LINUX_MTD_SPINAND_H +#define __LINUX_MTD_SPINAND_H + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +/** + * Standard SPI NAND flash operations + */ + +#define SPINAND_RESET_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_WR_EN_DIS_OP(enable) \ + SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_READID_OP(ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_SET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, valptr, 1)) + +#define SPINAND_GET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \ + SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_IN(1, valptr, 1)) + +#define SPINAND_BLK_ERASE_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 2), \ + SPI_MEM_OP_DUMMY(ndummy, 2), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 4), \ + SPI_MEM_OP_DUMMY(ndummy, 4), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + +#define SPINAND_PROG_EXEC_OP(addr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \ + SPI_MEM_OP_ADDR(3, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PROG_LOAD(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 1)) + +#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(len, buf, 4)) + +/** + * Standard SPI NAND flash commands + */ +#define SPINAND_CMD_PROG_LOAD_X4 0x32 +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34 + +/* feature register */ +#define REG_BLOCK_LOCK 0xa0 +#define BL_ALL_UNLOCKED 0x00 + +/* configuration register */ +#define REG_CFG 0xb0 +#define CFG_OTP_ENABLE BIT(6) +#define CFG_ECC_ENABLE BIT(4) +#define CFG_QUAD_ENABLE BIT(0) + +/* status register */ +#define REG_STATUS 0xc0 +#define STATUS_BUSY BIT(0) +#define STATUS_ERASE_FAILED BIT(2) +#define STATUS_PROG_FAILED BIT(3) +#define STATUS_ECC_MASK GENMASK(5, 4) +#define STATUS_ECC_NO_BITFLIPS (0 << 4) +#define STATUS_ECC_HAS_BITFLIPS (1 << 4) +#define STATUS_ECC_UNCOR_ERROR (2 << 4) + +struct spinand_op; +struct spinand_device; + +#define SPINAND_MAX_ID_LEN 4 + +/** + * struct spinand_id - SPI NAND id structure + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length + * + * struct_spinand_id->data contains all bytes returned after a READ_ID command, + * including dummy bytes if the chip does not emit ID bytes right after the + * READ_ID command. The responsibility to extract real ID bytes is left to + * struct_manufacurer_ops->detect(). + */ +struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; +}; + +/** + * struct manufacurer_ops - SPI NAND manufacturer specific operations + * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed + * the core calls the struct_manufacurer_ops->detect() hook of each + * registered manufacturer until one of them return 1. Note that + * the first thing to check in this hook is that the manufacturer ID + * in struct_spinand_device->id matches the manufacturer whose + * ->detect() hook has been called. Should return 1 if there's a + * match, 0 if the manufacturer ID does not match and a negative + * error code otherwise. When true is returned, the core assumes + * that properties of the NAND chip (spinand->base.memorg and + * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that + * NAND chips coming from this vendor can be detected and initialized properly. + */ +struct spinand_manufacturer_ops { + int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); +}; + +/** + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name + * @ops: manufacturer operations + */ +struct spinand_manufacturer { + u8 id; + char *name; + const struct spinand_manufacturer_ops *ops; +}; + +/** + * struct spinand_op_variants - SPI NAND operation variants + * @ops: the list of variants for a given operation + * @nops: the number of variants + * + * Some operations like read-from-cache/write-to-cache have several variants + * depending on the number of IO lines you use to transfer data or address + * cycles. This structure is a way to describe the different variants supported + * by a chip and let the core pick the best one based on the SPI mem controller + * capabilities. + */ +struct spinand_op_variants { + const struct spi_mem_op *ops; + unsigned int nops; +}; + +#define SPINAND_OP_VARIANTS(name, ...) \ + const struct spinand_op_variants name = { \ + .ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \ + .nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \ + sizeof(struct spi_mem_op), \ + } + +/** + * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND + * chip + * @get_status: get the ECC status. Should return a positive number encoding + * the number of corrected bitflips if correction was possible or + * -EBADMSG if there are uncorrectable errors. I can also return + * other negative error codes if the error is not caused by + * uncorrectable bitflips + * @ooblayout: the OOB layout used by the on-die ECC implementation + */ +struct spinand_ecc_info { + int (*get_status)(struct spinand_device *spinand, u8 status); + const struct mtd_ooblayout_ops *ooblayout; +}; + +#define SPINAND_HAS_QE_BIT BIT(0) + +/** + * struct spinand_info - Structure used to describe SPI NAND chips + * @model: model name + * @devid: device ID + * @flags: OR-ing of the SPINAND_XXX flags + * @memorg: memory organization + * @eccreq: ECC requirements + * @eccinfo: on-die ECC info + * @op_variants: operations variants + * @op_variants.read_cache: variants of the read-cache operation + * @op_variants.write_cache: variants of the write-cache operation + * @op_variants.update_cache: variants of the update-cache operation + * @select_target: function used to select a target/die. Required only for + * multi-die chips + * + * Each SPI NAND manufacturer driver should have a spinand_info table + * describing all the chips supported by the driver. + */ +struct spinand_info { + const char *model; + u8 devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct spinand_ecc_info eccinfo; + struct { + const struct spinand_op_variants *read_cache; + const struct spinand_op_variants *write_cache; + const struct spinand_op_variants *update_cache; + } op_variants; + int (*select_target)(struct spinand_device *spinand, + unsigned int target); +}; + +#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ + .write_cache = __write, \ + .update_cache = __update, \ + } + +#define SPINAND_ECCINFO(__ooblayout, __get_status) \ + .eccinfo = { \ + .ooblayout = __ooblayout, \ + .get_status = __get_status, \ + } + +#define SPINAND_SELECT_TARGET(__func) \ + .select_target = __func, + +#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ + __flags, ...) \ + { \ + .model = __model, \ + .devid = __id, \ + .memorg = __memorg, \ + .eccreq = __eccreq, \ + .op_variants = __op_variants, \ + .flags = __flags, \ + __VA_ARGS__ \ + } + +/** + * struct spinand_device - SPI NAND device instance + * @base: NAND device instance + * @slave: pointer to the SPI slave object + * @lock: lock used to serialize accesses to the NAND + * @id: NAND ID as returned by READ_ID + * @flags: NAND flags + * @op_templates: various SPI mem op templates + * @op_templates.read_cache: read cache op template + * @op_templates.write_cache: write cache op template + * @op_templates.update_cache: update cache op template + * @select_target: select a specific target/die. Usually called before sending + * a command addressing a page or an eraseblock embedded in + * this die. Only required if your chip exposes several dies + * @cur_target: currently selected target/die + * @eccinfo: on-die ECC information + * @cfg_cache: config register cache. One entry per die + * @databuf: bounce buffer for data + * @oobbuf: bounce buffer for OOB data + * @scratchbuf: buffer used for everything but page accesses. This is needed + * because the spi-mem interface explicitly requests that buffers + * passed in spi_mem_op be DMA-able, so we can't based the bufs on + * the stack + * @manufacturer: SPI NAND manufacturer information + * @priv: manufacturer private data + */ +struct spinand_device { + struct nand_device base; +#ifndef __UBOOT__ + struct spi_mem *spimem; + struct mutex lock; +#else + struct spi_slave *slave; +#endif + struct spinand_id id; + u32 flags; + + struct { + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; + } op_templates; + + int (*select_target)(struct spinand_device *spinand, + unsigned int target); + unsigned int cur_target; + + struct spinand_ecc_info eccinfo; + + u8 *cfg_cache; + u8 *databuf; + u8 *oobbuf; + u8 *scratchbuf; + const struct spinand_manufacturer *manufacturer; + void *priv; +}; + +/** + * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance + * @mtd: MTD instance + * + * Return: the SPI NAND device attached to @mtd. + */ +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nanddev(mtd), struct spinand_device, base); +} + +/** + * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device + * @spinand: SPI NAND device + * + * Return: the MTD device embedded in @spinand. + */ +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand) +{ + return nanddev_to_mtd(&spinand->base); +} + +/** + * nand_to_spinand() - Get the SPI NAND device embedding an NAND object + * @nand: NAND object + * + * Return: the SPI NAND device embedding @nand. + */ +static inline struct spinand_device *nand_to_spinand(struct nand_device *nand) +{ + return container_of(nand, struct spinand_device, base); +} + +/** + * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object + * @spinand: SPI NAND device + * + * Return: the NAND device embedded in @spinand. + */ +static inline struct nand_device * +spinand_to_nand(struct spinand_device *spinand) +{ + return &spinand->base; +} + +/** + * spinand_set_of_node - Attach a DT node to a SPI NAND device + * @spinand: SPI NAND device + * @np: DT node + * + * Attach a DT node to a SPI NAND device. + */ +static inline void spinand_set_of_node(struct spinand_device *spinand, + const struct device_node *np) +{ + nanddev_set_of_node(&spinand->base, np); +} + +int spinand_match_and_init(struct spinand_device *dev, + const struct spinand_info *table, + unsigned int table_size, u8 devid); + +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_select_target(struct spinand_device *spinand, unsigned int target); + +#endif /* __LINUX_MTD_SPINAND_H */ -- cgit v1.2.3 From 883d8778ae177172c0a53c018faa39e61f30dea3 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 16 Aug 2018 17:30:13 +0200 Subject: mtd: spinand: Add initial support for Micron MT29F2G01ABAGD Add a basic driver for Micron SPI NANDs. Only one device is supported right now, but the driver will be extended to support more devices afterwards. Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 17 ++++++ drivers/mtd/nand/spi/micron.c | 135 ++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 3 + 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/micron.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index f0c6e69d2e..4eb745abd4 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o +spinand-objs := core.o micron.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 08f853ae11..36b8b52bc2 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -829,8 +829,25 @@ static const struct nand_ops spinand_ops = { .isbad = spinand_isbad, }; +static const struct spinand_manufacturer *spinand_manufacturers[] = { + µn_spinand_manufacturer, +}; + static int spinand_manufacturer_detect(struct spinand_device *spinand) { + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { + ret = spinand_manufacturers[i]->ops->detect(spinand); + if (ret > 0) { + spinand->manufacturer = spinand_manufacturers[i]; + return 0; + } else if (ret < 0) { + return ret; + } + } + return -ENOTSUPP; } diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c new file mode 100644 index 0000000000..83951c5d0f --- /dev/null +++ b/drivers/mtd/nand/spi/micron.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_MICRON 0x2c + +#define MICRON_STATUS_ECC_MASK GENMASK(7, 4) +#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4) +#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) +#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 64; + region->length = 64; + + return 0; +} + +static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { + .ecc = mt29f2g01abagd_ooblayout_ecc, + .free = mt29f2g01abagd_ooblayout_free, +}; + +static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & MICRON_STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case MICRON_STATUS_ECC_1TO3_BITFLIPS: + return 3; + + case MICRON_STATUS_ECC_4TO6_BITFLIPS: + return 6; + + case MICRON_STATUS_ECC_7TO8_BITFLIPS: + return 8; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info micron_spinand_table[] = { + SPINAND_INFO("MT29F2G01ABAGD", 0x24, + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, + mt29f2g01abagd_ecc_get_status)), +}; + +static int micron_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Micron SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_MICRON) + return 0; + + ret = spinand_match_and_init(spinand, micron_spinand_table, + ARRAY_SIZE(micron_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .detect = micron_spinand_detect, +}; + +const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, + .name = "Micron", + .ops = µn_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index ad59fc01ef..5302d38de8 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -200,6 +200,9 @@ struct spinand_manufacturer { const struct spinand_manufacturer_ops *ops; }; +/* SPI NAND manufacturers */ +extern const struct spinand_manufacturer micron_spinand_manufacturer; + /** * struct spinand_op_variants - SPI NAND operation variants * @ops: the list of variants for a given operation -- cgit v1.2.3 From 3181c0a622d35bd8e6d4407458e7204d4df5a8c1 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 16 Aug 2018 17:30:14 +0200 Subject: mtd: spinand: Add initial support for Winbond W25M02GV Add support for the W25M02GV chip. Signed-off-by: Frieder Schrempf Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/winbond.c | 143 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/winbond.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 4eb745abd4..11ba5de68b 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o +spinand-objs := core.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 36b8b52bc2..ef3e6445d8 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -831,6 +831,7 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { µn_spinand_manufacturer, + &winbond_spinand_manufacturer, }; static int spinand_manufacturer_detect(struct spinand_device *spinand) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c new file mode 100644 index 0000000000..eac811d97c --- /dev/null +++ b/drivers/mtd/nand/spi/winbond.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 exceet electronics GmbH + * + * Authors: + * Frieder Schrempf + * Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_WINBOND 0xEF + +#define WINBOND_CFG_BUF_READ BIT(3) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + + return 0; +} + +static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 2; + region->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { + .ecc = w25m02gv_ooblayout_ecc, + .free = w25m02gv_ooblayout_free, +}; + +static int w25m02gv_select_target(struct spinand_device *spinand, + unsigned int target) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, + spinand->scratchbuf, + 1)); + + *spinand->scratchbuf = target; + return spi_mem_exec_op(spinand->slave, &op); +} + +static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", 0xAB, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), +}; + +/** + * winbond_spinand_detect - initialize device related part in spinand_device + * struct if it is a Winbond device. + * @spinand: SPI NAND device structure + */ +static int winbond_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Winbond SPI NAND read ID need a dummy byte, + * so the first byte in raw_id is dummy. + */ + if (id[1] != SPINAND_MFR_WINBOND) + return 0; + + ret = spinand_match_and_init(spinand, winbond_spinand_table, + ARRAY_SIZE(winbond_spinand_table), id[2]); + if (ret) + return ret; + + return 1; +} + +static int winbond_spinand_init(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + + /* + * Make sure all dies are in buffer read mode and not continuous read + * mode. + */ + for (i = 0; i < nand->memorg.ntargets; i++) { + spinand_select_target(spinand, i); + spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, + WINBOND_CFG_BUF_READ); + } + + return 0; +} + +static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { + .detect = winbond_spinand_detect, + .init = winbond_spinand_init, +}; + +const struct spinand_manufacturer winbond_spinand_manufacturer = { + .id = SPINAND_MFR_WINBOND, + .name = "Winbond", + .ops = &winbond_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5302d38de8..d40a7c8f4e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -202,6 +202,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer winbond_spinand_manufacturer; /** * struct spinand_op_variants - SPI NAND operation variants -- cgit v1.2.3 From 6f041ccabb03bea16c2f21f3254dc9c1cb38425c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:15 +0200 Subject: mtd: spinand: Add initial support for the MX35LF1GE4AB chip Add minimal support for the MX35LF1GE4AB SPI NAND chip. Signed-off-by: Boris Brezillon Acked-by: Jagan Teki --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/macronix.c | 138 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/macronix.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 11ba5de68b..a66edd9199 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o micron.o winbond.o +spinand-objs := core.o macronix.o micron.o winbond.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ef3e6445d8..362d104846 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -830,6 +830,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + ¯onix_spinand_manufacturer, µn_spinand_manufacturer, &winbond_spinand_manufacturer, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c new file mode 100644 index 0000000000..dd351dcb6c --- /dev/null +++ b/drivers/mtd/nand/spi/macronix.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 Macronix + * + * Author: Boris Brezillon + */ + +#ifndef __UBOOT__ +#include +#include +#endif +#include + +#define SPINAND_MFR_MACRONIX 0xC2 + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 2; + region->length = mtd->oobsize - 2; + + return 0; +} + +static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = { + .ecc = mx35lf1ge4ab_ooblayout_ecc, + .free = mx35lf1ge4ab_ooblayout_free, +}; + +static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) +{ + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_DUMMY(1, 1), + SPI_MEM_OP_DATA_IN(1, eccsr, 1)); + + return spi_mem_exec_op(spinand->slave, &op); +} + +static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 eccsr; + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) + return nand->eccreq.strength; + + if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) + return nand->eccreq.strength; + + return eccsr; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info macronix_spinand_table[] = { + SPINAND_INFO("MX35LF1GE4AB", 0x12, + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +}; + +static int macronix_spinand_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + /* + * Macronix SPI NAND read ID needs a dummy byte, so the first byte in + * raw_id is garbage. + */ + if (id[1] != SPINAND_MFR_MACRONIX) + return 0; + + ret = spinand_match_and_init(spinand, macronix_spinand_table, + ARRAY_SIZE(macronix_spinand_table), + id[2]); + if (ret) + return ret; + + return 1; +} + +static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { + .detect = macronix_spinand_detect, +}; + +const struct spinand_manufacturer macronix_spinand_manufacturer = { + .id = SPINAND_MFR_MACRONIX, + .name = "Macronix", + .ops = ¯onix_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index d40a7c8f4e..8c9c756179 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -201,6 +201,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; -- cgit v1.2.3 From 515d0212615b8b4bbe1e39ccf7946e042dc1bf58 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:16 +0200 Subject: mtd: spinand: Add initial support for the MX35LF2GE4AB chip Add support for the MX35LF2GE4AB chip, which is similar to its cousin MX35LF1GE4AB, with two planes instead of one. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- drivers/mtd/nand/spi/macronix.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index dd351dcb6c..662c561e50 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -27,13 +27,13 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int mx35lf1ge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, +static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { return -ERANGE; } -static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, +static int mx35lfxge4ab_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -45,9 +45,9 @@ static int mx35lf1ge4ab_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops mx35lf1ge4ab_ooblayout = { - .ecc = mx35lf1ge4ab_ooblayout_ecc, - .free = mx35lf1ge4ab_ooblayout_free, +static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { + .ecc = mx35lfxge4ab_ooblayout_ecc, + .free = mx35lfxge4ab_ooblayout_free, }; static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) @@ -102,8 +102,16 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&mx35lf1ge4ab_ooblayout, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF2GE4AB", 0x22, + NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; static int macronix_spinand_detect(struct spinand_device *spinand) -- cgit v1.2.3 From 0fa1fc43c87d01a2eb30553f9aea4b52ed43c1f5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:17 +0200 Subject: dt-bindings: Add bindings for SPI NAND devices Add bindings for SPI NAND chips. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- doc/device-tree-bindings/mtd/spi-nand.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 doc/device-tree-bindings/mtd/spi-nand.txt diff --git a/doc/device-tree-bindings/mtd/spi-nand.txt b/doc/device-tree-bindings/mtd/spi-nand.txt new file mode 100644 index 0000000000..8b51f3b6d5 --- /dev/null +++ b/doc/device-tree-bindings/mtd/spi-nand.txt @@ -0,0 +1,5 @@ +SPI NAND flash + +Required properties: +- compatible: should be "spi-nand" +- reg: should encode the chip-select line used to access the NAND chip -- cgit v1.2.3 From 4048a5c519a86eab2805f4f07fe9f5c6c8a1fe91 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:18 +0200 Subject: mtd: declare MTD_PARTITIONS symbol in Kconfig UBI selects MTD_PARTITIONS which is the symbol to compile drivers/mtd/mtdpart.c. Unfortunately, the symbol was not defined in Kconfig and this worked only with board files defining it. Fix this by adding a boolean in Kconfig so boards defined by defconfig files only will work as expected. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon --- drivers/mtd/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 9341d518f3..d98457e223 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,5 +1,8 @@ menu "MTD Support" +config MTD_PARTITIONS + bool + config MTD bool "Enable Driver Model for MTD drivers" depends on DM -- cgit v1.2.3 From b0036f70044d3d59d5c90f0c4f1c8c1a90b7cd55 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:19 +0200 Subject: mtd: mtdpart: balance debug messages Balance debug message in the partition allocation/removal process in order to keep track of them more easily. Signed-off-by: Miquel Raynal --- drivers/mtd/mtdpart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ccbb1757ea..9ccb1b3361 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -362,6 +362,8 @@ int del_mtd_partitions(struct mtd_info *master) struct mtd_part *slave, *next; int ret, err = 0; + debug("Deleting MTD partitions on \"%s\":\n", master->name); + mutex_lock(&mtd_partitions_mutex); list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { -- cgit v1.2.3 From b01c146d182da12e0b60022b34d15fd3a0c0f0fc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:20 +0200 Subject: cmd: ubi: delete useless and misleading definitions These definitions are simply not used and are misleading because similar definitions exist in jffs2/load_kernel.h and are used widely to define MTD device types (which is, by the way, totally redundant with what the MTD core does). Remove these definitions. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Boris Brezillon --- cmd/ubi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/ubi.c b/cmd/ubi.c index 913f0f71fd..0a3405a3b1 100644 --- a/cmd/ubi.c +++ b/cmd/ubi.c @@ -27,11 +27,6 @@ #undef ubi_msg #define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__) -#define DEV_TYPE_NONE 0 -#define DEV_TYPE_NAND 1 -#define DEV_TYPE_ONENAND 2 -#define DEV_TYPE_NOR 3 - /* Private own data */ static struct ubi_device *ubi; static char buffer[80]; -- cgit v1.2.3 From 2b9ace5527d33539844fa831e12ad8357a25246e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:21 +0200 Subject: cmd: mtdparts: add fallthrough in switch statement Switch blocks for deriving size naturally use fallthrough between 'case' statements. Make it explicit. Signed-off-by: Miquel Raynal --- cmd/mtdparts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 0da3afd75f..756fc6018f 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -177,13 +177,16 @@ static u64 memsize_parse (const char *const ptr, const char **retptr) case 'G': case 'g': ret <<= 10; + /* Fallthrough */ case 'M': case 'm': ret <<= 10; + /* Fallthrough */ case 'K': case 'k': ret <<= 10; (*retptr)++; + /* Fallthrough */ default: break; } -- cgit v1.2.3 From a353e6aa8ec8f0aa101cc7e9543fe7843ddc6d98 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 6 Sep 2018 09:08:43 +0200 Subject: lib: strto: parse all lowercase metric prefixes in ustrtoul[l] Both ustrtoul and ustrtoull interpret 1k but not 1m or 1g. Even if the SI symbols for Mega and Giga are 'M' and 'G', certain entries of eg. mtdparts also use (wrongly) the metric prefix 'm' and 'g'. I do not see how parsing lowercase prefixes could break anything, so parse them like their uppercase counterpart. Also, even though kiB is not equal to kB in general, lets not change U-Boot behavior and always use kiB and kB (same applies for MiB vs. MB and GiB vs. GB) as a representation for 1024 instead of 1000. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese --- lib/strto.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/strto.c b/lib/strto.c index 7f6076909a..b7fc31d6e5 100644 --- a/lib/strto.c +++ b/lib/strto.c @@ -85,14 +85,13 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) unsigned long ustrtoul(const char *cp, char **endp, unsigned int base) { unsigned long result = simple_strtoul(cp, endp, base); - switch (**endp) { - case 'G': + switch (tolower(**endp)) { + case 'g': result *= 1024; /* fall through */ - case 'M': + case 'm': result *= 1024; /* fall through */ - case 'K': case 'k': result *= 1024; if ((*endp)[1] == 'i') { @@ -108,14 +107,13 @@ unsigned long ustrtoul(const char *cp, char **endp, unsigned int base) unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base) { unsigned long long result = simple_strtoull(cp, endp, base); - switch (**endp) { - case 'G': + switch (tolower(**endp)) { + case 'g': result *= 1024; /* fall through */ - case 'M': + case 'm': result *= 1024; /* fall through */ - case 'K': case 'k': result *= 1024; if ((*endp)[1] == 'i') { -- cgit v1.2.3 From b87b0d8d79e05f85e4f18200eac4283d513c3f3c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 6 Sep 2018 09:08:44 +0200 Subject: lib: strto: fix metric suffix parsing in strtoul[l] While 1kB or 1kiB will be parsed correctly, 1k will return the right amount, but the metric suffix will not be escaped once the char pointer updated. Fix this situation by simplifying the move of the endp pointer. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese --- lib/strto.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/strto.c b/lib/strto.c index b7fc31d6e5..55ff9f7437 100644 --- a/lib/strto.c +++ b/lib/strto.c @@ -94,12 +94,11 @@ unsigned long ustrtoul(const char *cp, char **endp, unsigned int base) /* fall through */ case 'k': result *= 1024; - if ((*endp)[1] == 'i') { - if ((*endp)[2] == 'B') - (*endp) += 3; - else - (*endp) += 2; - } + (*endp)++; + if (**endp == 'i') + (*endp)++; + if (**endp == 'B') + (*endp)++; } return result; } @@ -116,12 +115,11 @@ unsigned long long ustrtoull(const char *cp, char **endp, unsigned int base) /* fall through */ case 'k': result *= 1024; - if ((*endp)[1] == 'i') { - if ((*endp)[2] == 'B') - (*endp) += 3; - else - (*endp) += 2; - } + (*endp)++; + if (**endp == 'i') + (*endp)++; + if (**endp == 'B') + (*endp)++; } return result; } -- cgit v1.2.3 From 00ac922db4085982011438e9471e0fcacca2e8fc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 6 Sep 2018 09:08:45 +0200 Subject: cmd: mtdparts: accept spi-nand devices Let spi-nand devices be recognized by mtdparts. This is superfluous but a full mtdparts rework would be very time-consuming. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Boris Brezillon Reviewed-by: Stefan Roese --- cmd/mtdparts.c | 13 ++++++++----- include/jffs2/load_kernel.h | 7 +++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 756fc6018f..2e547894c6 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -37,7 +37,7 @@ * mtdids=[,,...] * * := = - * := 'nand'|'nor'|'onenand' + * := 'nand'|'nor'|'onenand'|'spi-nand' * := mtd device number, 0... * := unique device tag used by linux kernel to find mtd device (mtd->name) * @@ -339,7 +339,7 @@ static int part_validate_eraseblock(struct mtdids *id, struct part_info *part) if (!mtd->numeraseregions) { /* - * Only one eraseregion (NAND, OneNAND or uniform NOR), + * Only one eraseregion (NAND, SPI-NAND, OneNAND or uniform NOR), * checking for alignment is easy here */ offset = part->offset; @@ -1030,7 +1030,7 @@ static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_ } /** - * Parse device id string := 'nand'|'nor'|'onenand', + * Parse device id string := 'nand'|'nor'|'onenand'|'spi-nand', * return device type and number. * * @param id string describing device id @@ -1054,6 +1054,9 @@ int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type, } else if (strncmp(p, "onenand", 7) == 0) { *dev_type = MTD_DEV_TYPE_ONENAND; p += 7; + } else if (strncmp(p, "spi-nand", 8) == 0) { + *dev_type = MTD_DEV_TYPE_SPINAND; + p += 8; } else { printf("incorrect device type in %s\n", id); return 1; @@ -1636,7 +1639,7 @@ static int parse_mtdids(const char *const ids) while(p && (*p != '\0')) { ret = 1; - /* parse 'nor'|'nand'|'onenand' */ + /* parse 'nor'|'nand'|'onenand'|'spi-nand' */ if (mtd_id_parse(p, &p, &type, &num) != 0) break; @@ -2112,7 +2115,7 @@ static char mtdparts_help_text[] = "'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n" "mtdids=[,,...]\n\n" " := =\n" - " := 'nand'|'nor'|'onenand'\n" + " := 'nand'|'nor'|'onenand'|'spi-nand'\n" " := mtd device number, 0...\n" " := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n" "'mtdparts' - partition list\n\n" diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index 1ddff062ad..9346d7ee9f 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -15,9 +15,12 @@ #define MTD_DEV_TYPE_NOR 0x0001 #define MTD_DEV_TYPE_NAND 0x0002 #define MTD_DEV_TYPE_ONENAND 0x0004 +#define MTD_DEV_TYPE_SPINAND 0x0008 -#define MTD_DEV_TYPE(type) ((type == MTD_DEV_TYPE_NAND) ? "nand" : \ - (type == MTD_DEV_TYPE_ONENAND) ? "onenand" : "nor") +#define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" : \ + (type == MTD_DEV_TYPE_NOR ? "nor" : \ + (type == MTD_DEV_TYPE_ONENAND ? "onenand" : \ + "spi-nand"))) \ struct mtd_device { struct list_head link; -- cgit v1.2.3 From d60aea94e92a8783e95591b7eeaf733903eeaec7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 6 Sep 2018 09:08:46 +0200 Subject: cmd: mtdparts: remove mandatory 'mtdparts=' prefix All U-Boot users must define the mtdparts environment variable with: setenv mtdparts mtdparts=... While this may ease the partition declaration job to be passed to Linux, this is a pure software limitation and forcing this prefix is a complete non-sense. Let the user to declare manually the mtdparts variable without the prefix. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese --- cmd/mtdparts.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/cmd/mtdparts.c b/cmd/mtdparts.c index 2e547894c6..f7ed1a0779 100644 --- a/cmd/mtdparts.c +++ b/cmd/mtdparts.c @@ -44,7 +44,7 @@ * * 'mtdparts' - partition list * - * mtdparts=mtdparts=[;...] + * mtdparts=[mtdparts=][;...] * * := :[,...] * := unique device tag used by linux kernel to find mtd device (mtd->name) @@ -62,11 +62,11 @@ * * 1 NOR Flash, with 1 single writable partition: * mtdids=nor0=edb7312-nor - * mtdparts=mtdparts=edb7312-nor:- + * mtdparts=[mtdparts=]edb7312-nor:- * * 1 NOR Flash with 2 partitions, 1 NAND with one * mtdids=nor0=edb7312-nor,nand0=edb7312-nand - * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + * mtdparts=[mtdparts=]edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) * */ @@ -1099,9 +1099,6 @@ static int generate_mtdparts(char *buf, u32 buflen) return 0; } - strcpy(p, "mtdparts="); - p += 9; - list_for_each(dentry, &devices) { dev = list_entry(dentry, struct mtd_device, link); @@ -1572,11 +1569,9 @@ static int parse_mtdparts(const char *const mtdparts) if (!p) p = mtdparts; - if (strncmp(p, "mtdparts=", 9) != 0) { - printf("mtdparts variable doesn't start with 'mtdparts='\n"); - return err; - } - p += 9; + /* Skip the useless prefix, if any */ + if (strncmp(p, "mtdparts=", 9) == 0) + p += 9; while (*p != '\0') { err = 1; -- cgit v1.2.3 From 739def7253c4b782bcb9cc816edf3b2f2aeafb4e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:23 +0200 Subject: dm: drop unused helper in MTD header include/mtd.h might be included by files even if CONFIG_DM is not enabled. In this case, the call to dev_get_uclass_priv() would trigger a build error. Because this helper has no user, let's drop it off. Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/mtd.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/mtd.h b/include/mtd.h index 548e7f191b..9e5c807294 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -8,15 +8,4 @@ #include -/* - * Get mtd_info structure of the dev, which is stored as uclass private. - * - * @dev: The MTD device - * @return: pointer to mtd_info, NULL on error - */ -static inline struct mtd_info *mtd_get_info(struct udevice *dev) -{ - return dev_get_uclass_priv(dev); -} - #endif /* _MTD_H_ */ -- cgit v1.2.3 From e9f62db64b55fa3451731781cc3b1d3bdd271d54 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:24 +0200 Subject: mtd: uclass: add probe function The user might want to trigger the probe of any MTD device, export these functions so they can be called from a command source file. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- drivers/mtd/mtd-uclass.c | 16 ++++++++++++++++ include/mtd.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/drivers/mtd/mtd-uclass.c b/drivers/mtd/mtd-uclass.c index 9ca049c437..5418217431 100644 --- a/drivers/mtd/mtd-uclass.c +++ b/drivers/mtd/mtd-uclass.c @@ -5,9 +5,25 @@ #include #include +#include #include #include +/** + * mtd_probe - Probe the device @dev if not already done + * + * @dev: U-Boot device to probe + * + * @return 0 on success, an error otherwise. + */ +int mtd_probe(struct udevice *dev) +{ + if (device_active(dev)) + return 0; + + return device_probe(dev); +} + /* * Implement a MTD uclass which should include most flash drivers. * The uclass private is pointed to mtd_info. diff --git a/include/mtd.h b/include/mtd.h index 9e5c807294..75aca262d4 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -8,4 +8,6 @@ #include +int mtd_probe(struct udevice *dev); + #endif /* _MTD_H_ */ -- cgit v1.2.3 From 21cc1fb5af06e468c74ae601bac719d306523f9c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:25 +0200 Subject: mtd: mtdpart: add a generic mtdparts-like parser The current parser is very specific to U-Boot mtdparts implementation. It does not use MTD structures like mtd_info and mtd_partition. Copy and adapt the current parser in drivers/mtd/mtd-uclass.c (to not break the current use of mtdparts.c itself) and write some kind of a wrapper around the current implementation to allow other commands to benefit from this parsing in a user-friendly way. This new function will allocate an mtd_partition array for each successful call. This array must be freed after use by the caller. The given 'mtdparts' buffer pointer will be moved forward to the next MTD device (if any, it will point towards a '\0' character otherwise). Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- drivers/mtd/mtdpart.c | 210 +++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/partitions.h | 21 +++++ 2 files changed, 231 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9ccb1b3361..49353b038a 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "mtdcore.h" @@ -76,6 +77,215 @@ char *kstrdup(const char *s, gfp_t gfp) } #endif +#define MTD_SIZE_REMAINING (~0LLU) +#define MTD_OFFSET_NOT_SPECIFIED (~0LLU) + +/** + * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition + * with it and update the @mtdparts string pointer. + * + * The partition name is allocated and must be freed by the caller. + * + * This function is widely inspired from part_parse (mtdparts.c). + * + * @mtdparts: String describing the partition with mtdparts command syntax + * @partition: MTD partition structure to fill + * + * @return 0 on success, an error otherwise. + */ +static int mtd_parse_partition(const char **_mtdparts, + struct mtd_partition *partition) +{ + const char *mtdparts = *_mtdparts; + const char *name = NULL; + int name_len; + char *buf; + + /* Ensure the partition structure is empty */ + memset(partition, 0, sizeof(struct mtd_partition)); + + /* Fetch the partition size */ + if (*mtdparts == '-') { + /* Assign all remaining space to this partition */ + partition->size = MTD_SIZE_REMAINING; + mtdparts++; + } else { + partition->size = ustrtoull(mtdparts, (char **)&mtdparts, 0); + if (partition->size < SZ_4K) { + printf("Minimum partition size 4kiB, %lldB requested\n", + partition->size); + return -EINVAL; + } + } + + /* Check for the offset */ + partition->offset = MTD_OFFSET_NOT_SPECIFIED; + if (*mtdparts == '@') { + mtdparts++; + partition->offset = ustrtoull(mtdparts, (char **)&mtdparts, 0); + } + + /* Now look for the name */ + if (*mtdparts == '(') { + name = ++mtdparts; + mtdparts = strchr(name, ')'); + if (!mtdparts) { + printf("No closing ')' found in partition name\n"); + return -EINVAL; + } + name_len = mtdparts - name + 1; + if ((name_len - 1) == 0) { + printf("Empty partition name\n"); + return -EINVAL; + } + mtdparts++; + } else { + /* Name will be of the form size@offset */ + name_len = 22; + } + + /* Check if the partition is read-only */ + if (strncmp(mtdparts, "ro", 2) == 0) { + partition->mask_flags |= MTD_WRITEABLE; + mtdparts += 2; + } + + /* Check for a potential next partition definition */ + if (*mtdparts == ',') { + if (partition->size == MTD_SIZE_REMAINING) { + printf("No partitions allowed after a fill-up\n"); + return -EINVAL; + } + ++mtdparts; + } else if ((*mtdparts == ';') || (*mtdparts == '\0')) { + /* NOP */ + } else { + printf("Unexpected character '%c' in mtdparts\n", *mtdparts); + return -EINVAL; + } + + /* + * Allocate a buffer for the name and either copy the provided name or + * auto-generate it with the form 'size@offset'. + */ + buf = malloc(name_len); + if (!buf) + return -ENOMEM; + + if (name) + strncpy(buf, name, name_len - 1); + else + snprintf(buf, name_len, "0x%08llx@0x%08llx", + partition->size, partition->offset); + + buf[name_len - 1] = '\0'; + partition->name = buf; + + *_mtdparts = mtdparts; + + return 0; +} + +/** + * mtd_parse_partitions - Create a partition array from an mtdparts definition + * + * Stateless function that takes a @parent MTD device, a string @_mtdparts + * describing the partitions (with the "mtdparts" command syntax) and creates + * the corresponding MTD partition structure array @_parts. Both the name and + * the structure partition itself must be freed freed, the caller may use + * @mtd_free_parsed_partitions() for this purpose. + * + * @parent: MTD device which contains the partitions + * @_mtdparts: Pointer to a string describing the partitions with "mtdparts" + * command syntax. + * @_parts: Allocated array containing the partitions, must be freed by the + * caller. + * @_nparts: Size of @_parts array. + * + * @return 0 on success, an error otherwise. + */ +int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nparts) +{ + struct mtd_partition partition = {}, *parts; + const char *mtdparts = *_mtdparts; + int cur_off = 0, cur_sz = 0; + int nparts = 0; + int ret, idx; + u64 sz; + + /* First, iterate over the partitions until we know their number */ + while (mtdparts[0] != '\0' && mtdparts[0] != ';') { + ret = mtd_parse_partition(&mtdparts, &partition); + if (ret) + return ret; + + free((char *)partition.name); + nparts++; + } + + /* Allocate an array of partitions to give back to the caller */ + parts = malloc(sizeof(*parts) * nparts); + if (!parts) { + printf("Not enough space to save partitions meta-data\n"); + return -ENOMEM; + } + + /* Iterate again over each partition to save the data in our array */ + for (idx = 0; idx < nparts; idx++) { + ret = mtd_parse_partition(_mtdparts, &parts[idx]); + if (ret) + return ret; + + if (parts[idx].size == MTD_SIZE_REMAINING) + parts[idx].size = parent->size - cur_sz; + cur_sz += parts[idx].size; + + sz = parts[idx].size; + if (sz < parent->writesize || do_div(sz, parent->writesize)) { + printf("Partition size must be a multiple of %d\n", + parent->writesize); + return -EINVAL; + } + + if (parts[idx].offset == MTD_OFFSET_NOT_SPECIFIED) + parts[idx].offset = cur_off; + cur_off += parts[idx].size; + + parts[idx].ecclayout = parent->ecclayout; + } + + /* Offset by one mtdparts to point to the next device if any */ + if (*_mtdparts[0] == ';') + (*_mtdparts)++; + + *_parts = parts; + *_nparts = nparts; + + return 0; +} + +/** + * mtd_free_parsed_partitions - Free dynamically allocated partitions + * + * Each successful call to @mtd_parse_partitions must be followed by a call to + * @mtd_free_parsed_partitions to free any allocated array during the parsing + * process. + * + * @parts: Array containing the partitions that will be freed. + * @nparts: Size of @parts array. + */ +void mtd_free_parsed_partitions(struct mtd_partition *parts, + unsigned int nparts) +{ + int i; + + for (i = 0; i < nparts; i++) + free((char *)parts[i].name); + + free(parts); +} + /* * MTD methods which simply translate the effective address and pass through * to the _real_ device. diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index ce0e8dbee4..6eea0a547a 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -87,4 +87,25 @@ int mtd_add_partition(struct mtd_info *master, const char *name, int mtd_del_partition(struct mtd_info *master, int partno); uint64_t mtd_get_device_size(const struct mtd_info *mtd); +#if defined(CONFIG_MTD_PARTITIONS) +int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nparts); +void mtd_free_parsed_partitions(struct mtd_partition *parts, + unsigned int nparts); +#else +static inline int +mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nparts) +{ + *_nparts = 0; + + return 0; +} +static inline void +mtd_free_parsed_partitions(struct mtd_partition *parts, unsigned int nparts) +{ + return; +} +#endif /* defined(MTD_PARTITIONS) */ + #endif -- cgit v1.2.3 From ff4afa8a981e22eef670c7c857cb87983346cc2c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:26 +0200 Subject: mtd: uboot: search for an equivalent MTD name with the mtdids Using an MTD device (resp. partition) name in mtdparts is simple and straightforward. However, for a long time already, another name was given in mtdparts to indicate a device (resp. partition) so the "mtdids" environment variable was created to do the match. Let's create a function that, from an MTD device (resp. partition) name, search for the equivalent name in the "mtdparts" environment variable thanks to the "mtdids" string. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- drivers/mtd/mtd_uboot.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/mtd.h | 5 ++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index 2b3b2eecca..8d7e7890b7 100644 --- a/drivers/mtd/mtd_uboot.c +++ b/drivers/mtd/mtd_uboot.c @@ -5,7 +5,70 @@ */ #include #include -#include +#include /* Legacy */ + +/** + * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to + * the mtdids legacy environment variable. + * + * The mtdids string is a list of comma-separated 'dev_id=mtd_id' tupples. + * Check if one of the mtd_id matches mtdname, in this case save dev_id in + * altname. + * + * @mtdname: Current MTD device name + * @altname: Alternate name to return + * @max_len: Length of the alternate name buffer + * + * @return 0 on success, an error otherwise. + */ +int mtd_search_alternate_name(const char *mtdname, char *altname, + unsigned int max_len) +{ + const char *mtdids, *equal, *comma, *dev_id, *mtd_id; + int dev_id_len, mtd_id_len; + + mtdids = env_get("mtdids"); + if (!mtdids) + return -EINVAL; + + do { + /* Find the '=' sign */ + dev_id = mtdids; + equal = strchr(dev_id, '='); + if (!equal) + break; + dev_id_len = equal - mtdids; + mtd_id = equal + 1; + + /* Find the end of the tupple */ + comma = strchr(mtdids, ','); + if (comma) + mtd_id_len = comma - mtd_id; + else + mtd_id_len = &mtdids[strlen(mtdids)] - mtd_id + 1; + + if (!dev_id_len || !mtd_id_len) + return -EINVAL; + + if (dev_id_len + 1 > max_len) + continue; + + /* Compare the name we search with the current mtd_id */ + if (!strncmp(mtdname, mtd_id, mtd_id_len)) { + strncpy(altname, dev_id, dev_id_len); + altname[dev_id_len] = 0; + + return 0; + } + + /* Go to the next tupple */ + mtdids = comma + 1; + } while (comma); + + return -EINVAL; +} + +/* Legacy */ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, loff_t *maxsize, int devtype) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b8c2c3fd59..af6f4a61f8 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -549,5 +549,10 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, const uint64_t length, uint64_t *len_incl_bad, int *truncated); + +/* drivers/mtd/mtd_uboot.c */ +int mtd_search_alternate_name(const char *mtdname, char *altname, + unsigned int max_len); + #endif #endif /* __MTD_MTD_H__ */ -- cgit v1.2.3 From 2a74930da57f6fbe3c24509f1d481f435acd2356 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:27 +0200 Subject: mtd: mtdpart: implement proper partition handling Instead of collecting partitions in a flat list, create a hierarchy within the mtd_info structure: use a partitions list to keep track of the partitions of an MTD device (which might be itself a partition of another MTD device), a pointer to the parent device (NULL when the MTD device is the root one, not a partition). By also saving directly in mtd_info the offset of the partition, we can get rid of the mtd_part structure. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 2 + drivers/mtd/mtdpart.c | 413 +++++++++++++++++------------------------ include/linux/mtd/mtd.h | 32 ++++ include/linux/mtd/partitions.h | 1 - 4 files changed, 209 insertions(+), 239 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index fb1d68d5e2..fb6c779abb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -426,6 +426,8 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + INIT_LIST_HEAD(&mtd->partitions); + /* default value if not set by driver */ if (mtd->bitflip_threshold == 0) mtd->bitflip_threshold = mtd->ecc_strength; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 49353b038a..4d2ac8107f 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -30,29 +30,12 @@ #include "mtdcore.h" -/* Our partition linked list */ -static LIST_HEAD(mtd_partitions); #ifndef __UBOOT__ static DEFINE_MUTEX(mtd_partitions_mutex); #else DEFINE_MUTEX(mtd_partitions_mutex); #endif -/* Our partition node structure */ -struct mtd_part { - struct mtd_info mtd; - struct mtd_info *master; - uint64_t offset; - struct list_head list; -}; - -/* - * Given a pointer to the MTD object in the mtd_part structure, we can retrieve - * the pointer to that structure with this macro. - */ -#define PART(x) ((struct mtd_part *)(x)) - - #ifdef __UBOOT__ /* from mm/util.c */ @@ -294,19 +277,18 @@ void mtd_free_parsed_partitions(struct mtd_partition *parts, static int part_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); struct mtd_ecc_stats stats; int res; - stats = part->master->ecc_stats; - res = part->master->_read(part->master, from + part->offset, len, - retlen, buf); + stats = mtd->parent->ecc_stats; + res = mtd->parent->_read(mtd->parent, from + mtd->offset, len, + retlen, buf); if (unlikely(mtd_is_eccerr(res))) mtd->ecc_stats.failed += - part->master->ecc_stats.failed - stats.failed; + mtd->parent->ecc_stats.failed - stats.failed; else mtd->ecc_stats.corrected += - part->master->ecc_stats.corrected - stats.corrected; + mtd->parent->ecc_stats.corrected - stats.corrected; return res; } @@ -314,17 +296,13 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, static int part_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { - struct mtd_part *part = PART(mtd); - - return part->master->_point(part->master, from + part->offset, len, - retlen, virt, phys); + return mtd->parent->_point(mtd->parent, from + mtd->offset, len, + retlen, virt, phys); } static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) { - struct mtd_part *part = PART(mtd); - - return part->master->_unpoint(part->master, from + part->offset, len); + return mtd->parent->_unpoint(mtd->parent, from + mtd->offset, len); } #endif @@ -333,17 +311,13 @@ static unsigned long part_get_unmapped_area(struct mtd_info *mtd, unsigned long offset, unsigned long flags) { - struct mtd_part *part = PART(mtd); - - offset += part->offset; - return part->master->_get_unmapped_area(part->master, len, offset, - flags); + offset += mtd->offset; + return mtd->parent->_get_unmapped_area(mtd->parent, len, offset, flags); } static int part_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct mtd_part *part = PART(mtd); int res; if (from >= mtd->size) @@ -368,7 +342,7 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - res = part->master->_read_oob(part->master, from + part->offset, ops); + res = mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops); if (unlikely(res)) { if (mtd_is_bitflip(res)) mtd->ecc_stats.corrected++; @@ -381,99 +355,87 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_read_user_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_read_user_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_get_user_prot_info(part->master, len, retlen, - buf); + return mtd->parent->_get_user_prot_info(mtd->parent, len, retlen, + buf); } static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_read_fact_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_read_fact_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_get_fact_prot_info(part->master, len, retlen, - buf); + return mtd->parent->_get_fact_prot_info(mtd->parent, len, retlen, + buf); } static int part_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_write(part->master, to + part->offset, len, - retlen, buf); + return mtd->parent->_write(mtd->parent, to + mtd->offset, len, + retlen, buf); } static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_panic_write(part->master, to + part->offset, len, - retlen, buf); + return mtd->parent->_panic_write(mtd->parent, to + mtd->offset, len, + retlen, buf); } static int part_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - struct mtd_part *part = PART(mtd); - if (to >= mtd->size) return -EINVAL; if (ops->datbuf && to + ops->len > mtd->size) return -EINVAL; - return part->master->_write_oob(part->master, to + part->offset, ops); + return mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops); } static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mtd_part *part = PART(mtd); - return part->master->_write_user_prot_reg(part->master, from, len, - retlen, buf); + return mtd->parent->_write_user_prot_reg(mtd->parent, from, len, + retlen, buf); } static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_lock_user_prot_reg(part->master, from, len); + return mtd->parent->_lock_user_prot_reg(mtd->parent, from, len); } #ifndef __UBOOT__ static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { - struct mtd_part *part = PART(mtd); - return part->master->_writev(part->master, vecs, count, - to + part->offset, retlen); + return mtd->parent->_writev(mtd->parent, vecs, count, + to + mtd->offset, retlen); } #endif static int part_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct mtd_part *part = PART(mtd); int ret; - instr->addr += part->offset; - ret = part->master->_erase(part->master, instr); + instr->addr += mtd->offset; + ret = mtd->parent->_erase(mtd->parent, instr); if (ret) { if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; + instr->fail_addr -= mtd->offset; + instr->addr -= mtd->offset; } return ret; } @@ -481,11 +443,9 @@ static int part_erase(struct mtd_info *mtd, struct erase_info *instr) void mtd_erase_callback(struct erase_info *instr) { if (instr->mtd->_erase == part_erase) { - struct mtd_part *part = PART(instr->mtd); - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; + instr->fail_addr -= instr->mtd->offset; + instr->addr -= instr->mtd->offset; } if (instr->callback) instr->callback(instr); @@ -494,107 +454,112 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback); static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_lock(part->master, ofs + part->offset, len); + return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len); } static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_unlock(part->master, ofs + part->offset, len); + return mtd->parent->_unlock(mtd->parent, ofs + mtd->offset, len); } static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - struct mtd_part *part = PART(mtd); - return part->master->_is_locked(part->master, ofs + part->offset, len); + return mtd->parent->_is_locked(mtd->parent, ofs + mtd->offset, len); } static void part_sync(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - part->master->_sync(part->master); + mtd->parent->_sync(mtd->parent); } #ifndef __UBOOT__ static int part_suspend(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - return part->master->_suspend(part->master); + return mtd->parent->_suspend(mtd->parent); } static void part_resume(struct mtd_info *mtd) { - struct mtd_part *part = PART(mtd); - part->master->_resume(part->master); + mtd->parent->_resume(mtd->parent); } #endif static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); - ofs += part->offset; - return part->master->_block_isreserved(part->master, ofs); + ofs += mtd->offset; + return mtd->parent->_block_isreserved(mtd->parent, ofs); } static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); - ofs += part->offset; - return part->master->_block_isbad(part->master, ofs); + ofs += mtd->offset; + return mtd->parent->_block_isbad(mtd->parent, ofs); } static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct mtd_part *part = PART(mtd); int res; - ofs += part->offset; - res = part->master->_block_markbad(part->master, ofs); + ofs += mtd->offset; + res = mtd->parent->_block_markbad(mtd->parent, ofs); if (!res) mtd->ecc_stats.badblocks++; return res; } -static inline void free_partition(struct mtd_part *p) +static inline void free_partition(struct mtd_info *p) { - kfree(p->mtd.name); + kfree(p->name); kfree(p); } /* * This function unregisters and destroy all slave MTD objects which are - * attached to the given master MTD object. + * attached to the given master MTD object, recursively. */ +static int do_del_mtd_partitions(struct mtd_info *master) +{ + struct mtd_info *slave, *next; + int ret, err = 0; + + list_for_each_entry_safe(slave, next, &master->partitions, node) { + if (mtd_has_partitions(slave)) + del_mtd_partitions(slave); + + debug("Deleting %s MTD partition\n", slave->name); + ret = del_mtd_device(slave); + if (ret < 0) { + printf("Error when deleting partition \"%s\" (%d)\n", + slave->name, ret); + err = ret; + continue; + } + + list_del(&slave->node); + free_partition(slave); + } + + return err; +} int del_mtd_partitions(struct mtd_info *master) { - struct mtd_part *slave, *next; - int ret, err = 0; + int ret; debug("Deleting MTD partitions on \"%s\":\n", master->name); mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if (slave->master == master) { - ret = del_mtd_device(&slave->mtd); - if (ret < 0) { - err = ret; - continue; - } - list_del(&slave->list); - free_partition(slave); - } + ret = do_del_mtd_partitions(master); mutex_unlock(&mtd_partitions_mutex); - return err; + return ret; } -static struct mtd_part *allocate_partition(struct mtd_info *master, - const struct mtd_partition *part, int partno, - uint64_t cur_offset) +static struct mtd_info *allocate_partition(struct mtd_info *master, + const struct mtd_partition *part, + int partno, uint64_t cur_offset) { - struct mtd_part *slave; + struct mtd_info *slave; char *name; /* allocate the partition structure */ @@ -609,85 +574,87 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } /* set up the MTD object for this partition */ - slave->mtd.type = master->type; - slave->mtd.flags = master->flags & ~part->mask_flags; - slave->mtd.size = part->size; - slave->mtd.writesize = master->writesize; - slave->mtd.writebufsize = master->writebufsize; - slave->mtd.oobsize = master->oobsize; - slave->mtd.oobavail = master->oobavail; - slave->mtd.subpage_sft = master->subpage_sft; - - slave->mtd.name = name; - slave->mtd.owner = master->owner; + slave->type = master->type; + slave->flags = master->flags & ~part->mask_flags; + slave->size = part->size; + slave->writesize = master->writesize; + slave->writebufsize = master->writebufsize; + slave->oobsize = master->oobsize; + slave->oobavail = master->oobavail; + slave->subpage_sft = master->subpage_sft; + + slave->name = name; + slave->owner = master->owner; #ifndef __UBOOT__ - slave->mtd.backing_dev_info = master->backing_dev_info; + slave->backing_dev_info = master->backing_dev_info; /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone * to have the same data be in two different partitions. */ - slave->mtd.dev.parent = master->dev.parent; + slave->dev.parent = master->dev.parent; #endif if (master->_read) - slave->mtd._read = part_read; + slave->_read = part_read; if (master->_write) - slave->mtd._write = part_write; + slave->_write = part_write; if (master->_panic_write) - slave->mtd._panic_write = part_panic_write; + slave->_panic_write = part_panic_write; #ifndef __UBOOT__ if (master->_point && master->_unpoint) { - slave->mtd._point = part_point; - slave->mtd._unpoint = part_unpoint; + slave->_point = part_point; + slave->_unpoint = part_unpoint; } #endif if (master->_get_unmapped_area) - slave->mtd._get_unmapped_area = part_get_unmapped_area; + slave->_get_unmapped_area = part_get_unmapped_area; if (master->_read_oob) - slave->mtd._read_oob = part_read_oob; + slave->_read_oob = part_read_oob; if (master->_write_oob) - slave->mtd._write_oob = part_write_oob; + slave->_write_oob = part_write_oob; if (master->_read_user_prot_reg) - slave->mtd._read_user_prot_reg = part_read_user_prot_reg; + slave->_read_user_prot_reg = part_read_user_prot_reg; if (master->_read_fact_prot_reg) - slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; + slave->_read_fact_prot_reg = part_read_fact_prot_reg; if (master->_write_user_prot_reg) - slave->mtd._write_user_prot_reg = part_write_user_prot_reg; + slave->_write_user_prot_reg = part_write_user_prot_reg; if (master->_lock_user_prot_reg) - slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; + slave->_lock_user_prot_reg = part_lock_user_prot_reg; if (master->_get_user_prot_info) - slave->mtd._get_user_prot_info = part_get_user_prot_info; + slave->_get_user_prot_info = part_get_user_prot_info; if (master->_get_fact_prot_info) - slave->mtd._get_fact_prot_info = part_get_fact_prot_info; + slave->_get_fact_prot_info = part_get_fact_prot_info; if (master->_sync) - slave->mtd._sync = part_sync; + slave->_sync = part_sync; #ifndef __UBOOT__ if (!partno && !master->dev.class && master->_suspend && master->_resume) { - slave->mtd._suspend = part_suspend; - slave->mtd._resume = part_resume; + slave->_suspend = part_suspend; + slave->_resume = part_resume; } if (master->_writev) - slave->mtd._writev = part_writev; + slave->_writev = part_writev; #endif if (master->_lock) - slave->mtd._lock = part_lock; + slave->_lock = part_lock; if (master->_unlock) - slave->mtd._unlock = part_unlock; + slave->_unlock = part_unlock; if (master->_is_locked) - slave->mtd._is_locked = part_is_locked; + slave->_is_locked = part_is_locked; if (master->_block_isreserved) - slave->mtd._block_isreserved = part_block_isreserved; + slave->_block_isreserved = part_block_isreserved; if (master->_block_isbad) - slave->mtd._block_isbad = part_block_isbad; + slave->_block_isbad = part_block_isbad; if (master->_block_markbad) - slave->mtd._block_markbad = part_block_markbad; - slave->mtd._erase = part_erase; - slave->master = master; + slave->_block_markbad = part_block_markbad; + slave->_erase = part_erase; + slave->parent = master; slave->offset = part->offset; + INIT_LIST_HEAD(&slave->partitions); + INIT_LIST_HEAD(&slave->node); if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; @@ -703,41 +670,41 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } if (slave->offset == MTDPART_OFS_RETAIN) { slave->offset = cur_offset; - if (master->size - slave->offset >= slave->mtd.size) { - slave->mtd.size = master->size - slave->offset - - slave->mtd.size; + if (master->size - slave->offset >= slave->size) { + slave->size = master->size - slave->offset + - slave->size; } else { debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", part->name, master->size - slave->offset, - slave->mtd.size); + slave->size); /* register to preserve ordering */ goto out_register; } } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = master->size - slave->offset; + if (slave->size == MTDPART_SIZ_FULL) + slave->size = master->size - slave->offset; debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, - (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); + (unsigned long long)(slave->offset + slave->size), slave->name); /* let's do some sanity checks */ if (slave->offset >= master->size) { /* let's register it anyway to preserve ordering */ slave->offset = 0; - slave->mtd.size = 0; + slave->size = 0; printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", part->name); goto out_register; } - if (slave->offset + slave->mtd.size > master->size) { - slave->mtd.size = master->size - slave->offset; + if (slave->offset + slave->size > master->size) { + slave->size = master->size - slave->offset; printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", - part->name, master->name, (unsigned long long)slave->mtd.size); + part->name, master->name, slave->size); } if (master->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = master->numeraseregions; - u64 end = slave->offset + slave->mtd.size; + u64 end = slave->offset + slave->size; struct mtd_erase_region_info *regions = master->eraseregions; /* Find the first erase regions which is part of this @@ -750,44 +717,43 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } + if (slave->erasesize < regions[i].erasesize) + slave->erasesize = regions[i].erasesize; } - BUG_ON(slave->mtd.erasesize == 0); + WARN_ON(slave->erasesize == 0); } else { /* Single erase size */ - slave->mtd.erasesize = master->erasesize; + slave->erasesize = master->erasesize; } - if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->offset, &slave->mtd)) { + if ((slave->flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->offset, slave)) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; + slave->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", part->name); } - if ((slave->mtd.flags & MTD_WRITEABLE) && - mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) { - slave->mtd.flags &= ~MTD_WRITEABLE; + if ((slave->flags & MTD_WRITEABLE) && + mtd_mod_by_eb(slave->size, slave)) { + slave->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", part->name); } - slave->mtd.ecclayout = master->ecclayout; - slave->mtd.ecc_step_size = master->ecc_step_size; - slave->mtd.ecc_strength = master->ecc_strength; - slave->mtd.bitflip_threshold = master->bitflip_threshold; + slave->ecclayout = master->ecclayout; + slave->ecc_step_size = master->ecc_step_size; + slave->ecc_strength = master->ecc_strength; + slave->bitflip_threshold = master->bitflip_threshold; if (master->_block_isbad) { uint64_t offs = 0; - while (offs < slave->mtd.size) { + while (offs < slave->size) { if (mtd_block_isbad(master, offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; + slave->ecc_stats.badblocks++; + offs += slave->erasesize; } } @@ -800,7 +766,7 @@ int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length) { struct mtd_partition part; - struct mtd_part *p, *new; + struct mtd_info *p, *new; uint64_t start, end; int ret = 0; @@ -829,21 +795,20 @@ int mtd_add_partition(struct mtd_info *master, const char *name, end = offset + length; mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(p, &mtd_partitions, list) - if (p->master == master) { - if ((start >= p->offset) && - (start < (p->offset + p->mtd.size))) - goto err_inv; - - if ((end >= p->offset) && - (end < (p->offset + p->mtd.size))) - goto err_inv; - } + list_for_each_entry(p, &master->partitions, node) { + if (start >= p->offset && + (start < (p->offset + p->size))) + goto err_inv; + + if (end >= p->offset && + (end < (p->offset + p->size))) + goto err_inv; + } - list_add(&new->list, &mtd_partitions); + list_add_tail(&new->node, &master->partitions); mutex_unlock(&mtd_partitions_mutex); - add_mtd_device(&new->mtd); + add_mtd_device(new); return ret; err_inv: @@ -855,18 +820,17 @@ EXPORT_SYMBOL_GPL(mtd_add_partition); int mtd_del_partition(struct mtd_info *master, int partno) { - struct mtd_part *slave, *next; + struct mtd_info *slave, *next; int ret = -EINVAL; mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if ((slave->master == master) && - (slave->mtd.index == partno)) { - ret = del_mtd_device(&slave->mtd); + list_for_each_entry_safe(slave, next, &master->partitions, node) + if (slave->index == partno) { + ret = del_mtd_device(slave); if (ret < 0) break; - list_del(&slave->list); + list_del(&slave->node); free_partition(slave); break; } @@ -890,20 +854,10 @@ int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; + struct mtd_info *slave; uint64_t cur_offset = 0; int i; -#ifdef __UBOOT__ - /* - * Need to init the list here, since LIST_INIT() does not - * work on platforms where relocation has problems (like MIPS - * & PPC). - */ - if (mtd_partitions.next == NULL) - INIT_LIST_HEAD(&mtd_partitions); -#endif - debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); for (i = 0; i < nbparts; i++) { @@ -912,12 +866,12 @@ int add_mtd_partitions(struct mtd_info *master, return PTR_ERR(slave); mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); + list_add_tail(&slave->node, &master->partitions); mutex_unlock(&mtd_partitions_mutex); - add_mtd_device(&slave->mtd); + add_mtd_device(slave); - cur_offset = slave->offset + slave->mtd.size; + cur_offset = slave->offset + slave->size; } return 0; @@ -1020,29 +974,12 @@ int parse_mtd_partitions(struct mtd_info *master, const char *const *types, } #endif -int mtd_is_partition(const struct mtd_info *mtd) -{ - struct mtd_part *part; - int ispart = 0; - - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(part, &mtd_partitions, list) - if (&part->mtd == mtd) { - ispart = 1; - break; - } - mutex_unlock(&mtd_partitions_mutex); - - return ispart; -} -EXPORT_SYMBOL_GPL(mtd_is_partition); - /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) { - if (!mtd_is_partition(mtd)) - return mtd->size; + if (mtd_is_partition(mtd)) + return mtd->parent->size; - return PART(mtd)->master->size; + return mtd->size; } EXPORT_SYMBOL_GPL(mtd_get_device_size); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index af6f4a61f8..68e5915324 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_DM) #include @@ -307,6 +308,27 @@ struct mtd_info { struct udevice *dev; #endif int usecount; + + /* MTD devices do not have any parent. MTD partitions do. */ + struct mtd_info *parent; + + /* + * Offset of the partition relatively to the parent offset. + * Is 0 for real MTD devices (ie. not partitions). + */ + u64 offset; + + /* + * List node used to add an MTD partition to the parent + * partition list. + */ + struct list_head node; + + /* + * List of partitions attached to this MTD device (the parent + * MTD device can itself be a partition). + */ + struct list_head partitions; }; #if IS_ENABLED(CONFIG_DM) @@ -334,6 +356,16 @@ static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) } #endif +static inline bool mtd_is_partition(const struct mtd_info *mtd) +{ + return mtd->parent; +} + +static inline bool mtd_has_partitions(const struct mtd_info *mtd) +{ + return !list_empty(&mtd->partitions); +} + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 6eea0a547a..3822237f2a 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -81,7 +81,6 @@ extern void register_mtd_parser(struct mtd_part_parser *parser); extern void deregister_mtd_parser(struct mtd_part_parser *parser); #endif -int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); -- cgit v1.2.3 From 5db66b3aee6f2c057706d8715f7e5c472e82f047 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:28 +0200 Subject: cmd: mtd: add 'mtd' command There should not be a 'nand' command, a 'sf' command and certainly not a new 'spi-nand' command. Write a 'mtd' command instead to manage all MTD devices/partitions at once. This should be the preferred way to access any MTD device. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- cmd/Kconfig | 10 +- cmd/Makefile | 1 + cmd/mtd.c | 473 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/Makefile | 2 +- drivers/mtd/mtd_uboot.c | 161 +++++++++++++++- include/mtd.h | 1 + 6 files changed, 644 insertions(+), 4 deletions(-) create mode 100644 cmd/mtd.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 13d4c991bf..c9be85989c 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -864,6 +864,12 @@ config CMD_MMC_SWRITE Enable support for the "mmc swrite" command to write Android sparse images to eMMC. +config CMD_MTD + bool "mtd" + select MTD_PARTITIONS + help + MTD commands support. + config CMD_NAND bool "nand" default y if NAND_SUNXI @@ -1697,14 +1703,14 @@ config CMD_MTDPARTS config MTDIDS_DEFAULT string "Default MTD IDs" - depends on CMD_MTDPARTS || CMD_NAND || CMD_FLASH + depends on CMD_MTD || CMD_MTDPARTS || CMD_NAND || CMD_FLASH help Defines a default MTD IDs list for use with MTD partitions in the Linux MTD command line partitions format. config MTDPARTS_DEFAULT string "Default MTD partition scheme" - depends on CMD_MTDPARTS || CMD_NAND || CMD_FLASH + depends on CMD_MTD || CMD_MTDPARTS || CMD_NAND || CMD_FLASH help Defines a default MTD partitioning scheme in the Linux MTD command line partitions format diff --git a/cmd/Makefile b/cmd/Makefile index a61fab6583..3ba65d4d93 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_CMD_MISC) += misc.o obj-$(CONFIG_CMD_MMC) += mmc.o obj-$(CONFIG_CMD_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MP) += mp.o +obj-$(CONFIG_CMD_MTD) += mtd.o obj-$(CONFIG_CMD_MTDPARTS) += mtdparts.o obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_NET) += net.o diff --git a/cmd/mtd.c b/cmd/mtd.c new file mode 100644 index 0000000000..6142223984 --- /dev/null +++ b/cmd/mtd.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * mtd.c + * + * Generic command to handle basic operations on any memory device. + * + * Copyright: Bootlin, 2018 + * Author: Miquèl Raynal + */ + +#include +#include +#include +#include +#include +#include + +static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len) +{ + do_div(len, mtd->writesize); + + return len; +} + +static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->writesize); +} + +static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size) +{ + return !do_div(size, mtd->erasesize); +} + +static void mtd_dump_buf(const u8 *buf, uint len, uint offset) +{ + int i, j; + + for (i = 0; i < len; ) { + printf("0x%08x:\t", offset + i); + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf(" "); + i += 8; + for (j = 0; j < 8; j++) + printf("%02x ", buf[i + j]); + printf("\n"); + i += 8; + } +} + +static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off, + const u8 *buf, u64 len, bool woob) +{ + bool has_pages = mtd->type == MTD_NANDFLASH || + mtd->type == MTD_MLCNANDFLASH; + int npages = mtd_len_to_pages(mtd, len); + uint page; + + if (has_pages) { + for (page = 0; page < npages; page++) { + u64 data_off = page * mtd->writesize; + + printf("\nDump %d data bytes from 0x%08llx:\n", + mtd->writesize, start_off + data_off); + mtd_dump_buf(&buf[data_off], + mtd->writesize, start_off + data_off); + + if (woob) { + u64 oob_off = page * mtd->oobsize; + + printf("Dump %d OOB bytes from page at 0x%08llx:\n", + mtd->oobsize, start_off + data_off); + mtd_dump_buf(&buf[len + oob_off], + mtd->oobsize, 0); + } + } + } else { + printf("\nDump %lld data bytes from 0x%llx:\n", + len, start_off); + mtd_dump_buf(buf, len, start_off); + } +} + +static void mtd_show_parts(struct mtd_info *mtd, int level) +{ + struct mtd_info *part; + int i; + + list_for_each_entry(part, &mtd->partitions, node) { + for (i = 0; i < level; i++) + printf("\t"); + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", + part->offset, part->offset + part->size, part->name); + + mtd_show_parts(part, level + 1); + } +} + +static void mtd_show_device(struct mtd_info *mtd) +{ + /* Device */ + printf("* %s\n", mtd->name); +#if defined(CONFIG_DM) + if (mtd->dev) { + printf(" - device: %s\n", mtd->dev->name); + printf(" - parent: %s\n", mtd->dev->parent->name); + printf(" - driver: %s\n", mtd->dev->driver->name); + } +#endif + + /* MTD device information */ + printf(" - type: "); + switch (mtd->type) { + case MTD_RAM: + printf("RAM\n"); + break; + case MTD_ROM: + printf("ROM\n"); + break; + case MTD_NORFLASH: + printf("NOR flash\n"); + break; + case MTD_NANDFLASH: + printf("NAND flash\n"); + break; + case MTD_DATAFLASH: + printf("Data flash\n"); + break; + case MTD_UBIVOLUME: + printf("UBI volume\n"); + break; + case MTD_MLCNANDFLASH: + printf("MLC NAND flash\n"); + break; + case MTD_ABSENT: + default: + printf("Unknown\n"); + break; + } + + printf(" - block size: 0x%x bytes\n", mtd->erasesize); + printf(" - min I/O: 0x%x bytes\n", mtd->writesize); + + if (mtd->oobsize) { + printf(" - OOB size: %u bytes\n", mtd->oobsize); + printf(" - OOB available: %u bytes\n", mtd->oobavail); + } + + if (mtd->ecc_strength) { + printf(" - ECC strength: %u bits\n", mtd->ecc_strength); + printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size); + printf(" - bitflip threshold: %u bits\n", + mtd->bitflip_threshold); + } + + printf(" - 0x%012llx-0x%012llx : \"%s\"\n", + mtd->offset, mtd->offset + mtd->size, mtd->name); + + /* MTD partitions, if any */ + mtd_show_parts(mtd, 1); +} + +/* Logic taken from fs/ubifs/recovery.c:is_empty() */ +static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op) +{ + int i; + + for (i = 0; i < op->len; i++) + if (op->datbuf[i] != 0xff) + return false; + + for (i = 0; i < op->ooblen; i++) + if (op->oobbuf[i] != 0xff) + return false; + + return true; +} + +static int do_mtd_list(void) +{ + struct mtd_info *mtd; + int dev_nb = 0; + + /* Ensure all devices (and their partitions) are probed */ + mtd_probe_devices(); + + printf("List of MTD devices:\n"); + mtd_for_each_device(mtd) { + if (!mtd_is_partition(mtd)) + mtd_show_device(mtd); + + dev_nb++; + } + + if (!dev_nb) { + printf("No MTD device found\n"); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int mtd_special_write_oob(struct mtd_info *mtd, u64 off, + struct mtd_oob_ops *io_op, + bool write_empty_pages, bool woob) +{ + int ret = 0; + + /* + * By default, do not write an empty page. + * Skip it by simulating a successful write. + */ + if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) { + io_op->retlen = mtd->writesize; + io_op->oobretlen = woob ? mtd->oobsize : 0; + } else { + ret = mtd_write_oob(mtd, off, io_op); + } + + return ret; +} + +static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct mtd_info *mtd; + const char *cmd; + char *mtd_name; + + /* All MTD commands need at least two arguments */ + if (argc < 2) + return CMD_RET_USAGE; + + /* Parse the command name and its optional suffixes */ + cmd = argv[1]; + + /* List the MTD devices if that is what the user wants */ + if (strcmp(cmd, "list") == 0) + return do_mtd_list(); + + /* + * The remaining commands require also at least a device ID. + * Check the selected device is valid. Ensure it is probed. + */ + if (argc < 3) + return CMD_RET_USAGE; + + mtd_name = argv[2]; + mtd_probe_devices(); + mtd = get_mtd_device_nm(mtd_name); + if (IS_ERR_OR_NULL(mtd)) { + printf("MTD device %s not found, ret %ld\n", + mtd_name, PTR_ERR(mtd)); + return CMD_RET_FAILURE; + } + put_mtd_device(mtd); + + argc -= 3; + argv += 3; + + /* Do the parsing */ + if (!strncmp(cmd, "read", 4) || !strncmp(cmd, "dump", 4) || + !strncmp(cmd, "write", 5)) { + bool has_pages = mtd->type == MTD_NANDFLASH || + mtd->type == MTD_MLCNANDFLASH; + bool dump, read, raw, woob, write_empty_pages; + struct mtd_oob_ops io_op = {}; + uint user_addr = 0, npages; + u64 start_off, off, len, remaining, default_len; + u32 oob_len; + u8 *buf; + int ret; + + dump = !strncmp(cmd, "dump", 4); + read = dump || !strncmp(cmd, "read", 4); + raw = strstr(cmd, ".raw"); + woob = strstr(cmd, ".oob"); + write_empty_pages = !has_pages || strstr(cmd, ".dontskipff"); + + if (!dump) { + if (!argc) + return CMD_RET_USAGE; + + user_addr = simple_strtoul(argv[0], NULL, 16); + argc--; + argv++; + } + + start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) { + printf("Offset not aligned with a page (0x%x)\n", + mtd->writesize); + return CMD_RET_FAILURE; + } + + default_len = dump ? mtd->writesize : mtd->size; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : + default_len; + if (!mtd_is_aligned_with_min_io_size(mtd, len)) { + len = round_up(len, mtd->writesize); + printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n", + mtd->writesize, len); + } + + remaining = len; + npages = mtd_len_to_pages(mtd, len); + oob_len = woob ? npages * mtd->oobsize : 0; + + if (dump) + buf = kmalloc(len + oob_len, GFP_KERNEL); + else + buf = map_sysmem(user_addr, 0); + + if (!buf) { + printf("Could not map/allocate the user buffer\n"); + return CMD_RET_FAILURE; + } + + if (has_pages) + printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n", + read ? "Reading" : "Writing", len, npages, start_off, + raw ? " [raw]" : "", woob ? " [oob]" : "", + !read && write_empty_pages ? " [dontskipff]" : ""); + else + printf("%s %lld byte(s) at offset 0x%08llx\n", + read ? "Reading" : "Writing", len, start_off); + + io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB; + io_op.len = has_pages ? mtd->writesize : len; + io_op.ooblen = woob ? mtd->oobsize : 0; + io_op.datbuf = buf; + io_op.oobbuf = woob ? &buf[len] : NULL; + + /* Search for the first good block after the given offset */ + off = start_off; + while (mtd_block_isbad(mtd, off)) + off += mtd->erasesize; + + /* Loop over the pages to do the actual read/write */ + while (remaining) { + /* Skip the block if it is bad */ + if (mtd_is_aligned_with_block_size(mtd, off) && + mtd_block_isbad(mtd, off)) { + off += mtd->erasesize; + continue; + } + + if (read) + ret = mtd_read_oob(mtd, off, &io_op); + else + ret = mtd_special_write_oob(mtd, off, &io_op, + write_empty_pages, + woob); + + if (ret) { + printf("Failure while %s at offset 0x%llx\n", + read ? "reading" : "writing", off); + return CMD_RET_FAILURE; + } + + off += io_op.retlen; + remaining -= io_op.retlen; + io_op.datbuf += io_op.retlen; + io_op.oobbuf += io_op.oobretlen; + } + + if (!ret && dump) + mtd_dump_device_buf(mtd, start_off, buf, len, woob); + + if (dump) + kfree(buf); + else + unmap_sysmem(buf); + + if (ret) { + printf("%s on %s failed with error %d\n", + read ? "Read" : "Write", mtd->name, ret); + return CMD_RET_FAILURE; + } + + } else if (!strcmp(cmd, "erase")) { + bool scrub = strstr(cmd, ".dontskipbad"); + struct erase_info erase_op = {}; + u64 off, len; + int ret; + + off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0; + len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size; + + if (!mtd_is_aligned_with_block_size(mtd, off)) { + printf("Offset not aligned with a block (0x%x)\n", + mtd->erasesize); + return CMD_RET_FAILURE; + } + + if (!mtd_is_aligned_with_block_size(mtd, len)) { + printf("Size not a multiple of a block (0x%x)\n", + mtd->erasesize); + return CMD_RET_FAILURE; + } + + printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n", + off, off + len - 1, mtd_div_by_eb(len, mtd)); + + erase_op.mtd = mtd; + erase_op.addr = off; + erase_op.len = len; + erase_op.scrub = scrub; + + while (erase_op.len) { + ret = mtd_erase(mtd, &erase_op); + + /* Abort if its not a bad block error */ + if (ret != -EIO) + break; + + printf("Skipping bad block at 0x%08llx\n", + erase_op.fail_addr); + + /* Skip bad block and continue behind it */ + erase_op.len -= erase_op.fail_addr - erase_op.addr; + erase_op.len -= mtd->erasesize; + erase_op.addr = erase_op.fail_addr + mtd->erasesize; + } + + if (ret && ret != -EIO) + return CMD_RET_FAILURE; + } else if (!strcmp(cmd, "bad")) { + loff_t off; + + if (!mtd_can_have_bb(mtd)) { + printf("Only NAND-based devices can have bad blocks\n"); + return CMD_RET_SUCCESS; + } + + printf("MTD device %s bad blocks list:\n", mtd->name); + for (off = 0; off < mtd->size; off += mtd->erasesize) + if (mtd_block_isbad(mtd, off)) + printf("\t0x%08llx\n", off); + } else { + return CMD_RET_USAGE; + } + + return CMD_RET_SUCCESS; +} + +static char mtd_help_text[] = +#ifdef CONFIG_SYS_LONGHELP + "- generic operations on memory technology devices\n\n" + "mtd list\n" + "mtd read[.raw][.oob] [ []]\n" + "mtd dump[.raw][.oob] [ []]\n" + "mtd write[.raw][.oob][.dontskipff] [ []]\n" + "mtd erase[.dontskipbad] [ []]\n" + "\n" + "Specific functions:\n" + "mtd bad \n" + "\n" + "With:\n" + "\t: NAND partition/chip name\n" + "\t: user address from/to which data will be retrieved/stored\n" + "\t: offset in in bytes (default: start of the part)\n" + "\t\t* must be block-aligned for erase\n" + "\t\t* must be page-aligned otherwise\n" + "\t: length of the operation in bytes (default: the entire device)\n" + "\t\t* must be a multiple of a block for erase\n" + "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n" + "\n" + "The .dontskipff option forces writing empty pages, don't use it if unsure.\n" +#endif + ""; + +U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text); diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index cdf515256a..22ceda93c0 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. -ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF))) +ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)$(CONFIG_CMD_SF)$(CONFIG_CMD_MTD))) obj-y += mtdcore.o mtd_uboot.o endif obj-$(CONFIG_MTD) += mtd-uclass.o diff --git a/drivers/mtd/mtd_uboot.c b/drivers/mtd/mtd_uboot.c index 8d7e7890b7..6a211d52ff 100644 --- a/drivers/mtd/mtd_uboot.c +++ b/drivers/mtd/mtd_uboot.c @@ -4,8 +4,15 @@ * Heiko Schocher, DENX Software Engineering, hs@denx.de. */ #include +#include +#include +#include /* LEGACY */ #include -#include /* Legacy */ +#include +#include + +#define MTD_NAME_MAX_LEN 20 + /** * mtd_search_alternate_name - Search an alternate name for @mtdname thanks to @@ -68,6 +75,158 @@ int mtd_search_alternate_name(const char *mtdname, char *altname, return -EINVAL; } +#if IS_ENABLED(CONFIG_MTD) +static void mtd_probe_uclass_mtd_devs(void) +{ + struct udevice *dev; + int idx = 0; + + /* Probe devices with DM compliant drivers */ + while (!uclass_find_device(UCLASS_MTD, idx, &dev) && dev) { + mtd_probe(dev); + idx++; + } +} +#else +static void mtd_probe_uclass_mtd_devs(void) { } +#endif + +#if defined(CONFIG_MTD_PARTITIONS) +int mtd_probe_devices(void) +{ + static char *old_mtdparts; + static char *old_mtdids; + const char *mtdparts = env_get("mtdparts"); + const char *mtdids = env_get("mtdids"); + bool remaining_partitions = true; + struct mtd_info *mtd; + + mtd_probe_uclass_mtd_devs(); + + /* Check if mtdparts/mtdids changed since last call, otherwise: exit */ + if (!strcmp(mtdparts, old_mtdparts) && !strcmp(mtdids, old_mtdids)) + return 0; + + /* Update the local copy of mtdparts */ + free(old_mtdparts); + free(old_mtdids); + old_mtdparts = strdup(mtdparts); + old_mtdids = strdup(mtdids); + + /* If at least one partition is still in use, do not delete anything */ + mtd_for_each_device(mtd) { + if (mtd->usecount) { + printf("Partition \"%s\" already in use, aborting\n", + mtd->name); + return -EACCES; + } + } + + /* + * Everything looks clear, remove all partitions. It is not safe to + * remove entries from the mtd_for_each_device loop as it uses idr + * indexes and the partitions removal is done in bulk (all partitions of + * one device at the same time), so break and iterate from start each + * time a new partition is found and deleted. + */ + while (remaining_partitions) { + remaining_partitions = false; + mtd_for_each_device(mtd) { + if (!mtd_is_partition(mtd) && mtd_has_partitions(mtd)) { + del_mtd_partitions(mtd); + remaining_partitions = true; + break; + } + } + } + + /* Start the parsing by ignoring the extra 'mtdparts=' prefix, if any */ + if (strstr(mtdparts, "mtdparts=")) + mtdparts += 9; + + /* For each MTD device in mtdparts */ + while (mtdparts[0] != '\0') { + char mtd_name[MTD_NAME_MAX_LEN], *colon; + struct mtd_partition *parts; + int mtd_name_len, nparts; + int ret; + + colon = strchr(mtdparts, ':'); + if (!colon) { + printf("Wrong mtdparts: %s\n", mtdparts); + return -EINVAL; + } + + mtd_name_len = colon - mtdparts; + strncpy(mtd_name, mtdparts, mtd_name_len); + mtd_name[mtd_name_len] = '\0'; + /* Move the pointer forward (including the ':') */ + mtdparts += mtd_name_len + 1; + mtd = get_mtd_device_nm(mtd_name); + if (IS_ERR_OR_NULL(mtd)) { + char linux_name[MTD_NAME_MAX_LEN]; + + /* + * The MTD device named "mtd_name" does not exist. Try + * to find a correspondance with an MTD device having + * the same type and number as defined in the mtdids. + */ + debug("No device named %s\n", mtd_name); + ret = mtd_search_alternate_name(mtd_name, linux_name, + MTD_NAME_MAX_LEN); + if (!ret) + mtd = get_mtd_device_nm(linux_name); + + /* + * If no device could be found, move the mtdparts + * pointer forward until the next set of partitions. + */ + if (ret || IS_ERR_OR_NULL(mtd)) { + printf("Could not find a valid device for %s\n", + mtd_name); + mtdparts = strchr(mtdparts, ';'); + if (mtdparts) + mtdparts++; + + continue; + } + } + + /* + * Parse the MTD device partitions. It will update the mtdparts + * pointer, create an array of parts (that must be freed), and + * return the number of partition structures in the array. + */ + ret = mtd_parse_partitions(mtd, &mtdparts, &parts, &nparts); + if (ret) { + printf("Could not parse device %s\n", mtd->name); + put_mtd_device(mtd); + return -EINVAL; + } + + if (!nparts) + continue; + + /* Create the new MTD partitions */ + add_mtd_partitions(mtd, parts, nparts); + + /* Free the structures allocated during the parsing */ + mtd_free_parsed_partitions(parts, nparts); + + put_mtd_device(mtd); + } + + return 0; +} +#else +int mtd_probe_devices(void) +{ + mtd_probe_uclass_mtd_devs(); + + return 0; +} +#endif /* defined(CONFIG_MTD_PARTITIONS) */ + /* Legacy */ static int get_part(const char *partname, int *idx, loff_t *off, loff_t *size, diff --git a/include/mtd.h b/include/mtd.h index 75aca262d4..65fcd3c700 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -9,5 +9,6 @@ #include int mtd_probe(struct udevice *dev); +int mtd_probe_devices(void); #endif /* _MTD_H_ */ -- cgit v1.2.3 From c58fb2cdb3e4abb8c2714b351b3856661573c747 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:29 +0200 Subject: cmd: ubi: clean the partition handling UBI should not mess with MTD partitions, now that the partitions are handled in a clean way, clean the ubi command and avoid using this uneeded extra-glue to reference the devices. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- cmd/Kconfig | 2 ++ cmd/ubi.c | 96 ++++++++++++++++--------------------------------------------- 2 files changed, 27 insertions(+), 71 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index c9be85989c..b571e84cc1 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1840,6 +1840,8 @@ config CMD_UBI capabilities. Please, consult the MTD web site for more details (www.linux-mtd.infradead.org). Activate this option if you want to use U-Boot UBI commands. + It is also strongly encouraged to also enable CONFIG_MTD to get full + partition support. config CMD_UBIFS tristate "Enable UBIFS - Unsorted block images filesystem commands" diff --git a/cmd/ubi.c b/cmd/ubi.c index 0a3405a3b1..bb8f97fc28 100644 --- a/cmd/ubi.c +++ b/cmd/ubi.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -29,17 +30,6 @@ /* Private own data */ static struct ubi_device *ubi; -static char buffer[80]; -static int ubi_initialized; - -struct selected_dev { - char part_name[80]; - int selected; - int nr; - struct mtd_info *mtd_info; -}; - -static struct selected_dev ubi_dev; #ifdef CONFIG_CMD_UBIFS int ubifs_is_mounted(void); @@ -404,43 +394,24 @@ int ubi_volume_read(char *volume, char *buf, size_t size) return err; } -static int ubi_dev_scan(struct mtd_info *info, char *ubidev, - const char *vid_header_offset) +static int ubi_dev_scan(struct mtd_info *info, const char *vid_header_offset) { - struct mtd_device *dev; - struct part_info *part; - struct mtd_partition mtd_part; char ubi_mtd_param_buffer[80]; - u8 pnum; int err; - if (find_dev_and_part(ubidev, &dev, &pnum, &part) != 0) - return 1; + if (!vid_header_offset) + sprintf(ubi_mtd_param_buffer, "%s", info->name); + else + sprintf(ubi_mtd_param_buffer, "%s,%s", info->name, + vid_header_offset); - sprintf(buffer, "mtd=%d", pnum); - memset(&mtd_part, 0, sizeof(mtd_part)); - mtd_part.name = buffer; - mtd_part.size = part->size; - mtd_part.offset = part->offset; - add_mtd_partitions(info, &mtd_part, 1); - - strcpy(ubi_mtd_param_buffer, buffer); - if (vid_header_offset) - sprintf(ubi_mtd_param_buffer, "mtd=%d,%s", pnum, - vid_header_offset); err = ubi_mtd_param_parse(ubi_mtd_param_buffer, NULL); - if (err) { - del_mtd_partitions(info); + if (err) return -err; - } err = ubi_init(); - if (err) { - del_mtd_partitions(info); + if (err) return -err; - } - - ubi_initialized = 1; return 0; } @@ -465,50 +436,33 @@ int ubi_detach(void) /* * Call ubi_exit() before re-initializing the UBI subsystem */ - if (ubi_initialized) { + if (ubi) ubi_exit(); - del_mtd_partitions(ubi_dev.mtd_info); - ubi_initialized = 0; - } - ubi_dev.selected = 0; + ubi = NULL; + return 0; } int ubi_part(char *part_name, const char *vid_header_offset) { + struct mtd_info *mtd; int err = 0; - char mtd_dev[16]; - struct mtd_device *dev; - struct part_info *part; - u8 pnum; ubi_detach(); - /* - * Search the mtd device number where this partition - * is located - */ - if (find_dev_and_part(part_name, &dev, &pnum, &part)) { + + mtd_probe_devices(); + mtd = get_mtd_device_nm(part_name); + if (IS_ERR(mtd)) { printf("Partition %s not found!\n", part_name); return 1; } - sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(dev->id->type), dev->id->num); - ubi_dev.mtd_info = get_mtd_device_nm(mtd_dev); - if (IS_ERR(ubi_dev.mtd_info)) { - printf("Partition %s not found on device %s!\n", part_name, - mtd_dev); - return 1; - } + put_mtd_device(mtd); - ubi_dev.selected = 1; - - strcpy(ubi_dev.part_name, part_name); - err = ubi_dev_scan(ubi_dev.mtd_info, ubi_dev.part_name, - vid_header_offset); + err = ubi_dev_scan(mtd, vid_header_offset); if (err) { printf("UBI init error %d\n", err); printf("Please check, if the correct MTD partition is used (size big enough?)\n"); - ubi_dev.selected = 0; return err; } @@ -539,13 +493,13 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) /* Print current partition */ if (argc == 2) { - if (!ubi_dev.selected) { - printf("Error, no UBI device/partition selected!\n"); + if (!ubi) { + printf("Error, no UBI device selected!\n"); return 1; } - printf("Device %d: %s, partition %s\n", - ubi_dev.nr, ubi_dev.mtd_info->name, ubi_dev.part_name); + printf("Device %d: %s, MTD partition %s\n", + ubi->ubi_num, ubi->ubi_name, ubi->mtd->name); return 0; } @@ -558,8 +512,8 @@ static int do_ubi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return ubi_part(argv[2], vid_header_offset); } - if ((strcmp(argv[1], "part") != 0) && (!ubi_dev.selected)) { - printf("Error, no UBI device/partition selected!\n"); + if ((strcmp(argv[1], "part") != 0) && !ubi) { + printf("Error, no UBI device selected!\n"); return 1; } -- cgit v1.2.3 From 938db6fe5da3581ed2c17d64c7e016a7a8df5237 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:30 +0200 Subject: cmd: mtdparts: describe as legacy The 'mtdparts' command is not needed anymore. While the environment variable is still valid (and useful, along with the 'mtdids' one), the command has been replaced by 'mtd' which is much more close to the MTD stack and do not add its own specific glue. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- cmd/Kconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index b571e84cc1..12d19fd675 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1699,7 +1699,11 @@ config CMD_MTDPARTS bool "MTD partition support" select MTD_DEVICE if (CMD_NAND || NAND) help - MTD partition support + MTD partitioning tool support. + It is strongly encouraged to avoid using this command + anymore along with 'sf', 'nand', 'onenand'. One can still + declare the partitions in the mtdparts environment variable + but better use the MTD stack and the 'mtd' command instead. config MTDIDS_DEFAULT string "Default MTD IDs" -- cgit v1.2.3 From 6ac5909f51fb4e50604f1a89d0535aefcc51f476 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Fri, 7 Sep 2018 14:25:29 +0800 Subject: spi: designware_spi: Add reset ctrl to driver Add code to reset all reset signals as in SPI DT node. A reset property is an optional feature, so only print out a warning and do not fail if a reset property is not present. If a reset property is discovered, then use it to deassert, thus bringing the IP out of reset. Release reset when _remove(). Signed-off-by: Ley Foon Tan Acked-by: Marek Vasut Reviewed-by: Jagan Teki --- drivers/spi/designware_spi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/spi/designware_spi.c b/drivers/spi/designware_spi.c index d8b73ea326..5cca414486 100644 --- a/drivers/spi/designware_spi.c +++ b/drivers/spi/designware_spi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,8 @@ struct dw_spi_priv { void *tx_end; void *rx; void *rx_end; + + struct reset_ctl_bulk resets; }; static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset) @@ -231,6 +234,34 @@ err_rate: return -EINVAL; } +static int dw_spi_reset(struct udevice *bus) +{ + int ret; + struct dw_spi_priv *priv = dev_get_priv(bus); + + ret = reset_get_bulk(bus, &priv->resets); + if (ret) { + /* + * Return 0 if error due to !CONFIG_DM_RESET and reset + * DT property is not present. + */ + if (ret == -ENOENT || ret == -ENOTSUPP) + return 0; + + dev_warn(bus, "Can't get reset: %d\n", ret); + return ret; + } + + ret = reset_deassert_bulk(&priv->resets); + if (ret) { + reset_release_bulk(&priv->resets); + dev_err(bus, "Failed to reset: %d\n", ret); + return ret; + } + + return 0; +} + static int dw_spi_probe(struct udevice *bus) { struct dw_spi_platdata *plat = dev_get_platdata(bus); @@ -244,6 +275,10 @@ static int dw_spi_probe(struct udevice *bus) if (ret) return ret; + ret = dw_spi_reset(bus); + if (ret) + return ret; + /* Currently only bits_per_word == 8 supported */ priv->bits_per_word = 8; @@ -478,6 +513,13 @@ static int dw_spi_set_mode(struct udevice *bus, uint mode) return 0; } +static int dw_spi_remove(struct udevice *bus) +{ + struct dw_spi_priv *priv = dev_get_priv(bus); + + return reset_release_bulk(&priv->resets); +} + static const struct dm_spi_ops dw_spi_ops = { .xfer = dw_spi_xfer, .set_speed = dw_spi_set_speed, @@ -502,4 +544,5 @@ U_BOOT_DRIVER(dw_spi) = { .platdata_auto_alloc_size = sizeof(struct dw_spi_platdata), .priv_auto_alloc_size = sizeof(struct dw_spi_priv), .probe = dw_spi_probe, + .remove = dw_spi_remove, }; -- cgit v1.2.3 From 4ccb2f21153fdaab55b189117b9cbeeb233324e1 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Fri, 7 Sep 2018 09:49:34 +0530 Subject: driver/spi: fsl_qspi: Remove non-DM stuff Convert fsl_qspi.c to complete DM mode. Signed-off-by: Ashish Kumar Tested-by: Rajat Srivastava Tested-by: Ye Li Reviewed-by: Jagan Teki --- drivers/spi/fsl_qspi.c | 138 ------------------------------------------------- 1 file changed, 138 deletions(-) diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index 197f41f9db..1598c4f698 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -84,7 +84,6 @@ DECLARE_GLOBAL_DATA_PTR; /* QSPI max chipselect signals number */ #define FSL_QSPI_MAX_CHIPSELECT_NUM 4 -#ifdef CONFIG_DM_SPI /** * struct fsl_qspi_platdata - platform data for Freescale QSPI * @@ -105,7 +104,6 @@ struct fsl_qspi_platdata { u32 flash_num; u32 num_chipselect; }; -#endif /** * struct fsl_qspi_priv - private data for Freescale QSPI @@ -136,12 +134,6 @@ struct fsl_qspi_priv { struct fsl_qspi_regs *regs; }; -#ifndef CONFIG_DM_SPI -struct fsl_qspi { - struct spi_slave slave; - struct fsl_qspi_priv priv; -}; -#endif static u32 qspi_read32(u32 flags, u32 *addr) { @@ -869,136 +861,7 @@ void qspi_cfg_smpr(struct fsl_qspi_priv *priv, u32 clear_bits, u32 set_bits) smpr_val |= set_bits; qspi_write32(priv->flags, &priv->regs->smpr, smpr_val); } -#ifndef CONFIG_DM_SPI -static unsigned long spi_bases[] = { - QSPI0_BASE_ADDR, -#ifdef CONFIG_MX6SX - QSPI1_BASE_ADDR, -#endif -}; - -static unsigned long amba_bases[] = { - QSPI0_AMBA_BASE, -#ifdef CONFIG_MX6SX - QSPI1_AMBA_BASE, -#endif -}; - -static inline struct fsl_qspi *to_qspi_spi(struct spi_slave *slave) -{ - return container_of(slave, struct fsl_qspi, slave); -} - -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - u32 mcr_val; - struct fsl_qspi *qspi; - struct fsl_qspi_regs *regs; - u32 total_size; - - if (bus >= ARRAY_SIZE(spi_bases)) - return NULL; - - if (cs >= FSL_QSPI_FLASH_NUM) - return NULL; - - qspi = spi_alloc_slave(struct fsl_qspi, bus, cs); - if (!qspi) - return NULL; - -#ifdef CONFIG_SYS_FSL_QSPI_BE - qspi->priv.flags |= QSPI_FLAG_REGMAP_ENDIAN_BIG; -#endif - - regs = (struct fsl_qspi_regs *)spi_bases[bus]; - qspi->priv.regs = regs; - /* - * According cs, use different amba_base to choose the - * corresponding flash devices. - * - * If not, only one flash device is used even if passing - * different cs using `sf probe` - */ - qspi->priv.cur_amba_base = amba_bases[bus] + cs * FSL_QSPI_FLASH_SIZE; - - qspi->slave.max_write_size = TX_BUFFER_SIZE; - - mcr_val = qspi_read32(qspi->priv.flags, ®s->mcr); - - /* Set endianness to LE for i.mx */ - if (IS_ENABLED(CONFIG_MX6) || IS_ENABLED(CONFIG_MX7)) - mcr_val = QSPI_MCR_END_CFD_LE; - - qspi_write32(qspi->priv.flags, ®s->mcr, - QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK | - (mcr_val & QSPI_MCR_END_CFD_MASK)); - - qspi_cfg_smpr(&qspi->priv, - ~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_DDRSMP_MASK | - QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK), 0); - - total_size = FSL_QSPI_FLASH_SIZE * FSL_QSPI_FLASH_NUM; - /* - * Any read access to non-implemented addresses will provide - * undefined results. - * - * In case single die flash devices, TOP_ADDR_MEMA2 and - * TOP_ADDR_MEMB2 should be initialized/programmed to - * TOP_ADDR_MEMA1 and TOP_ADDR_MEMB1 respectively - in effect, - * setting the size of these devices to 0. This would ensure - * that the complete memory map is assigned to only one flash device. - */ - qspi_write32(qspi->priv.flags, ®s->sfa1ad, - FSL_QSPI_FLASH_SIZE | amba_bases[bus]); - qspi_write32(qspi->priv.flags, ®s->sfa2ad, - FSL_QSPI_FLASH_SIZE | amba_bases[bus]); - qspi_write32(qspi->priv.flags, ®s->sfb1ad, - total_size | amba_bases[bus]); - qspi_write32(qspi->priv.flags, ®s->sfb2ad, - total_size | amba_bases[bus]); - - qspi_set_lut(&qspi->priv); - -#ifdef CONFIG_SYS_FSL_QSPI_AHB - qspi_init_ahb_read(&qspi->priv); -#endif - - qspi_module_disable(&qspi->priv, 0); - - return &qspi->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct fsl_qspi *qspi = to_qspi_spi(slave); - - free(qspi); -} -int spi_claim_bus(struct spi_slave *slave) -{ - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ - /* Nothing to do */ -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *dout, void *din, unsigned long flags) -{ - struct fsl_qspi *qspi = to_qspi_spi(slave); - - return qspi_xfer(&qspi->priv, bitlen, dout, din, flags); -} - -void spi_init(void) -{ - /* Nothing to do */ -} -#else static int fsl_qspi_child_pre_probe(struct udevice *dev) { struct spi_slave *slave = dev_get_parent_priv(dev); @@ -1265,4 +1128,3 @@ U_BOOT_DRIVER(fsl_qspi) = { .probe = fsl_qspi_probe, .child_pre_probe = fsl_qspi_child_pre_probe, }; -#endif -- cgit v1.2.3 From b3bec2525604d6b42bb9e7fd719c84b022447db3 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 25 Aug 2018 19:34:24 +0200 Subject: spi: sh_qspi: Add DM support to SH QSPI driver Add DM support to the SH QSPI driver while retaining non-DM support. The later is required as this driver is used in SPL which has a size limitation of 16 kiB. Signed-off-by: Marek Vasut Cc: Nobuhiro Iwamatsu [jagan: use proper commit head] Reviewed-by: Jagan Teki --- drivers/spi/sh_qspi.c | 215 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 65 deletions(-) diff --git a/drivers/spi/sh_qspi.c b/drivers/spi/sh_qspi.c index e9123e2c39..64dfd748d6 100644 --- a/drivers/spi/sh_qspi.c +++ b/drivers/spi/sh_qspi.c @@ -67,15 +67,12 @@ struct sh_qspi_regs { }; struct sh_qspi_slave { +#ifndef CONFIG_DM_SPI struct spi_slave slave; +#endif struct sh_qspi_regs *regs; }; -static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave) -{ - return container_of(slave, struct sh_qspi_slave, slave); -} - static void sh_qspi_init(struct sh_qspi_slave *ss) { /* QSPI initialize */ @@ -119,15 +116,8 @@ static void sh_qspi_init(struct sh_qspi_slave *ss) setbits_8(&ss->regs->spcr, SPCR_SPE); } -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - return 1; -} - -void spi_cs_activate(struct spi_slave *slave) +static void sh_qspi_cs_activate(struct sh_qspi_slave *ss) { - struct sh_qspi_slave *ss = to_sh_qspi(slave); - /* Set master mode only */ writeb(SPCR_MSTR, &ss->regs->spcr); @@ -147,61 +137,15 @@ void spi_cs_activate(struct spi_slave *slave) setbits_8(&ss->regs->spcr, SPCR_SPE); } -void spi_cs_deactivate(struct spi_slave *slave) +static void sh_qspi_cs_deactivate(struct sh_qspi_slave *ss) { - struct sh_qspi_slave *ss = to_sh_qspi(slave); - /* Disable SPI Function */ clrbits_8(&ss->regs->spcr, SPCR_SPE); } -void spi_init(void) -{ - /* nothing to do */ -} - -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct sh_qspi_slave *ss; - - if (!spi_cs_is_valid(bus, cs)) - return NULL; - - ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs); - if (!ss) { - printf("SPI_error: Fail to allocate sh_qspi_slave\n"); - return NULL; - } - - ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE; - - /* Init SH QSPI */ - sh_qspi_init(ss); - - return &ss->slave; -} - -void spi_free_slave(struct spi_slave *slave) +static int sh_qspi_xfer_common(struct sh_qspi_slave *ss, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) { - struct sh_qspi_slave *spi = to_sh_qspi(slave); - - free(spi); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) -{ - struct sh_qspi_slave *ss = to_sh_qspi(slave); u32 nbyte, chunk; int i, ret = 0; u8 dtdata = 0, drdata; @@ -210,7 +154,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, if (dout == NULL && din == NULL) { if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + sh_qspi_cs_deactivate(ss); return 0; } @@ -222,7 +166,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, nbyte = bitlen / 8; if (flags & SPI_XFER_BEGIN) { - spi_cs_activate(slave); + sh_qspi_cs_activate(ss); /* Set 1048576 byte */ writel(0x100000, spbmul0); @@ -273,7 +217,148 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, } if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + sh_qspi_cs_deactivate(ss); return ret; } + +#ifndef CONFIG_DM_SPI +static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave) +{ + return container_of(slave, struct sh_qspi_slave, slave); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + + sh_qspi_cs_activate(ss); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + + sh_qspi_cs_deactivate(ss); +} + +void spi_init(void) +{ + /* nothing to do */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct sh_qspi_slave *ss; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs); + if (!ss) { + printf("SPI_error: Fail to allocate sh_qspi_slave\n"); + return NULL; + } + + ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE; + + /* Init SH QSPI */ + sh_qspi_init(ss); + + return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct sh_qspi_slave *spi = to_sh_qspi(slave); + + free(spi); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct sh_qspi_slave *ss = to_sh_qspi(slave); + + return sh_qspi_xfer_common(ss, bitlen, dout, din, flags); +} + +#else + +#include + +static int sh_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct sh_qspi_slave *ss = dev_get_platdata(bus); + + return sh_qspi_xfer_common(ss, bitlen, dout, din, flags); +} + +static int sh_qspi_set_speed(struct udevice *dev, uint speed) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int sh_qspi_set_mode(struct udevice *dev, uint mode) +{ + /* This is a SPI NOR controller, do nothing. */ + return 0; +} + +static int sh_qspi_probe(struct udevice *dev) +{ + struct sh_qspi_slave *ss = dev_get_platdata(dev); + + sh_qspi_init(ss); + + return 0; +} + +static int sh_qspi_ofdata_to_platdata(struct udevice *dev) +{ + struct sh_qspi_slave *plat = dev_get_platdata(dev); + + plat->regs = (struct sh_qspi_regs *)dev_read_addr(dev); + + return 0; +} + +static const struct dm_spi_ops sh_qspi_ops = { + .xfer = sh_qspi_xfer, + .set_speed = sh_qspi_set_speed, + .set_mode = sh_qspi_set_mode, +}; + +static const struct udevice_id sh_qspi_ids[] = { + { .compatible = "renesas,qspi" }, + { } +}; + +U_BOOT_DRIVER(sh_qspi) = { + .name = "sh_qspi", + .id = UCLASS_SPI, + .of_match = sh_qspi_ids, + .ops = &sh_qspi_ops, + .ofdata_to_platdata = sh_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct sh_qspi_slave), + .probe = sh_qspi_probe, +}; +#endif -- cgit v1.2.3