diff options
Diffstat (limited to 'drivers/mtd/nand/spi')
| -rw-r--r-- | drivers/mtd/nand/spi/Makefile | 5 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/alliancememory.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/ato.c | 14 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/core.c | 595 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/dosilicon.c | 91 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/esmt.c | 130 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/fmsh.c | 146 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/foresee.c | 50 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/gigadevice.c | 187 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/macronix.c | 142 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/micron.c | 171 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/otp.c | 362 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/paragon.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/skyhigh.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/toshiba.c | 23 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/winbond.c | 378 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/xtx.c | 20 |
17 files changed, 2103 insertions, 271 deletions
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 1e61ab21893a..a47bd22cd309 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o alliancememory.o ato.o esmt.o foresee.o gigadevice.o macronix.o -spinand-objs += micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o +spinand-objs := core.o otp.o +spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o +spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/alliancememory.c b/drivers/mtd/nand/spi/alliancememory.c index 6046c73f8424..9e97c40955c9 100644 --- a/drivers/mtd/nand/spi/alliancememory.c +++ b/drivers/mtd/nand/spi/alliancememory.c @@ -17,20 +17,20 @@ #define AM_STATUS_ECC_MAX_CORRECTED (3 << 4) static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int am_get_eccsize(struct mtd_info *mtd) { diff --git a/drivers/mtd/nand/spi/ato.c b/drivers/mtd/nand/spi/ato.c index bb5298911137..45d38ce0736c 100644 --- a/drivers/mtd/nand/spi/ato.c +++ b/drivers/mtd/nand/spi/ato.c @@ -14,17 +14,17 @@ static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int ato25d1ga_ooblayout_ecc(struct mtd_info *mtd, int section, diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index da4713692674..0b076790bd9d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -20,10 +20,111 @@ #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> -static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) +static struct spi_mem_op +spinand_fill_reset_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg, - spinand->scratchbuf); + return spinand->op_templates->reset; +} + +static struct spi_mem_op +spinand_fill_readid_op(struct spinand_device *spinand, + u8 naddr, u8 ndummy, void *buf, unsigned int len) +{ + struct spi_mem_op op = spinand->op_templates->readid; + + op.addr.nbytes = naddr; + op.dummy.nbytes = ndummy; + op.data.buf.in = buf; + op.data.nbytes = len; + + return op; +} + +struct spi_mem_op +spinand_fill_wr_en_op(struct spinand_device *spinand) +{ + return spinand->op_templates->wr_en; +} + +static __maybe_unused struct spi_mem_op +spinand_fill_wr_dis_op(struct spinand_device *spinand) +{ + return spinand->op_templates->wr_dis; +} + +struct spi_mem_op +spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr) +{ + struct spi_mem_op op = spinand->op_templates->set_feature; + + if (op.cmd.dtr && op.cmd.buswidth == 8) + reg |= reg << 8; + + op.addr.val = reg; + op.data.buf.out = valptr; + + return op; +} + +struct spi_mem_op +spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr) +{ + struct spi_mem_op op = spinand->op_templates->get_feature; + + if (op.cmd.dtr && op.cmd.buswidth == 8) + reg |= reg << 8; + + op.addr.val = reg; + op.data.buf.in = valptr; + + return op; +} + +static struct spi_mem_op +spinand_fill_blk_erase_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->blk_erase; + + op.addr.val = addr; + + return op; +} + +static struct spi_mem_op +spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->page_read; + + op.addr.val = addr; + + return op; +} + +static struct spi_mem_op +spinand_fill_page_read_packed_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->page_read; + + op.cmd.opcode |= addr >> 16; + op.addr.val = addr & 0xFFFF; + + return op; +} + +struct spi_mem_op +spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->prog_exec; + + op.addr.val = addr; + + return op; +} + +int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) +{ + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + reg, spinand->scratchbuf); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -36,8 +137,8 @@ static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) 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); + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + reg, spinand->scratchbuf); *spinand->scratchbuf = val; return spi_mem_exec_op(spinand->spimem, &op); @@ -177,18 +278,9 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand) return 0; } -static int spinand_init_quad_enable(struct spinand_device *spinand) +static int spinand_init_quad_enable(struct spinand_device *spinand, + bool enable) { - 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); } @@ -269,7 +361,7 @@ static int spinand_ondie_ecc_init_ctx(struct nand_device *nand) nand->ecc.ctx.conf.step_size = nand->ecc.requirements.step_size; nand->ecc.ctx.conf.strength = nand->ecc.requirements.strength; - engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + engine_conf = kzalloc_obj(*engine_conf); if (!engine_conf) return -ENOMEM; @@ -360,9 +452,9 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status) engine_conf->status = status; } -static int spinand_write_enable_op(struct spinand_device *spinand) +int spinand_write_enable_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); + struct spi_mem_op op = SPINAND_OP(spinand, wr_en); return spi_mem_exec_op(spinand->spimem, &op); } @@ -372,7 +464,10 @@ static int spinand_load_page_op(struct spinand_device *spinand, { 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); + bool packed = spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ; + struct spi_mem_op op = packed ? + SPINAND_OP(spinand, page_read_packed, row) : + SPINAND_OP(spinand, page_read, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -430,8 +525,16 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, * Dirmap accesses are allowed to toggle the CS. * Toggling the CS during a continuous read is forbidden. */ - if (nbytes && req->continuous) - return -EIO; + if (nbytes && req->continuous) { + /* + * Spi controller with broken support of continuous + * reading was detected. Disable future use of + * continuous reading and return -EAGAIN to retry + * reading within regular mode. + */ + spinand->cont_read_possible = false; + return -EAGAIN; + } } if (req->datalen) @@ -519,7 +622,7 @@ static int spinand_program_op(struct spinand_device *spinand, { 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); + struct spi_mem_op op = SPINAND_OP(spinand, prog_exec, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -529,18 +632,28 @@ static int spinand_erase_op(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); unsigned int row = nanddev_pos_to_row(nand, pos); - struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row); + struct spi_mem_op op = SPINAND_OP(spinand, blk_erase, row); return spi_mem_exec_op(spinand->spimem, &op); } -static int spinand_wait(struct spinand_device *spinand, - unsigned long initial_delay_us, - unsigned long poll_delay_us, - u8 *s) +/** + * spinand_wait() - Poll memory device status + * @spinand: the spinand device + * @initial_delay_us: delay in us before starting to poll + * @poll_delay_us: time to sleep between reads in us + * @s: the pointer to variable to store the value of REG_STATUS + * + * This function polls a status register (REG_STATUS) and returns when + * the STATUS_READY bit is 0 or when the timeout has expired. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us, + unsigned long poll_delay_us, u8 *s) { - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(REG_STATUS, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + REG_STATUS, spinand->scratchbuf); u8 status; int ret; @@ -573,8 +686,8 @@ out: static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_OP( - naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + struct spi_mem_op op = SPINAND_OP(spinand, readid, + naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -586,7 +699,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, static int spinand_reset_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_RESET_OP; + struct spi_mem_op op = SPINAND_OP(spinand, reset); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -604,8 +717,16 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock) return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); } -static int spinand_read_page(struct spinand_device *spinand, - const struct nand_page_io_req *req) +/** + * spinand_read_page() - Read a page + * @spinand: the spinand device + * @req: the I/O request + * + * Return: 0 or a positive number of bitflips corrected on success. + * A negative error code otherwise. + */ +int spinand_read_page(struct spinand_device *spinand, + const struct nand_page_io_req *req) { struct nand_device *nand = spinand_to_nand(spinand); u8 status; @@ -635,8 +756,16 @@ static int spinand_read_page(struct spinand_device *spinand, return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req); } -static int spinand_write_page(struct spinand_device *spinand, - const struct nand_page_io_req *req) +/** + * spinand_write_page() - Write a page + * @spinand: the spinand device + * @req: the I/O request + * + * Return: 0 or a positive number of bitflips corrected on success. + * A negative error code otherwise. + */ +int spinand_write_page(struct spinand_device *spinand, + const struct nand_page_io_req *req) { struct nand_device *nand = spinand_to_nand(spinand); u8 status; @@ -662,7 +791,10 @@ static int spinand_write_page(struct spinand_device *spinand, SPINAND_WRITE_INITIAL_DELAY_US, SPINAND_WRITE_POLL_DELAY_US, &status); - if (!ret && (status & STATUS_PROG_FAILED)) + if (ret) + return ret; + + if (status & STATUS_PROG_FAILED) return -EIO; return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req); @@ -674,11 +806,15 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from, { struct spinand_device *spinand = mtd_to_spinand(mtd); struct nand_device *nand = mtd_to_nanddev(mtd); + struct mtd_ecc_stats old_stats; struct nand_io_iter iter; bool disable_ecc = false; bool ecc_failed = false; + unsigned int retry_mode = 0; int ret; + old_stats = mtd->ecc_stats; + if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout) disable_ecc = true; @@ -690,18 +826,43 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from, if (ret) break; +read_retry: ret = spinand_read_page(spinand, &iter.req); if (ret < 0 && ret != -EBADMSG) break; - if (ret == -EBADMSG) + if (ret == -EBADMSG && spinand->set_read_retry) { + if (spinand->read_retries && (++retry_mode <= spinand->read_retries)) { + ret = spinand->set_read_retry(spinand, retry_mode); + if (ret < 0) { + spinand->set_read_retry(spinand, 0); + return ret; + } + + /* Reset ecc_stats; retry */ + mtd->ecc_stats = old_stats; + goto read_retry; + } else { + /* No more retry modes; real failure */ + ecc_failed = true; + } + } else if (ret == -EBADMSG) { ecc_failed = true; - else + } else { *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } ret = 0; ops->retlen += iter.req.datalen; ops->oobretlen += iter.req.ooblen; + + /* Reset to retry mode 0 */ + if (retry_mode) { + retry_mode = 0; + ret = spinand->set_read_retry(spinand, retry_mode); + if (ret < 0) + return ret; + } } if (ecc_failed && !ret) @@ -793,6 +954,14 @@ static void spinand_cont_read_init(struct spinand_device *spinand) (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || engine_type == NAND_ECC_ENGINE_TYPE_NONE)) { spinand->cont_read_possible = true; + + /* + * Ensure continuous read is disabled on probe. + * Some devices retain this state across soft reset, + * which leaves the OOB area inaccessible and results + * in false positive returns from spinand_isbad(). + */ + spinand_cont_read_enable(spinand, false); } } @@ -841,10 +1010,19 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, old_stats = mtd->ecc_stats; - if (spinand_use_cont_read(mtd, from, ops)) + if (spinand_use_cont_read(mtd, from, ops)) { ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips); - else + if (ret == -EAGAIN && !spinand->cont_read_possible) { + /* + * Spi controller with broken support of continuous + * reading was detected (see spinand_read_from_cache_op()), + * repeat reading in regular mode. + */ + ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); + } + } else { ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); + } if (ops->stats) { ops->stats->uncorrectable_errors += @@ -1035,23 +1213,51 @@ static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs) return ret; } +static struct spi_mem_dirmap_desc *spinand_create_rdesc( + struct spinand_device *spinand, + struct spi_mem_dirmap_info *info) +{ + struct nand_device *nand = spinand_to_nand(spinand); + struct spi_mem_dirmap_desc *desc = NULL; + + if (spinand->cont_read_possible) { + /* + * spi controller may return an error if info->length is + * too large + */ + info->length = nanddev_eraseblock_size(nand); + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, + spinand->spimem, info); + } + + if (IS_ERR_OR_NULL(desc)) { + /* + * continuous reading is not supported by flash or + * its spi controller, use regular reading + */ + spinand->cont_read_possible = false; + + info->length = nanddev_page_size(nand) + + nanddev_per_page_oobsize(nand); + desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, + spinand->spimem, info); + } + + return desc; +} + static int spinand_create_dirmap(struct spinand_device *spinand, unsigned int plane) { struct nand_device *nand = spinand_to_nand(spinand); - struct spi_mem_dirmap_info info = { - .length = nanddev_page_size(nand) + - nanddev_per_page_oobsize(nand), - }; + struct spi_mem_dirmap_info info = { 0 }; struct spi_mem_dirmap_desc *desc; - if (spinand->cont_read_possible) - info.length = nanddev_eraseblock_size(nand); - /* The plane number is passed in MSB just above the column address */ info.offset = plane << fls(nand->memorg.pagesize); - info.op_tmpl = *spinand->op_templates.update_cache; + info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); + info.op_tmpl = *spinand->op_templates->update_cache; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); if (IS_ERR(desc)) @@ -1059,9 +1265,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; - info.op_tmpl = *spinand->op_templates.read_cache; - desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, - spinand->spimem, &info); + info.op_tmpl = *spinand->op_templates->read_cache; + desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -1074,7 +1279,8 @@ static int spinand_create_dirmap(struct spinand_device *spinand, return 0; } - info.op_tmpl = *spinand->op_templates.update_cache; + info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); + info.op_tmpl = *spinand->op_templates->update_cache; info.op_tmpl.data.ecc = true; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); @@ -1083,10 +1289,9 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc_ecc = desc; - info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl = *spinand->op_templates->read_cache; info.op_tmpl.data.ecc = true; - desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, - spinand->spimem, &info); + desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -1125,7 +1330,10 @@ static const struct nand_ops spinand_ops = { static const struct spinand_manufacturer *spinand_manufacturers[] = { &alliancememory_spinand_manufacturer, &ato_spinand_manufacturer, + &dosilicon_spinand_manufacturer, + &esmt_8c_spinand_manufacturer, &esmt_c8_spinand_manufacturer, + &fmsh_spinand_manufacturer, &foresee_spinand_manufacturer, &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, @@ -1195,8 +1403,13 @@ static int spinand_id_detect(struct spinand_device *spinand) static int spinand_manufacturer_init(struct spinand_device *spinand) { - if (spinand->manufacturer->ops->init) - return spinand->manufacturer->ops->init(spinand); + int ret; + + if (spinand->manufacturer->ops->init) { + ret = spinand->manufacturer->ops->init(spinand); + if (ret) + return ret; + } return 0; } @@ -1208,8 +1421,105 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } +static bool spinand_op_is_odtr(const struct spi_mem_op *op) +{ + return op->cmd.dtr && op->cmd.buswidth == 8; +} + +static void spinand_init_ssdr_templates(struct spinand_device *spinand) +{ + struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates; + + tmpl->reset = (struct spi_mem_op)SPINAND_RESET_1S_0_0_OP; + tmpl->readid = (struct spi_mem_op)SPINAND_READID_1S_1S_1S_OP(0, 0, NULL, 0); + tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_1S_0_0_OP; + tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_1S_0_0_OP; + tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_1S_1S_1S_OP(0, NULL); + tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_1S_1S_1S_OP(0, NULL); + tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_1S_1S_0_OP(0); + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0); + tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0); + spinand->op_templates = &spinand->ssdr_op_templates; + spinand->bus_iface = SSDR; +} + +static int spinand_support_vendor_ops(struct spinand_device *spinand, + const struct spinand_info *info, + enum spinand_bus_interface iface) +{ + int i; + + if (!info->vendor_ops) + return 0; + /* + * The vendor ops array is only used in order to verify this chip and all its memory + * operations are supported. If we see patterns emerging, we could ideally name these + * operations and define them at the SPI NAND core level instead. + * For now, this only serves as a sanity check. + */ + for (i = 0; i < info->vendor_ops->nops; i++) { + const struct spi_mem_op *op = &info->vendor_ops->ops[i]; + + if ((iface == SSDR && spinand_op_is_odtr(op)) || + (iface == ODTR && !spinand_op_is_odtr(op))) + continue; + + if (!spi_mem_supports_op(spinand->spimem, op)) + return -EOPNOTSUPP; + } + + return 0; +} + +static int spinand_init_odtr_instruction_set(struct spinand_device *spinand) +{ + struct spinand_mem_ops *tmpl = &spinand->odtr_op_templates; + + tmpl->reset = (struct spi_mem_op)SPINAND_RESET_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->reset)) + return -EOPNOTSUPP; + + tmpl->readid = (struct spi_mem_op)SPINAND_READID_8D_8D_8D_OP(0, 0, NULL, 0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->readid)) + return -EOPNOTSUPP; + + tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_en)) + return -EOPNOTSUPP; + + tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_dis)) + return -EOPNOTSUPP; + + tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_8D_8D_8D_OP(0, NULL); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->set_feature)) + return -EOPNOTSUPP; + + tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_8D_8D_8D_OP(0, NULL); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->get_feature)) + return -EOPNOTSUPP; + + tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->blk_erase)) + return -EOPNOTSUPP; + + if (spinand->flags & SPINAND_ODTR_PACKED_PAGE_READ) + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_PACKED_8D_8D_0_OP(0); + else + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read)) { + return -EOPNOTSUPP; + } + + tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->prog_exec)) + return -EOPNOTSUPP; + + return 0; +} + static const struct spi_mem_op * -spinand_select_op_variant(struct spinand_device *spinand, +spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_interface iface, const struct spinand_op_variants *variants) { struct nand_device *nand = spinand_to_nand(spinand); @@ -1223,6 +1533,10 @@ spinand_select_op_variant(struct spinand_device *spinand, unsigned int nbytes; int ret; + if ((iface == SSDR && spinand_op_is_odtr(&op)) || + (iface == ODTR && !spinand_op_is_odtr(&op))) + continue; + nbytes = nanddev_per_page_oobsize(nand) + nanddev_page_size(nand); @@ -1239,7 +1553,7 @@ spinand_select_op_variant(struct spinand_device *spinand, nbytes -= op.data.nbytes; - op_duration_ns += spi_mem_calc_op_duration(&op); + op_duration_ns += spi_mem_calc_op_duration(spinand->spimem, &op); } if (!nbytes && op_duration_ns < best_op_duration_ns) { @@ -1274,6 +1588,7 @@ int spinand_match_and_init(struct spinand_device *spinand, u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; + int ret; for (i = 0; i < table_size; i++) { const struct spinand_info *info = &table[i]; @@ -1291,30 +1606,66 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->flags = table[i].flags; spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; + spinand->configure_chip = table[i].configure_chip; spinand->set_cont_read = table[i].set_cont_read; + spinand->fact_otp = &table[i].fact_otp; + spinand->user_otp = &table[i].user_otp; + spinand->read_retries = table[i].read_retries; + spinand->set_read_retry = table[i].set_read_retry; + + /* I/O variants selection with single-spi SDR commands */ - op = spinand_select_op_variant(spinand, + op = spinand_select_op_variant(spinand, SSDR, info->op_variants.read_cache); if (!op) - return -ENOTSUPP; + return -EOPNOTSUPP; - spinand->op_templates.read_cache = op; + spinand->ssdr_op_templates.read_cache = op; - op = spinand_select_op_variant(spinand, + op = spinand_select_op_variant(spinand, SSDR, info->op_variants.write_cache); if (!op) - return -ENOTSUPP; + return -EOPNOTSUPP; - spinand->op_templates.write_cache = op; + spinand->ssdr_op_templates.write_cache = op; - op = spinand_select_op_variant(spinand, + op = spinand_select_op_variant(spinand, SSDR, info->op_variants.update_cache); - spinand->op_templates.update_cache = op; + if (!op) + return -EOPNOTSUPP; + + spinand->ssdr_op_templates.update_cache = op; + + ret = spinand_support_vendor_ops(spinand, info, SSDR); + if (ret) + return ret; + + /* I/O variants selection with octo-spi DDR commands (optional) */ + + ret = spinand_init_odtr_instruction_set(spinand); + if (ret) + return 0; + + ret = spinand_support_vendor_ops(spinand, info, ODTR); + if (ret) + return 0; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.read_cache); + spinand->odtr_op_templates.read_cache = op; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.write_cache); + spinand->odtr_op_templates.write_cache = op; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.update_cache); + spinand->odtr_op_templates.update_cache = op; return 0; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static int spinand_detect(struct spinand_device *spinand) @@ -1350,6 +1701,56 @@ static int spinand_detect(struct spinand_device *spinand) return 0; } +static int spinand_configure_chip(struct spinand_device *spinand) +{ + bool odtr = false, quad_enable = false; + int ret; + + if (spinand->odtr_op_templates.read_cache && + spinand->odtr_op_templates.write_cache && + spinand->odtr_op_templates.update_cache) + odtr = true; + + if (odtr) { + if (!spinand->configure_chip) + goto try_ssdr; + + /* ODTR bus interface configuration happens here */ + ret = spinand->configure_chip(spinand, ODTR); + if (ret) { + spinand->odtr_op_templates.read_cache = NULL; + spinand->odtr_op_templates.write_cache = NULL; + spinand->odtr_op_templates.update_cache = NULL; + goto try_ssdr; + } + + spinand->op_templates = &spinand->odtr_op_templates; + spinand->bus_iface = ODTR; + + return 0; + } + +try_ssdr: + if (spinand->flags & SPINAND_HAS_QE_BIT) { + if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 || + spinand->ssdr_op_templates.write_cache->data.buswidth == 4 || + spinand->ssdr_op_templates.update_cache->data.buswidth == 4) + quad_enable = true; + } + + ret = spinand_init_quad_enable(spinand, quad_enable); + if (ret) + return ret; + + if (spinand->configure_chip) { + ret = spinand->configure_chip(spinand, SSDR); + if (ret) + return ret; + } + + return ret; +} + static int spinand_init_flash(struct spinand_device *spinand) { struct device *dev = &spinand->spimem->spi->dev; @@ -1360,10 +1761,6 @@ static int spinand_init_flash(struct spinand_device *spinand) if (ret) return ret; - ret = spinand_init_quad_enable(spinand); - if (ret) - return ret; - ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0); if (ret) return ret; @@ -1376,19 +1773,25 @@ static int spinand_init_flash(struct spinand_device *spinand) return ret; } + ret = spinand_configure_chip(spinand); + if (ret) + goto manuf_cleanup; + /* After power up, all blocks are locked, so unlock them here. */ for (i = 0; i < nand->memorg.ntargets; i++) { ret = spinand_select_target(spinand, i); if (ret) - break; + goto manuf_cleanup; ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED); if (ret) - break; + goto manuf_cleanup; } - if (ret) - spinand_manufacturer_cleanup(spinand); + return 0; + +manuf_cleanup: + spinand_manufacturer_cleanup(spinand); return ret; } @@ -1409,6 +1812,32 @@ static void spinand_mtd_resume(struct mtd_info *mtd) spinand_ecc_enable(spinand, false); } +static int spinand_mtd_suspend(struct mtd_info *mtd) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + int ret; + + /* + * Return to SSDR interface in the suspend path to make sure the + * reset operation is correctly processed upon resume. + * + * Note: Once back in SSDR mode, every operation but the page helpers + * (dirmap based I/O accessors) will work. Page accesses would require + * destroying and recreating the dirmaps twice to work, which would be + * impacting for no reason, as this is just a transitional state. + */ + if (spinand->bus_iface == ODTR) { + ret = spinand->configure_chip(spinand, SSDR); + if (ret) + return ret; + + spinand->op_templates = &spinand->ssdr_op_templates; + spinand->bus_iface = SSDR; + } + + return 0; +} + static int spinand_init(struct spinand_device *spinand) { struct device *dev = &spinand->spimem->spi->dev; @@ -1424,6 +1853,8 @@ static int spinand_init(struct spinand_device *spinand) if (!spinand->scratchbuf) return -ENOMEM; + spinand_init_ssdr_templates(spinand); + ret = spinand_detect(spinand); if (ret) goto err_free_bufs; @@ -1476,8 +1907,15 @@ static int spinand_init(struct spinand_device *spinand) mtd->_block_isreserved = spinand_mtd_block_isreserved; mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; + mtd->_suspend = spinand_mtd_suspend; mtd->_resume = spinand_mtd_resume; + if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) { + ret = spinand_set_mtd_otp_ops(spinand); + if (ret) + goto err_cleanup_ecc_engine; + } + if (nand->ecc.engine) { ret = mtd_ooblayout_count_freebytes(mtd); if (ret < 0) @@ -1520,6 +1958,7 @@ static void spinand_cleanup(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); + nanddev_ecc_engine_cleanup(nand); nanddev_cleanup(nand); spinand_manufacturer_cleanup(spinand); kfree(spinand->databuf); diff --git a/drivers/mtd/nand/spi/dosilicon.c b/drivers/mtd/nand/spi/dosilicon.c new file mode 100644 index 000000000000..f99899866ceb --- /dev/null +++ b/drivers/mtd/nand/spi/dosilicon.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Ahmed Naseef <naseefkm@gmail.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define SPINAND_MFR_DOSILICON 0xE5 + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static int ds35xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 8 + (section * 16); + region->length = 8; + + return 0; +} + +static int ds35xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section == 0) { + /* reserve 2 bytes for the BBM */ + region->offset = 2; + region->length = 6; + } else { + region->offset = section * 16; + region->length = 8; + } + + return 0; +} + +static const struct mtd_ooblayout_ops ds35xx_ooblayout = { + .ecc = ds35xx_ooblayout_ecc, + .free = ds35xx_ooblayout_free, +}; + +static const struct spinand_info dosilicon_spinand_table[] = { + SPINAND_INFO("DS35Q1GA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x71), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 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(&ds35xx_ooblayout, NULL)), + SPINAND_INFO("DS35M1GA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 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(&ds35xx_ooblayout, NULL)), +}; + +static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer dosilicon_spinand_manufacturer = { + .id = SPINAND_MFR_DOSILICON, + .name = "Dosilicon", + .chips = dosilicon_spinand_table, + .nchips = ARRAY_SIZE(dosilicon_spinand_table), + .ops = &dosilicon_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c index 323a20901fc9..8607c2fdedcf 100644 --- a/drivers/mtd/nand/spi/esmt.c +++ b/drivers/mtd/nand/spi/esmt.c @@ -8,23 +8,29 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/mtd/spinand.h> +#include <linux/spi/spi-mem.h> /* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ #define SPINAND_MFR_ESMT_C8 0xc8 +#define SPINAND_MFR_ESMT_8C 0x8c + +#define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7) +#define ESMT_F50L1G41LB_CFG_OTP_LOCK \ + (CFG_OTP_ENABLE | ESMT_F50L1G41LB_CFG_OTP_PROTECT) 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); /* * OOB spare area map (64 bytes) @@ -102,6 +108,98 @@ static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { .free = f50l1g41lb_ooblayout_free, }; +static int f50l1g41lb_otp_info(struct spinand_device *spinand, size_t len, + struct otp_info *buf, size_t *retlen, bool user) +{ + if (len < sizeof(*buf)) + return -EINVAL; + + buf->locked = 0; + buf->start = 0; + buf->length = user ? spinand_user_otp_size(spinand) : + spinand_fact_otp_size(spinand); + + *retlen = sizeof(*buf); + return 0; +} + +static int f50l1g41lb_fact_otp_info(struct spinand_device *spinand, size_t len, + struct otp_info *buf, size_t *retlen) +{ + return f50l1g41lb_otp_info(spinand, len, buf, retlen, false); +} + +static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len, + struct otp_info *buf, size_t *retlen) +{ + return f50l1g41lb_otp_info(spinand, len, buf, retlen, true); +} + +static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from, + size_t len) +{ + struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en); + struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0); + u8 status; + int ret; + + ret = spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, + ESMT_F50L1G41LB_CFG_OTP_LOCK); + if (!ret) + return ret; + + ret = spi_mem_exec_op(spinand->spimem, &write_op); + if (!ret) + goto out; + + ret = spi_mem_exec_op(spinand->spimem, &exec_op); + if (!ret) + goto out; + + ret = spinand_wait(spinand, + SPINAND_WRITE_INITIAL_DELAY_US, + SPINAND_WRITE_POLL_DELAY_US, + &status); + if (!ret && (status & STATUS_PROG_FAILED)) + ret = -EIO; + +out: + if (spinand_upd_cfg(spinand, ESMT_F50L1G41LB_CFG_OTP_LOCK, 0)) { + dev_warn(&spinand_to_mtd(spinand)->dev, + "Can not disable OTP mode\n"); + ret = -EIO; + } + + return ret; +} + +static const struct spinand_user_otp_ops f50l1g41lb_user_otp_ops = { + .info = f50l1g41lb_user_otp_info, + .lock = f50l1g41lb_otp_lock, + .read = spinand_user_otp_read, + .write = spinand_user_otp_write, +}; + +static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = { + .info = f50l1g41lb_fact_otp_info, + .read = spinand_fact_otp_read, +}; + + +static const struct spinand_info esmt_8c_spinand_table[] = { + SPINAND_INFO("F50L1G41LC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), + SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), + SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), +}; + static const struct spinand_info esmt_c8_spinand_table[] = { SPINAND_INFO("F50L1G41LB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f, @@ -112,7 +210,9 @@ static const struct spinand_info esmt_c8_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), + SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), + SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), + SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), SPINAND_INFO("F50D1G41LB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f, 0x7f, 0x7f), @@ -122,7 +222,9 @@ static const struct spinand_info esmt_c8_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), + SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL), + SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops), + SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)), SPINAND_INFO("F50D2G41KA", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f, 0x7f, 0x7f), @@ -138,6 +240,14 @@ static const struct spinand_info esmt_c8_spinand_table[] = { static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { }; +const struct spinand_manufacturer esmt_8c_spinand_manufacturer = { + .id = SPINAND_MFR_ESMT_8C, + .name = "ESMT", + .chips = esmt_8c_spinand_table, + .nchips = ARRAY_SIZE(esmt_8c_spinand_table), + .ops = &esmt_spinand_manuf_ops, +}; + const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { .id = SPINAND_MFR_ESMT_C8, .name = "ESMT", diff --git a/drivers/mtd/nand/spi/fmsh.c b/drivers/mtd/nand/spi/fmsh.c new file mode 100644 index 000000000000..f417955f7d1c --- /dev/null +++ b/drivers/mtd/nand/spi/fmsh.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd. + * + * Author: Dingqiang Lin <jon.lin@rock-chips.com> + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/spinand.h> + +#define FM25S01BI3_STATUS_ECC_MASK (7 << 4) + #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4) + #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4) + #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4) + #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4) + #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4) + +#define SPINAND_MFR_FMSH 0xA1 + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 2; + region->length = 62; + + return 0; +} + +static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + switch (status & FM25S01BI3_STATUS_ECC_MASK) { + case FM25S01BI3_STATUS_ECC_NO_BITFLIPS: + return 0; + + case FM25S01BI3_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS: + return 6; + + case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS: + return 8; + + default: + break; + } + + return -EINVAL; +} + +static int fm25s01bi3_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 fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 4; + region->length = 12; + + return 0; +} + +static const struct mtd_ooblayout_ops fm25s01a_ooblayout = { + .ecc = fm25s01a_ooblayout_ecc, + .free = fm25s01a_ooblayout_free, +}; + +static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = { + .ecc = fm25s01bi3_ooblayout_ecc, + .free = fm25s01bi3_ooblayout_free, +}; + +static const struct spinand_info fmsh_spinand_table[] = { + SPINAND_INFO("FM25S01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)), + SPINAND_INFO("FM25S01BI3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&fm25s01bi3_ooblayout, + fm25s01bi3_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer fmsh_spinand_manufacturer = { + .id = SPINAND_MFR_FMSH, + .name = "Fudan Micro", + .chips = fmsh_spinand_table, + .nchips = ARRAY_SIZE(fmsh_spinand_table), + .ops = &fmsh_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/foresee.c b/drivers/mtd/nand/spi/foresee.c index ecd5f6bffa33..5ff18316092c 100644 --- a/drivers/mtd/nand/spi/foresee.c +++ b/drivers/mtd/nand/spi/foresee.c @@ -11,19 +11,24 @@ #define SPINAND_MFR_FORESEE 0xCD +#define F35SQB002G_STATUS_ECC_MASK (7 << 4) +#define F35SQB002G_STATUS_ECC_NO_BITFLIPS (0 << 4) +#define F35SQB002G_STATUS_ECC_1_3_BITFLIPS (1 << 4) +#define F35SQB002G_STATUS_ECC_UNCOR_ERROR (7 << 4) + static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int f35sqa002g_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -70,6 +75,25 @@ static int f35sqa002g_ecc_get_status(struct spinand_device *spinand, u8 status) return -EBADMSG; } +static int f35sqb002g_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + switch (status & F35SQB002G_STATUS_ECC_MASK) { + case F35SQB002G_STATUS_ECC_NO_BITFLIPS: + return 0; + + case F35SQB002G_STATUS_ECC_1_3_BITFLIPS: + return 3; + + case F35SQB002G_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ + return ((status & F35SQB002G_STATUS_ECC_MASK) >> 4) + 2; + } + + return -EINVAL; +} + static const struct spinand_info foresee_spinand_table[] = { SPINAND_INFO("F35SQA002G", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x72, 0x72), @@ -91,6 +115,16 @@ static const struct spinand_info foresee_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&f35sqa002g_ooblayout, f35sqa002g_ecc_get_status)), + SPINAND_INFO("F35SQB002G", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52, 0x52), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&f35sqa002g_ooblayout, + f35sqb002g_ecc_get_status)), }; static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index d620bb02a20a..a6d6d6e0cc37 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -4,6 +4,7 @@ * Chuanhong Guo <gch981213@gmail.com> */ +#include <linux/bitfield.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/mtd/spinand.h> @@ -23,45 +24,125 @@ #define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) +/* Feature bit definitions */ +#define GD_FEATURE_NR BIT(3) /* Normal Read(1=normal,0=continuous) */ +#define GD_FEATURE_CRDC BIT(2) /* Continuous Read Dummy */ + +/* ECC status extraction helpers */ +#define GD_ECCSR_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr) +#define GD_ECCSR_ACCUMULATED(eccsr) FIELD_GET(GENMASK(7, 4), eccsr) + +struct gigadevice_priv { + bool continuous_read; +}; + static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(read_cache_variants_f, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP_3A(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP_3A(0, 0, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_3A_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_1S_OP(0, 0, NULL, 0, 0)); static SPINAND_OP_VARIANTS(read_cache_variants_1gq5, - 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(read_cache_variants_2gq5, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 4, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static int gd5fxgm9_get_eccsr(struct spinand_device *spinand, u8 *eccsr) +{ + struct gigadevice_priv *priv = spinand->priv; + 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)); + int ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + if (priv->continuous_read) + *eccsr = GD_ECCSR_ACCUMULATED(*eccsr); + else + *eccsr = GD_ECCSR_LAST_PAGE(*eccsr); + + return 0; +} + +static int gd5fxgm9_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + u8 eccsr; + int ret; + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: + ret = gd5fxgm9_get_eccsr(spinand, spinand->scratchbuf); + if (ret) + return nanddev_get_ecc_conf(nand)->strength; + + eccsr = *spinand->scratchbuf; + if (WARN_ON(!eccsr || eccsr > nanddev_get_ecc_conf(nand)->strength)) + return nanddev_get_ecc_conf(nand)->strength; + + return eccsr; + + case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: + return 8; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + return -EINVAL; + } +} + +static int gd5fxgm9_set_continuous_read(struct spinand_device *spinand, bool enable) +{ + struct gigadevice_priv *priv = spinand->priv; + int ret; + + ret = spinand_upd_cfg(spinand, GD_FEATURE_NR, + enable ? 0 : GD_FEATURE_NR); + if (ret) + return ret; + + priv->continuous_read = enable; + + return 0; +} static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -185,8 +266,8 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf); int ret; switch (status & STATUS_ECC_MASK) { @@ -228,8 +309,8 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf); int ret; switch (status & STATUS_ECC_MASK) { @@ -533,9 +614,51 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GM9UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91, 0x01), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgm9_ecc_get_status), + SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)), + SPINAND_INFO("GD5F1GM9RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81, 0x01), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgm9_ecc_get_status), + SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)), }; +static int gd5fxgm9_spinand_init(struct spinand_device *spinand) +{ + struct gigadevice_priv *priv; + + priv = kzalloc_obj(*priv); + if (!priv) + return -ENOMEM; + + spinand->priv = priv; + + return 0; +} + +static void gd5fxgm9_spinand_cleanup(struct spinand_device *spinand) +{ + kfree(spinand->priv); +} + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { + .init = gd5fxgm9_spinand_init, + .cleanup = gd5fxgm9_spinand_cleanup, }; const struct spinand_manufacturer gigadevice_spinand_manufacturer = { diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 3dc4d63d6832..67cafa1bb8ef 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -14,6 +14,8 @@ #define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr) #define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), eccsr) #define MACRONIX_CFG_CONT_READ BIT(2) +#define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70 +#define MACRONIX_NUM_READ_RETRY_MODES 5 #define STATUS_ECC_HAS_BITFLIPS_THRESHOLD (3 << 4) @@ -26,18 +28,35 @@ struct macronix_priv { }; 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(false, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +#define SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(buf) \ + 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, buf, 1)) + +static SPINAND_OP_VARIANTS(macronix_ops, + SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(NULL)); + +static struct spi_mem_op +spinand_fill_macronix_read_eccsr_op(struct spinand_device *spinand, void *valptr) +{ + WARN_ON_ONCE(spinand->bus_iface != SSDR); + + return (struct spi_mem_op)SPINAND_MACRONIX_READ_ECCSR_1S_0_1S(valptr); +} static int mx35lfxge4ab_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -65,12 +84,10 @@ static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr) { struct macronix_priv *priv = spinand->priv; - 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)); + struct spi_mem_op op = SPINAND_OP(spinand, macronix_read_eccsr, eccsr); + int ret; - int ret = spi_mem_exec_op(spinand->spimem, &op); + ret = spi_mem_exec_op(spinand->spimem, &op); if (ret) return ret; @@ -136,6 +153,23 @@ static int macronix_set_cont_read(struct spinand_device *spinand, bool enable) return 0; } +/** + * macronix_set_read_retry - Set the retry mode + * @spinand: SPI NAND device + * @retry_mode: Specify which retry mode to set + * + * Return: 0 on success, a negative error code otherwise. + */ +static int macronix_set_read_retry(struct spinand_device *spinand, + unsigned int retry_mode) +{ + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + MACRONIX_FEATURE_ADDR_READ_RETRY, spinand->scratchbuf); + + *spinand->scratchbuf = retry_mode; + return spi_mem_exec_op(spinand->spimem, &op); +} + static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO("MX35LF1GE4AB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), @@ -145,6 +179,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35LF2GE4AB", @@ -166,9 +201,12 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), - SPINAND_CONT_READ(macronix_set_cont_read)), + SPINAND_CONT_READ(macronix_set_cont_read), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37, 0x03), NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), @@ -177,9 +215,12 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), - SPINAND_CONT_READ(macronix_set_cont_read)), + SPINAND_CONT_READ(macronix_set_cont_read), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF1G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -188,7 +229,9 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF2G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), @@ -198,7 +241,9 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT, - SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -207,7 +252,9 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF4G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), @@ -217,7 +264,9 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT, - SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35LF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -226,7 +275,9 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX31LF1GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -235,6 +286,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX31UF1GE4BC", @@ -245,6 +297,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), @@ -258,6 +311,7 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT | SPINAND_HAS_READ_PLANE_SELECT_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF4G24AD", @@ -269,8 +323,11 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -279,8 +336,11 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -289,9 +349,12 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), - SPINAND_CONT_READ(macronix_set_cont_read)), + SPINAND_CONT_READ(macronix_set_cont_read), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF2G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), @@ -302,6 +365,7 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT | SPINAND_HAS_READ_PLANE_SELECT_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G24AD", @@ -313,8 +377,11 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT | SPINAND_HAS_PROG_PLANE_SELECT_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -323,8 +390,11 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF2GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -333,9 +403,12 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), - SPINAND_CONT_READ(macronix_set_cont_read)), + SPINAND_CONT_READ(macronix_set_cont_read), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF2GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2, 0x01), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -344,6 +417,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_CONT_READ(macronix_set_cont_read)), @@ -355,6 +429,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF1G24AD", @@ -365,8 +440,11 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF1GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -375,9 +453,12 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), - SPINAND_CONT_READ(macronix_set_cont_read)), + SPINAND_CONT_READ(macronix_set_cont_read), + SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES, + macronix_set_read_retry)), SPINAND_INFO("MX35UF1GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -386,6 +467,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status), SPINAND_CONT_READ(macronix_set_cont_read)), @@ -397,6 +479,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX3UF2GE4BC", @@ -407,6 +490,7 @@ static const struct spinand_info macronix_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, + SPINAND_INFO_VENDOR_OPS(¯onix_ops), SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), }; @@ -415,7 +499,7 @@ static int macronix_spinand_init(struct spinand_device *spinand) { struct macronix_priv *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index ad0bb9755a09..36f6cbbd7462 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -9,6 +9,8 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/mtd/spinand.h> +#include <linux/spi/spi-mem.h> +#include <linux/string.h> #define SPINAND_MFR_MICRON 0x2c @@ -28,34 +30,38 @@ #define MICRON_SELECT_DIE(x) ((x) << 6) +#define MICRON_MT29F2G01ABAGD_CFG_OTP_STATE BIT(7) +#define MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK \ + (CFG_OTP_ENABLE | MICRON_MT29F2G01ABAGD_CFG_OTP_STATE) + static SPINAND_OP_VARIANTS(quadio_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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(x4_write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); static SPINAND_OP_VARIANTS(x4_update_cache_variants, - SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), - SPINAND_PROG_LOAD(false, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); /* Micron MT29F2G01AAAED Device */ static SPINAND_OP_VARIANTS(x4_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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(x1_write_cache_variants, - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); static SPINAND_OP_VARIANTS(x1_update_cache_variants, - SPINAND_PROG_LOAD(false, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -131,8 +137,8 @@ static const struct mtd_ooblayout_ops micron_4_ooblayout = { static int micron_select_target(struct spinand_device *spinand, unsigned int target) { - struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + MICRON_DIE_SELECT_REG, spinand->scratchbuf); if (target > 1) return -EINVAL; @@ -168,6 +174,131 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } +static int mt29f2g01abagd_otp_is_locked(struct spinand_device *spinand) +{ + size_t bufsize = spinand_otp_page_size(spinand); + size_t retlen; + u8 *buf; + int ret; + + buf = kmalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = spinand_upd_cfg(spinand, + MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK, + MICRON_MT29F2G01ABAGD_CFG_OTP_STATE); + if (ret) + goto free_buf; + + ret = spinand_user_otp_read(spinand, 0, bufsize, &retlen, buf); + + if (spinand_upd_cfg(spinand, MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK, + 0)) { + dev_warn(&spinand_to_mtd(spinand)->dev, + "Can not disable OTP mode\n"); + ret = -EIO; + } + + if (ret) + goto free_buf; + + /* If all zeros, then the OTP area is locked. */ + if (mem_is_zero(buf, bufsize)) + ret = 1; + +free_buf: + kfree(buf); + return ret; +} + +static int mt29f2g01abagd_otp_info(struct spinand_device *spinand, size_t len, + struct otp_info *buf, size_t *retlen, + bool user) +{ + int locked; + + if (len < sizeof(*buf)) + return -EINVAL; + + locked = mt29f2g01abagd_otp_is_locked(spinand); + if (locked < 0) + return locked; + + buf->locked = locked; + buf->start = 0; + buf->length = user ? spinand_user_otp_size(spinand) : + spinand_fact_otp_size(spinand); + + *retlen = sizeof(*buf); + return 0; +} + +static int mt29f2g01abagd_fact_otp_info(struct spinand_device *spinand, + size_t len, struct otp_info *buf, + size_t *retlen) +{ + return mt29f2g01abagd_otp_info(spinand, len, buf, retlen, false); +} + +static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand, + size_t len, struct otp_info *buf, + size_t *retlen) +{ + return mt29f2g01abagd_otp_info(spinand, len, buf, retlen, true); +} + +static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from, + size_t len) +{ + struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en); + struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0); + u8 status; + int ret; + + ret = spinand_upd_cfg(spinand, + MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK, + MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK); + if (!ret) + return ret; + + ret = spi_mem_exec_op(spinand->spimem, &write_op); + if (!ret) + goto out; + + ret = spi_mem_exec_op(spinand->spimem, &exec_op); + if (!ret) + goto out; + + ret = spinand_wait(spinand, + SPINAND_WRITE_INITIAL_DELAY_US, + SPINAND_WRITE_POLL_DELAY_US, + &status); + if (!ret && (status & STATUS_PROG_FAILED)) + ret = -EIO; + +out: + if (spinand_upd_cfg(spinand, MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK, 0)) { + dev_warn(&spinand_to_mtd(spinand)->dev, + "Can not disable OTP mode\n"); + ret = -EIO; + } + + return ret; +} + +static const struct spinand_user_otp_ops mt29f2g01abagd_user_otp_ops = { + .info = mt29f2g01abagd_user_otp_info, + .lock = mt29f2g01abagd_otp_lock, + .read = spinand_user_otp_read, + .write = spinand_user_otp_write, +}; + +static const struct spinand_fact_otp_ops mt29f2g01abagd_fact_otp_ops = { + .info = mt29f2g01abagd_fact_otp_info, + .read = spinand_fact_otp_read, +}; + static const struct spinand_info micron_spinand_table[] = { /* M79A 2Gb 3.3V */ SPINAND_INFO("MT29F2G01ABAGD", @@ -179,7 +310,9 @@ static const struct spinand_info micron_spinand_table[] = { &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, - micron_8_ecc_get_status)), + micron_8_ecc_get_status), + SPINAND_USER_OTP_INFO(12, 2, &mt29f2g01abagd_user_otp_ops), + SPINAND_FACT_OTP_INFO(2, 0, &mt29f2g01abagd_fact_otp_ops)), /* M79A 2Gb 1.8V */ SPINAND_INFO("MT29F2G01ABBGD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), diff --git a/drivers/mtd/nand/spi/otp.c b/drivers/mtd/nand/spi/otp.c new file mode 100644 index 000000000000..ce41bca86ea9 --- /dev/null +++ b/drivers/mtd/nand/spi/otp.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, SaluteDevices. All Rights Reserved. + * + * Author: Martin Kurbanov <mmkurbanov@salutedevices.com> + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/spinand.h> + +/** + * spinand_otp_page_size() - Get SPI-NAND OTP page size + * @spinand: the spinand device + * + * Return: the OTP page size. + */ +size_t spinand_otp_page_size(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + return nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); +} + +static size_t spinand_otp_size(struct spinand_device *spinand, + const struct spinand_otp_layout *layout) +{ + return layout->npages * spinand_otp_page_size(spinand); +} + +/** + * spinand_fact_otp_size() - Get SPI-NAND factory OTP area size + * @spinand: the spinand device + * + * Return: the OTP size. + */ +size_t spinand_fact_otp_size(struct spinand_device *spinand) +{ + return spinand_otp_size(spinand, &spinand->fact_otp->layout); +} + +/** + * spinand_user_otp_size() - Get SPI-NAND user OTP area size + * @spinand: the spinand device + * + * Return: the OTP size. + */ +size_t spinand_user_otp_size(struct spinand_device *spinand) +{ + return spinand_otp_size(spinand, &spinand->user_otp->layout); +} + +static int spinand_otp_check_bounds(struct spinand_device *spinand, loff_t ofs, + size_t len, + const struct spinand_otp_layout *layout) +{ + if (ofs < 0 || ofs + len > spinand_otp_size(spinand, layout)) + return -EINVAL; + + return 0; +} + +static int spinand_user_otp_check_bounds(struct spinand_device *spinand, + loff_t ofs, size_t len) +{ + return spinand_otp_check_bounds(spinand, ofs, len, + &spinand->user_otp->layout); +} + +static int spinand_otp_rw(struct spinand_device *spinand, loff_t ofs, + size_t len, size_t *retlen, u8 *buf, bool is_write, + const struct spinand_otp_layout *layout) +{ + struct nand_page_io_req req = {}; + unsigned long long page; + size_t copied = 0; + size_t otp_pagesize = spinand_otp_page_size(spinand); + int ret; + + if (!len) + return 0; + + ret = spinand_otp_check_bounds(spinand, ofs, len, layout); + if (ret) + return ret; + + ret = spinand_upd_cfg(spinand, CFG_OTP_ENABLE, CFG_OTP_ENABLE); + if (ret) + return ret; + + page = ofs; + req.dataoffs = do_div(page, otp_pagesize); + req.pos.page = page + layout->start_page; + req.type = is_write ? NAND_PAGE_WRITE : NAND_PAGE_READ; + req.mode = MTD_OPS_RAW; + req.databuf.in = buf; + + while (copied < len) { + req.datalen = min_t(unsigned int, + otp_pagesize - req.dataoffs, + len - copied); + + if (is_write) + ret = spinand_write_page(spinand, &req); + else + ret = spinand_read_page(spinand, &req); + + if (ret < 0) + break; + + req.databuf.in += req.datalen; + req.pos.page++; + req.dataoffs = 0; + copied += req.datalen; + } + + *retlen = copied; + + if (spinand_upd_cfg(spinand, CFG_OTP_ENABLE, 0)) { + dev_warn(&spinand_to_mtd(spinand)->dev, + "Can not disable OTP mode\n"); + ret = -EIO; + } + + return ret; +} + +/** + * spinand_fact_otp_read() - Read from OTP area + * @spinand: the spinand device + * @ofs: the offset to read + * @len: the number of data bytes to read + * @retlen: the pointer to variable to store the number of read bytes + * @buf: the buffer to store the read data + * + * Return: 0 on success, an error code otherwise. + */ +int spinand_fact_otp_read(struct spinand_device *spinand, loff_t ofs, + size_t len, size_t *retlen, u8 *buf) +{ + return spinand_otp_rw(spinand, ofs, len, retlen, buf, false, + &spinand->fact_otp->layout); +} + +/** + * spinand_user_otp_read() - Read from OTP area + * @spinand: the spinand device + * @ofs: the offset to read + * @len: the number of data bytes to read + * @retlen: the pointer to variable to store the number of read bytes + * @buf: the buffer to store the read data + * + * Return: 0 on success, an error code otherwise. + */ +int spinand_user_otp_read(struct spinand_device *spinand, loff_t ofs, + size_t len, size_t *retlen, u8 *buf) +{ + return spinand_otp_rw(spinand, ofs, len, retlen, buf, false, + &spinand->user_otp->layout); +} + +/** + * spinand_user_otp_write() - Write to OTP area + * @spinand: the spinand device + * @ofs: the offset to write to + * @len: the number of bytes to write + * @retlen: the pointer to variable to store the number of written bytes + * @buf: the buffer with data to write + * + * Return: 0 on success, an error code otherwise. + */ +int spinand_user_otp_write(struct spinand_device *spinand, loff_t ofs, + size_t len, size_t *retlen, const u8 *buf) +{ + return spinand_otp_rw(spinand, ofs, len, retlen, (u8 *)buf, true, + &spinand->user_otp->layout); +} + +static int spinand_mtd_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf, + bool is_fact) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + int ret; + + *retlen = 0; + + mutex_lock(&spinand->lock); + + if (is_fact) + ret = spinand->fact_otp->ops->info(spinand, len, buf, retlen); + else + ret = spinand->user_otp->ops->info(spinand, len, buf, retlen); + + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_mtd_fact_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) +{ + return spinand_mtd_otp_info(mtd, len, retlen, buf, true); +} + +static int spinand_mtd_user_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) +{ + return spinand_mtd_otp_info(mtd, len, retlen, buf, false); +} + +static int spinand_mtd_otp_read(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u8 *buf, bool is_fact) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + int ret; + + *retlen = 0; + + if (!len) + return 0; + + ret = spinand_otp_check_bounds(spinand, ofs, len, + is_fact ? &spinand->fact_otp->layout : + &spinand->user_otp->layout); + if (ret) + return ret; + + mutex_lock(&spinand->lock); + + if (is_fact) + ret = spinand->fact_otp->ops->read(spinand, ofs, len, retlen, + buf); + else + ret = spinand->user_otp->ops->read(spinand, ofs, len, retlen, + buf); + + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_mtd_fact_otp_read(struct mtd_info *mtd, loff_t ofs, + size_t len, size_t *retlen, u8 *buf) +{ + return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, true); +} + +static int spinand_mtd_user_otp_read(struct mtd_info *mtd, loff_t ofs, + size_t len, size_t *retlen, u8 *buf) +{ + return spinand_mtd_otp_read(mtd, ofs, len, retlen, buf, false); +} + +static int spinand_mtd_user_otp_write(struct mtd_info *mtd, loff_t ofs, + size_t len, size_t *retlen, const u8 *buf) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + const struct spinand_user_otp_ops *ops = spinand->user_otp->ops; + int ret; + + *retlen = 0; + + if (!len) + return 0; + + ret = spinand_user_otp_check_bounds(spinand, ofs, len); + if (ret) + return ret; + + mutex_lock(&spinand->lock); + ret = ops->write(spinand, ofs, len, retlen, buf); + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_mtd_user_otp_erase(struct mtd_info *mtd, loff_t ofs, + size_t len) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + const struct spinand_user_otp_ops *ops = spinand->user_otp->ops; + int ret; + + if (!len) + return 0; + + ret = spinand_user_otp_check_bounds(spinand, ofs, len); + if (ret) + return ret; + + mutex_lock(&spinand->lock); + ret = ops->erase(spinand, ofs, len); + mutex_unlock(&spinand->lock); + + return ret; +} + +static int spinand_mtd_user_otp_lock(struct mtd_info *mtd, loff_t ofs, + size_t len) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + const struct spinand_user_otp_ops *ops = spinand->user_otp->ops; + int ret; + + if (!len) + return 0; + + ret = spinand_user_otp_check_bounds(spinand, ofs, len); + if (ret) + return ret; + + mutex_lock(&spinand->lock); + ret = ops->lock(spinand, ofs, len); + mutex_unlock(&spinand->lock); + + return ret; +} + +/** + * spinand_set_mtd_otp_ops() - Setup OTP methods + * @spinand: the spinand device + * + * Setup OTP methods. + * + * Return: 0 on success, a negative error code otherwise. + */ +int spinand_set_mtd_otp_ops(struct spinand_device *spinand) +{ + struct mtd_info *mtd = spinand_to_mtd(spinand); + const struct spinand_fact_otp_ops *fact_ops = spinand->fact_otp->ops; + const struct spinand_user_otp_ops *user_ops = spinand->user_otp->ops; + + if (!user_ops && !fact_ops) + return -EINVAL; + + if (user_ops) { + if (user_ops->info) + mtd->_get_user_prot_info = spinand_mtd_user_otp_info; + + if (user_ops->read) + mtd->_read_user_prot_reg = spinand_mtd_user_otp_read; + + if (user_ops->write) + mtd->_write_user_prot_reg = spinand_mtd_user_otp_write; + + if (user_ops->lock) + mtd->_lock_user_prot_reg = spinand_mtd_user_otp_lock; + + if (user_ops->erase) + mtd->_erase_user_prot_reg = spinand_mtd_user_otp_erase; + } + + if (fact_ops) { + if (fact_ops->info) + mtd->_get_fact_prot_info = spinand_mtd_fact_otp_info; + + if (fact_ops->read) + mtd->_read_fact_prot_reg = spinand_mtd_fact_otp_read; + } + + return 0; +} diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c index 6e7cc6995380..73bd124273a5 100644 --- a/drivers/mtd/nand/spi/paragon.c +++ b/drivers/mtd/nand/spi/paragon.c @@ -22,20 +22,20 @@ 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int pn26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, diff --git a/drivers/mtd/nand/spi/skyhigh.c b/drivers/mtd/nand/spi/skyhigh.c index 961df0d74984..bf9ce163e6a7 100644 --- a/drivers/mtd/nand/spi/skyhigh.c +++ b/drivers/mtd/nand/spi/skyhigh.c @@ -17,20 +17,20 @@ #define SKYHIGH_CONFIG_PROTECT_EN BIT(1) static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 4, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int skyhigh_spinand_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 2e2106b2705f..ef649162ee68 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -15,28 +15,28 @@ #define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_x4_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); static SPINAND_OP_VARIANTS(update_cache_x4_variants, - SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), - SPINAND_PROG_LOAD(false, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); /* * Backward compatibility for 1st generation Serial NAND devices * which don't support Quad Program Load operation. */ static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); static SPINAND_OP_VARIANTS(update_cache_variants, - SPINAND_PROG_LOAD(false, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -73,7 +73,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + 0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 8394a1b1fb0c..7cc0f0091430 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/mtd/spinand.h> #include <linux/units.h> +#include <linux/delay.h> #define SPINAND_MFR_WINBOND 0xEF @@ -18,39 +19,118 @@ #define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4) +#define W25N0XJW_SR4 0xD0 +#define W25N0XJW_SR4_HS BIT(2) + +#define W35N01JW_VCR_IO_MODE_REG 0x00 +#define W35N01JW_VCR_IO_MODE_SINGLE_SDR 0xFF +#define W35N01JW_VCR_IO_MODE_OCTAL_SDR 0xDF +#define W35N01JW_VCR_IO_MODE_OCTAL_DDR_DS 0xE7 +#define W35N01JW_VCR_IO_MODE_OCTAL_DDR 0xC7 +#define W35N01JW_VCR_DUMMY_CLOCK_REG 0x01 + /* * "X2" in the core is equivalent to "dual output" in the datasheets, * "X4" in the core is equivalent to "quad output" in the datasheets. + * Quad and octal capable chips feature an absolute maximum frequency of 166MHz. */ -static SPINAND_OP_VARIANTS(read_cache_dtr_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_DTR_OP(0, 8, NULL, 0, 80 * HZ_PER_MHZ), - SPINAND_PAGE_READ_FROM_CACHE_X4_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), - 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_DTR_OP(0, 4, NULL, 0, 80 * HZ_PER_MHZ), - SPINAND_PAGE_READ_FROM_CACHE_X2_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), - 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_DTR_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), - SPINAND_PAGE_READ_FROM_CACHE_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0, 54 * HZ_PER_MHZ)); +static SPINAND_OP_VARIANTS(read_cache_octal_variants, + SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 24, NULL, 0, 120 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(0, 16, NULL, 0, 86 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 3, NULL, 0, 120 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(0, 2, NULL, 0, 105 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 20, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 16, NULL, 0, 162 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 12, NULL, 0, 124 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(0, 8, NULL, 0, 86 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_8S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_8S_OP(0, 1, NULL, 0, 133 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); + +static SPINAND_OP_VARIANTS(write_cache_octal_variants, + SPINAND_PROG_LOAD_8D_8D_8D_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_8S_8S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_8S_OP(0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_octal_variants, + SPINAND_PROG_LOAD_8D_8D_8D_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_8S_8S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(read_cache_dual_quad_dtr_variants, + SPINAND_PAGE_READ_FROM_CACHE_1S_4D_4D_OP(0, 8, NULL, 0, 80 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1D_4D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 4, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 104 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2D_2D_OP(0, 4, NULL, 0, 80 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1D_2D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 104 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1D_1D_OP(0, 2, NULL, 0, 80 * HZ_PER_MHZ), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 54 * HZ_PER_MHZ)); 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 2, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); + +#define SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1), \ + SPI_MEM_OP_ADDR(3, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 1)) + +#define SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, buf) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x81, 8), \ + SPI_MEM_DTR_OP_ADDR(4, reg << 8, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_DTR_OP_DATA_OUT(2, buf, 8)) + +static SPINAND_OP_VARIANTS(winbond_w35_ops, + SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(0, NULL), + SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(0, NULL)); + +static struct spi_mem_op +spinand_fill_winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, void *valptr) +{ + return (spinand->bus_iface == SSDR) ? + (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_1S_1S_1S(reg, valptr) : + (struct spi_mem_op)SPINAND_WINBOND_WRITE_VCR_8D_8D_8D(reg, valptr); +} + +#define SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(buf) \ + 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, buf, 1)) + +static SPINAND_OP_VARIANTS(winbond_w25_ops, + SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(NULL)); + +static struct spi_mem_op +spinand_fill_winbond_select_target_op(struct spinand_device *spinand, void *valptr) +{ + WARN_ON_ONCE(spinand->bus_iface != SSDR); + + return (struct spi_mem_op)SPINAND_WINBOND_SELECT_TARGET_1S_0_1S(valptr); +} static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) @@ -84,12 +164,8 @@ static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { 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)); + struct spi_mem_op op = SPINAND_OP(spinand, winbond_select_target, + spinand->scratchbuf); *spinand->scratchbuf = target; return spi_mem_exec_op(spinand->spimem, &op); @@ -141,12 +217,83 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { .free = w25n02kv_ooblayout_free, }; +static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 12; + region->length = 4; + + return 0; +} + +static int w25n01jw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section); + region->length = 12; + + /* Extract BBM */ + if (!section) { + region->offset += 2; + region->length -= 2; + } + + return 0; +} + +static int w35n01jw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + region->offset = (16 * section) + 12; + region->length = 4; + + return 0; +} + +static int w35n01jw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 7) + return -ERANGE; + + region->offset = 16 * section; + region->length = 12; + + /* Extract BBM */ + if (!section) { + region->offset += 2; + region->length -= 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops w25n01jw_ooblayout = { + .ecc = w25n01jw_ooblayout_ecc, + .free = w25n01jw_ooblayout_free, +}; + +static const struct mtd_ooblayout_ops w35n01jw_ooblayout = { + .ecc = w35n01jw_ooblayout_ecc, + .free = w35n01jw_ooblayout_free, +}; + static int w25n02kv_ecc_get_status(struct spinand_device *spinand, u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + 0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: @@ -179,6 +326,131 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } +static int w25n0xjw_hs_cfg(struct spinand_device *spinand, + enum spinand_bus_interface iface) +{ + const struct spi_mem_op *op; + bool hs; + u8 sr4; + int ret; + + if (iface != SSDR) + return -EOPNOTSUPP; + + /* + * SDR dual and quad I/O operations over 104MHz require the HS bit to + * enable a few more dummy cycles. + */ + op = spinand->op_templates->read_cache; + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + hs = false; + else if (op->cmd.buswidth != 1 || op->addr.buswidth == 1) + hs = false; + else if (op->max_freq && op->max_freq <= 104 * HZ_PER_MHZ) + hs = false; + else + hs = true; + + ret = spinand_read_reg_op(spinand, W25N0XJW_SR4, &sr4); + if (ret) + return ret; + + if (hs) + sr4 |= W25N0XJW_SR4_HS; + else + sr4 &= ~W25N0XJW_SR4_HS; + + ret = spinand_write_reg_op(spinand, W25N0XJW_SR4, sr4); + if (ret) + return ret; + + return 0; +} + +static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val) +{ + struct spi_mem_op op = SPINAND_OP(spinand, winbond_write_vcr, + reg, spinand->scratchbuf); + int ret; + + *spinand->scratchbuf = val; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + /* + * Write VCR operation doesn't set the busy bit in SR, which means we + * cannot perform a status poll. Minimum time of 50ns is needed to + * complete the write. + */ + ndelay(50); + + return 0; +} + +static int w35n0xjw_vcr_cfg(struct spinand_device *spinand, + enum spinand_bus_interface iface) +{ + const struct spi_mem_op *ref_op; + unsigned int dummy_cycles; + bool dtr, single; + u8 io_mode; + int ret; + + switch (iface) { + case SSDR: + ref_op = spinand->ssdr_op_templates.read_cache; + break; + case ODTR: + ref_op = spinand->odtr_op_templates.read_cache; + break; + default: + return -EOPNOTSUPP; + } + + dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) / + (ref_op->dummy.dtr ? 2 : 1); + switch (dummy_cycles) { + case 8: + case 12: + case 16: + case 20: + case 24: + case 28: + break; + default: + return -EINVAL; + } + + ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_DUMMY_CLOCK_REG, dummy_cycles); + if (ret) + return ret; + + single = (ref_op->cmd.buswidth == 1 && + ref_op->addr.buswidth == 1 && + ref_op->data.buswidth == 1); + dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr); + if (single && !dtr) + io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR; + else if (!single && !dtr) + io_mode = W35N01JW_VCR_IO_MODE_OCTAL_SDR; + else if (!single && dtr) + io_mode = W35N01JW_VCR_IO_MODE_OCTAL_DDR; + else + return -EINVAL; + + ret = w35n0xjw_write_vcr(spinand, W35N01JW_VCR_IO_MODE_REG, io_mode); + if (ret) + return ret; + + return 0; +} + static const struct spinand_info winbond_spinand_table[] = { /* 512M-bit densities */ SPINAND_INFO("W25N512GW", /* 1.8V */ @@ -213,11 +485,12 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbc, 0x21), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_dtr_variants, + SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, &write_cache_variants, &update_cache_variants), - 0, - SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&w25n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), SPINAND_INFO("W25N01KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21), NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1), @@ -227,6 +500,39 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25n01kv_ooblayout, w25n02kv_ecc_get_status)), + SPINAND_INFO("W35N01JW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdc, 0x21), + NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants), + 0, + SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), + SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), + SPINAND_INFO("W35N02JW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x22), + NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 2, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants), + SPINAND_ODTR_PACKED_PAGE_READ, + SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), + SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), + SPINAND_INFO("W35N04JW", /* 1.8V */ + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23), + NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_octal_variants, + &write_cache_octal_variants, + &update_cache_octal_variants), + SPINAND_ODTR_PACKED_PAGE_READ, + SPINAND_INFO_VENDOR_OPS(&winbond_w35_ops), + SPINAND_ECCINFO(&w35n01jw_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg)), /* 2G-bit densities */ SPINAND_INFO("W25M02GV", /* 2x1G-bit 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), @@ -236,17 +542,19 @@ static const struct spinand_info winbond_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, + SPINAND_INFO_VENDOR_OPS(&winbond_w25_ops), SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), SPINAND_INFO("W25N02JW", /* high-speed 1.8V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xbf, 0x22), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 2, 1), NAND_ECCREQ(1, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_dtr_variants, + SPINAND_INFO_OP_VARIANTS(&read_cache_dual_quad_dtr_variants, &write_cache_variants, &update_cache_variants), - 0, - SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_CONFIGURE_CHIP(w25n0xjw_hs_cfg)), SPINAND_INFO("W25N02KV", /* 3.3V */ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c index 3f539ca0de86..5915b37b47f5 100644 --- a/drivers/mtd/nand/spi/xtx.c +++ b/drivers/mtd/nand/spi/xtx.c @@ -23,20 +23,20 @@ #define XT26XXXD_STATUS_ECC_UNCOR_ERROR (2) static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, 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_FAST_OP(0, 1, NULL, 0), - SPINAND_PAGE_READ_FROM_CACHE_OP(0, 1, NULL, 0)); + SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0), + SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0)); static SPINAND_OP_VARIANTS(write_cache_variants, - SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), - SPINAND_PROG_LOAD(true, 0, NULL, 0)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(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)); + SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0), + SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0)); static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) |
