summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2021-04-15 21:14:51 +0200
committerRichard Weinberger <richard@nod.at>2021-04-15 21:14:51 +0200
commitef4ed780d005d65b1a70ba7803233cace93a73ac (patch)
tree8e30fbe16f3634ee8f2274e5ab5acb27f07edf2d /drivers/mtd
parent256437ebda641c49abbbe919becc16f75848380b (diff)
parent32cbc7cb70b07041e82f897f96b3035358470b14 (diff)
Merge tag 'nand/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux into mtd/next
MTD core: * Handle possible -EPROBE_DEFER from parse_mtd_partitions() NAND core: * Fix error handling in nand_prog_page_op() (x2) * Add a helper to retrieve the number of ECC bytes per step * Add a helper to retrieve the number of ECC steps * Let ECC engines advertize the exact number of steps * ECC Hamming: - Populate the public nsteps field - Use the public nsteps field * ECC BCH: - Populate the public nsteps field - Use the public nsteps field Raw NAND core: * Add support for secure regions in NAND memory * Try not to use the ECC private structures * Remove duplicate include in rawnand.h * BBT: - Skip bad blocks when searching for the BBT in NAND Raw NAND controller drivers: * Qcom: - Convert bindings to YAML - Use dma_mapping_error() for error check - Add missing nand_cleanup() in error path - Return actual error code instead of -ENODEV - Update last code word register - Add helper to configure location register - Rename parameter name in macro - Add helper to check last code word - Convert nandc to chip in Read/Write helper - Update register macro name for 0x2c offset * GPMI: - Fix a double free in gpmi_nand_init * Rockchip: - Use flexible-array member instead of zero-length array * Atmel: - Update ecc_stats.corrected counter * MXC: - Remove unneeded of_match_ptr() * R852: - replace spin_lock_irqsave by spin_lock in hard IRQ * Brcmnand: - Move to polling in pio mode on oops write - Read/write oob during EDU transfer - Fix OOB R/W with Hamming ECC * FSMC: - Fix error code in fsmc_nand_probe() * OMAP: - Use ECC information from the generic structures SPI-NAND core: * Add missing MODULE_DEVICE_TABLE() SPI-NAND drivers: * gigadevice: Support GD5F1GQ5UExxG
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/mtdcore.c3
-rw-r--r--drivers/mtd/nand/ecc-sw-bch.c10
-rw-r--r--drivers/mtd/nand/ecc-sw-hamming.c10
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c6
-rw-r--r--drivers/mtd/nand/raw/brcmnand/brcmnand.c69
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c2
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c2
-rw-r--r--drivers/mtd/nand/raw/mxc_nand.c2
-rw-r--r--drivers/mtd/nand/raw/nand_base.c145
-rw-r--r--drivers/mtd/nand/raw/nand_bbt.c5
-rw-r--r--drivers/mtd/nand/raw/omap2.c14
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c265
-rw-r--r--drivers/mtd/nand/raw/r852.c5
-rw-r--r--drivers/mtd/nand/raw/rockchip-nand-controller.c2
-rw-r--r--drivers/mtd/nand/spi/core.c2
-rw-r--r--drivers/mtd/nand/spi/gigadevice.c69
16 files changed, 444 insertions, 167 deletions
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index dab169d597c9..0bc6871c3863 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -822,6 +822,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
/* Prefer parsed partitions over driver-provided fallback */
ret = parse_mtd_partitions(mtd, types, parser_data);
+ if (ret == -EPROBE_DEFER)
+ goto out;
+
if (ret > 0)
ret = 0;
else if (nr_parts)
diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c
index 0a0ac11d5725..405552d014a8 100644
--- a/drivers/mtd/nand/ecc-sw-bch.c
+++ b/drivers/mtd/nand/ecc-sw-bch.c
@@ -236,7 +236,6 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
goto free_engine_conf;
engine_conf->code_size = code_size;
- engine_conf->nsteps = nsteps;
engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
if (!engine_conf->calc_buf || !engine_conf->code_buf) {
@@ -245,6 +244,7 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
}
nand->ecc.ctx.priv = engine_conf;
+ nand->ecc.ctx.nsteps = nsteps;
nand->ecc.ctx.total = nsteps * code_size;
ret = nand_ecc_sw_bch_init(nand);
@@ -253,7 +253,7 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
/* Verify the layout validity */
if (mtd_ooblayout_count_eccbytes(mtd) !=
- engine_conf->nsteps * engine_conf->code_size) {
+ nand->ecc.ctx.nsteps * engine_conf->code_size) {
pr_err("Invalid ECC layout\n");
ret = -EINVAL;
goto cleanup_bch_ctx;
@@ -295,7 +295,7 @@ static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
struct mtd_info *mtd = nanddev_to_mtd(nand);
int eccsize = nand->ecc.ctx.conf.step_size;
int eccbytes = engine_conf->code_size;
- int eccsteps = engine_conf->nsteps;
+ int eccsteps = nand->ecc.ctx.nsteps;
int total = nand->ecc.ctx.total;
u8 *ecccalc = engine_conf->calc_buf;
const u8 *data;
@@ -333,7 +333,7 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
int eccsize = nand->ecc.ctx.conf.step_size;
int total = nand->ecc.ctx.total;
int eccbytes = engine_conf->code_size;
- int eccsteps = engine_conf->nsteps;
+ int eccsteps = nand->ecc.ctx.nsteps;
u8 *ecccalc = engine_conf->calc_buf;
u8 *ecccode = engine_conf->code_buf;
unsigned int max_bitflips = 0;
@@ -365,7 +365,7 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]);
/* Finish a page read: compare and correct */
- for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
+ for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in;
eccsteps;
eccsteps--, i += eccbytes, data += eccsize) {
int stat = nand_ecc_sw_bch_correct(nand, data,
diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c
index 6334d1d7735d..a7655b668f32 100644
--- a/drivers/mtd/nand/ecc-sw-hamming.c
+++ b/drivers/mtd/nand/ecc-sw-hamming.c
@@ -504,7 +504,6 @@ int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
goto free_engine_conf;
engine_conf->code_size = 3;
- engine_conf->nsteps = mtd->writesize / conf->step_size;
engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
if (!engine_conf->calc_buf || !engine_conf->code_buf) {
@@ -513,7 +512,8 @@ int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
}
nand->ecc.ctx.priv = engine_conf;
- nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size;
+ nand->ecc.ctx.nsteps = mtd->writesize / conf->step_size;
+ nand->ecc.ctx.total = nand->ecc.ctx.nsteps * engine_conf->code_size;
return 0;
@@ -548,7 +548,7 @@ static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand,
struct mtd_info *mtd = nanddev_to_mtd(nand);
int eccsize = nand->ecc.ctx.conf.step_size;
int eccbytes = engine_conf->code_size;
- int eccsteps = engine_conf->nsteps;
+ int eccsteps = nand->ecc.ctx.nsteps;
int total = nand->ecc.ctx.total;
u8 *ecccalc = engine_conf->calc_buf;
const u8 *data;
@@ -586,7 +586,7 @@ static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
int eccsize = nand->ecc.ctx.conf.step_size;
int total = nand->ecc.ctx.total;
int eccbytes = engine_conf->code_size;
- int eccsteps = engine_conf->nsteps;
+ int eccsteps = nand->ecc.ctx.nsteps;
u8 *ecccalc = engine_conf->calc_buf;
u8 *ecccode = engine_conf->code_buf;
unsigned int max_bitflips = 0;
@@ -618,7 +618,7 @@ static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]);
/* Finish a page read: compare and correct */
- for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in;
+ for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in;
eccsteps;
eccsteps--, i += eccbytes, data += eccsize) {
int stat = nand_ecc_sw_hamming_correct(nand, data,
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index e6ceec8f50dc..8aab1017b460 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -883,10 +883,12 @@ static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
NULL, 0,
chip->ecc.strength);
- if (ret >= 0)
+ if (ret >= 0) {
+ mtd->ecc_stats.corrected += ret;
max_bitflips = max(ret, max_bitflips);
- else
+ } else {
mtd->ecc_stats.failed++;
+ }
databuf += chip->ecc.size;
eccbuf += chip->ecc.bytes;
diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
index 659eaa6f0980..f75929783b94 100644
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
@@ -242,6 +242,9 @@ struct brcmnand_controller {
u32 edu_ext_addr;
u32 edu_cmd;
u32 edu_config;
+ int sas; /* spare area size, per flash cache */
+ int sector_size_1k;
+ u8 *oob;
/* flash_dma reg */
const u16 *flash_dma_offsets;
@@ -249,7 +252,7 @@ struct brcmnand_controller {
dma_addr_t dma_pa;
int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf,
- u32 len, u8 dma_cmd);
+ u8 *oob, u32 len, u8 dma_cmd);
/* in-memory cache of the FLASH_CACHE, used only for some commands */
u8 flash_cache[FC_BYTES];
@@ -1479,6 +1482,23 @@ static irqreturn_t brcmnand_edu_irq(int irq, void *data)
edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr);
edu_readl(ctrl, EDU_EXT_ADDR);
+ if (ctrl->oob) {
+ if (ctrl->edu_cmd == EDU_CMD_READ) {
+ ctrl->oob += read_oob_from_regs(ctrl,
+ ctrl->edu_count + 1,
+ ctrl->oob, ctrl->sas,
+ ctrl->sector_size_1k);
+ } else {
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ ctrl->edu_ext_addr);
+ brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+ ctrl->oob += write_oob_to_regs(ctrl,
+ ctrl->edu_count,
+ ctrl->oob, ctrl->sas,
+ ctrl->sector_size_1k);
+ }
+ }
+
mb(); /* flush previous writes */
edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd);
edu_readl(ctrl, EDU_CMD);
@@ -1850,9 +1870,10 @@ static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf,
* Kick EDU engine
*/
static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
- u32 len, u8 cmd)
+ u8 *oob, u32 len, u8 cmd)
{
struct brcmnand_controller *ctrl = host->ctrl;
+ struct brcmnand_cfg *cfg = &host->hwcfg;
unsigned long timeo = msecs_to_jiffies(200);
int ret = 0;
int dir = (cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
@@ -1860,6 +1881,9 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
unsigned int trans = len >> FC_SHIFT;
dma_addr_t pa;
+ dev_dbg(ctrl->dev, "EDU %s %p:%p\n", ((edu_cmd == EDU_CMD_READ) ?
+ "read" : "write"), buf, oob);
+
pa = dma_map_single(ctrl->dev, buf, len, dir);
if (dma_mapping_error(ctrl->dev, pa)) {
dev_err(ctrl->dev, "unable to map buffer for EDU DMA\n");
@@ -1871,6 +1895,8 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
ctrl->edu_ext_addr = addr;
ctrl->edu_cmd = edu_cmd;
ctrl->edu_count = trans;
+ ctrl->sas = cfg->spare_area_size;
+ ctrl->oob = oob;
edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr);
edu_readl(ctrl, EDU_DRAM_ADDR);
@@ -1879,6 +1905,16 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
edu_writel(ctrl, EDU_LENGTH, FC_BYTES);
edu_readl(ctrl, EDU_LENGTH);
+ if (ctrl->oob && (ctrl->edu_cmd == EDU_CMD_WRITE)) {
+ brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+ ctrl->edu_ext_addr);
+ brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+ ctrl->oob += write_oob_to_regs(ctrl,
+ 1,
+ ctrl->oob, ctrl->sas,
+ ctrl->sector_size_1k);
+ }
+
/* Start edu engine */
mb(); /* flush previous writes */
edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd);
@@ -1893,6 +1929,14 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
dma_unmap_single(ctrl->dev, pa, len, dir);
+ /* read last subpage oob */
+ if (ctrl->oob && (ctrl->edu_cmd == EDU_CMD_READ)) {
+ ctrl->oob += read_oob_from_regs(ctrl,
+ 1,
+ ctrl->oob, ctrl->sas,
+ ctrl->sector_size_1k);
+ }
+
/* for program page check NAND status */
if (((brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
INTFC_FLASH_STATUS) & NAND_STATUS_FAIL) &&
@@ -2002,7 +2046,7 @@ static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
}
static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
- u32 len, u8 dma_cmd)
+ u8 *oob, u32 len, u8 dma_cmd)
{
struct brcmnand_controller *ctrl = host->ctrl;
dma_addr_t buf_pa;
@@ -2147,8 +2191,9 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
try_dmaread:
brcmnand_clear_ecc_addr(ctrl);
- if (ctrl->dma_trans && !oob && flash_dma_buf_ok(buf)) {
- err = ctrl->dma_trans(host, addr, buf,
+ if (ctrl->dma_trans && (has_edu(ctrl) || !oob) &&
+ flash_dma_buf_ok(buf)) {
+ err = ctrl->dma_trans(host, addr, buf, oob,
trans * FC_BYTES,
CMD_PAGE_READ);
@@ -2296,8 +2341,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < ctrl->max_oob; i += 4)
oob_reg_write(ctrl, i, 0xffffffff);
- if (use_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
- if (ctrl->dma_trans(host, addr, (u32 *)buf, mtd->writesize,
+ if (mtd->oops_panic_write)
+ /* switch to interrupt polling and PIO mode */
+ disable_ctrl_irqs(ctrl);
+
+ if (use_dma(ctrl) && (has_edu(ctrl) || !oob) && flash_dma_buf_ok(buf)) {
+ if (ctrl->dma_trans(host, addr, (u32 *)buf, oob, mtd->writesize,
CMD_PROGRAM_PAGE))
ret = -EIO;
@@ -2688,6 +2737,12 @@ static int brcmnand_attach_chip(struct nand_chip *chip)
ret = brcmstb_choose_ecc_layout(host);
+ /* If OOB is written with ECC enabled it will cause ECC errors */
+ if (is_hamming_ecc(host->ctrl, &host->hwcfg)) {
+ chip->ecc.write_oob = brcmnand_write_oob_raw;
+ chip->ecc.read_oob = brcmnand_read_oob_raw;
+ }
+
return ret;
}
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index a5d1bd0ff135..bf695255b43a 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -1078,11 +1078,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
host->read_dma_chan = dma_request_channel(mask, filter, NULL);
if (!host->read_dma_chan) {
dev_err(&pdev->dev, "Unable to get read dma channel\n");
+ ret = -ENODEV;
goto disable_clk;
}
host->write_dma_chan = dma_request_channel(mask, filter, NULL);
if (!host->write_dma_chan) {
dev_err(&pdev->dev, "Unable to get write dma channel\n");
+ ret = -ENODEV;
goto release_dma_read_chan;
}
}
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index 3fa8c22d3f36..4d08e4ab5c1b 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -2449,7 +2449,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
this->bch_geometry.auxiliary_size = 128;
ret = gpmi_alloc_dma_buffer(this);
if (ret)
- goto err_out;
+ return ret;
nand_controller_init(&this->base);
this->base.ops = &gpmi_nand_controller_ops;
diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c
index f78302e16b84..f6c96341b896 100644
--- a/drivers/mtd/nand/raw/mxc_nand.c
+++ b/drivers/mtd/nand/raw/mxc_nand.c
@@ -1849,7 +1849,7 @@ static int mxcnd_remove(struct platform_device *pdev)
static struct platform_driver mxcnd_driver = {
.driver = {
.name = DRIVER_NAME,
- .of_match_table = of_match_ptr(mxcnd_dt_ids),
+ .of_match_table = mxcnd_dt_ids,
},
.probe = mxcnd_probe,
.remove = mxcnd_remove,
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index c33fa1b1847f..fb072c444495 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -278,11 +278,48 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
return 0;
}
+/**
+ * nand_region_is_secured() - Check if the region is secured
+ * @chip: NAND chip object
+ * @offset: Offset of the region to check
+ * @size: Size of the region to check
+ *
+ * Checks if the region is secured by comparing the offset and size with the
+ * list of secure regions obtained from DT. Returns true if the region is
+ * secured else false.
+ */
+static bool nand_region_is_secured(struct nand_chip *chip, loff_t offset, u64 size)
+{
+ int i;
+
+ /* Skip touching the secure regions if present */
+ for (i = 0; i < chip->nr_secure_regions; i++) {
+ const struct nand_secure_region *region = &chip->secure_regions[i];
+
+ if (offset + size <= region->offset ||
+ offset >= region->offset + region->size)
+ continue;
+
+ pr_debug("%s: Region 0x%llx - 0x%llx is secured!",
+ __func__, offset, offset + size);
+
+ return true;
+ }
+
+ return false;
+}
+
static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
{
+ struct mtd_info *mtd = nand_to_mtd(chip);
+
if (chip->options & NAND_NO_BBM_QUIRK)
return 0;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, ofs, mtd->erasesize))
+ return -EIO;
+
if (chip->legacy.block_bad)
return chip->legacy.block_bad(chip, ofs);
@@ -397,6 +434,10 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
return -EINVAL;
}
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, to, ops->ooblen))
+ return -EIO;
+
chipnr = (int)(to >> chip->chip_shift);
/*
@@ -1294,8 +1335,6 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
};
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
- int ret;
- u8 status;
if (naddrs < 0)
return naddrs;
@@ -1335,15 +1374,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
op.ninstrs--;
}
- ret = nand_exec_op(chip, &op);
- if (!prog || ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
-
- return status;
+ return nand_exec_op(chip, &op);
}
/**
@@ -1449,7 +1480,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
unsigned int len)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int status;
+ u8 status;
+ int ret;
if (!len || !buf)
return -EINVAL;
@@ -1458,14 +1490,24 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
return -EINVAL;
if (nand_has_exec_op(chip)) {
- status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
+ ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
len, true);
+ if (ret)
+ return ret;
+
+ ret = nand_status_op(chip, &status);
+ if (ret)
+ return ret;
} else {
chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page,
page);
chip->legacy.write_buf(chip, buf, len);
chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->legacy.waitfunc(chip);
+ ret = chip->legacy.waitfunc(chip);
+ if (ret < 0)
+ return ret;
+
+ status = ret;
}
if (status & NAND_STATUS_FAIL)
@@ -3127,6 +3169,10 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
int retry_mode = 0;
bool ecc_fail = false;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, from, readlen))
+ return -EIO;
+
chipnr = (int)(from >> chip->chip_shift);
nand_select_target(chip, chipnr);
@@ -3458,6 +3504,10 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
pr_debug("%s: from = 0x%08Lx, len = %i\n",
__func__, (unsigned long long)from, readlen);
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, from, readlen))
+ return -EIO;
+
stats = mtd->ecc_stats;
len = mtd_oobavail(mtd, ops);
@@ -3979,6 +4029,10 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
return -EINVAL;
}
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, to, writelen))
+ return -EIO;
+
column = to & (mtd->writesize - 1);
chipnr = (int)(to >> chip->chip_shift);
@@ -4180,6 +4234,10 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
if (check_offs_len(chip, instr->addr, instr->len))
return -EINVAL;
+ /* Check if the region is secured */
+ if (nand_region_is_secured(chip, instr->addr, instr->len))
+ return -EIO;
+
/* Grab the lock and see if the device is available */
ret = nand_get_device(chip);
if (ret)
@@ -4995,6 +5053,31 @@ static bool of_get_nand_on_flash_bbt(struct device_node *np)
return of_property_read_bool(np, "nand-on-flash-bbt");
}
+static int of_get_nand_secure_regions(struct nand_chip *chip)
+{
+ struct device_node *dn = nand_get_flash_node(chip);
+ int nr_elem, i, j;
+
+ nr_elem = of_property_count_elems_of_size(dn, "secure-regions", sizeof(u64));
+ if (!nr_elem)
+ return 0;
+
+ chip->nr_secure_regions = nr_elem / 2;
+ chip->secure_regions = kcalloc(chip->nr_secure_regions, sizeof(*chip->secure_regions),
+ GFP_KERNEL);
+ if (!chip->secure_regions)
+ return -ENOMEM;
+
+ for (i = 0, j = 0; i < chip->nr_secure_regions; i++, j += 2) {
+ of_property_read_u64_index(dn, "secure-regions", j,
+ &chip->secure_regions[i].offset);
+ of_property_read_u64_index(dn, "secure-regions", j + 1,
+ &chip->secure_regions[i].size);
+ }
+
+ return 0;
+}
+
static int rawnand_dt_init(struct nand_chip *chip)
{
struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip));
@@ -5162,8 +5245,8 @@ int rawnand_sw_hamming_init(struct nand_chip *chip)
chip->ecc.size = base->ecc.ctx.conf.step_size;
chip->ecc.strength = base->ecc.ctx.conf.strength;
chip->ecc.total = base->ecc.ctx.total;
- chip->ecc.steps = engine_conf->nsteps;
- chip->ecc.bytes = engine_conf->code_size;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
return 0;
}
@@ -5201,7 +5284,7 @@ EXPORT_SYMBOL(rawnand_sw_hamming_cleanup);
int rawnand_sw_bch_init(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
- struct nand_ecc_sw_bch_conf *engine_conf;
+ const struct nand_ecc_props *ecc_conf = nanddev_get_ecc_conf(base);
int ret;
base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
@@ -5213,13 +5296,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip)
if (ret)
return ret;
- engine_conf = base->ecc.ctx.priv;
-
- chip->ecc.size = base->ecc.ctx.conf.step_size;
- chip->ecc.strength = base->ecc.ctx.conf.strength;
+ chip->ecc.size = ecc_conf->step_size;
+ chip->ecc.strength = ecc_conf->strength;
chip->ecc.total = base->ecc.ctx.total;
- chip->ecc.steps = engine_conf->nsteps;
- chip->ecc.bytes = engine_conf->code_size;
+ chip->ecc.steps = nanddev_get_ecc_nsteps(base);
+ chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base);
return 0;
}
@@ -5953,6 +6034,16 @@ static int nand_scan_tail(struct nand_chip *chip)
goto err_free_interface_config;
}
+ /*
+ * Look for secure regions in the NAND chip. These regions are supposed
+ * to be protected by a secure element like Trustzone. So the read/write
+ * accesses to these regions will be blocked in the runtime by this
+ * driver.
+ */
+ ret = of_get_nand_secure_regions(chip);
+ if (ret)
+ goto err_free_interface_config;
+
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
return 0;
@@ -5960,10 +6051,13 @@ static int nand_scan_tail(struct nand_chip *chip)
/* Build bad block table */
ret = nand_create_bbt(chip);
if (ret)
- goto err_free_interface_config;
+ goto err_free_secure_regions;
return 0;
+err_free_secure_regions:
+ kfree(chip->secure_regions);
+
err_free_interface_config:
kfree(chip->best_interface_config);
@@ -6051,6 +6145,9 @@ void nand_cleanup(struct nand_chip *chip)
nanddev_cleanup(&chip->base);
+ /* Free secure regions data */
+ kfree(chip->secure_regions);
+
/* Free bad block table memory */
kfree(chip->bbt);
kfree(chip->data_buf);
diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c
index dced32a126d9..6e25a5ce5ba9 100644
--- a/drivers/mtd/nand/raw/nand_bbt.c
+++ b/drivers/mtd/nand/raw/nand_bbt.c
@@ -525,6 +525,7 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
{
u64 targetsize = nanddev_target_size(&this->base);
struct mtd_info *mtd = nand_to_mtd(this);
+ struct nand_bbt_descr *bd = this->badblock_pattern;
int i, chips;
int startblock, block, dir;
int scanlen = mtd->writesize + mtd->oobsize;
@@ -560,6 +561,10 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
int actblock = startblock + dir * block;
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
+ /* Check if block is marked bad */
+ if (scan_block_fast(this, bd, offs, buf))
+ continue;
+
/* Read first page */
scan_read(this, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index 2c3e65cb68f3..c75e7a0b101f 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -1868,18 +1868,19 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_device *nand = mtd_to_nanddev(mtd);
- const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
+ unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
int off = BADBLOCK_MARKER_LENGTH;
- if (section >= engine_conf->nsteps)
+ if (section >= nsteps)
return -ERANGE;
/*
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
- oobregion->offset = off + (section * (engine_conf->code_size + 1));
- oobregion->length = engine_conf->code_size;
+ oobregion->offset = off + (section * (ecc_bytes + 1));
+ oobregion->length = ecc_bytes;
return 0;
}
@@ -1888,7 +1889,8 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_device *nand = mtd_to_nanddev(mtd);
- const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
+ unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
+ unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
int off = BADBLOCK_MARKER_LENGTH;
if (section)
@@ -1898,7 +1900,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
- off += ((engine_conf->code_size + 1) * engine_conf->nsteps);
+ off += ((ecc_bytes + 1) * nsteps);
if (off >= mtd->oobsize)
return -ERANGE;
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index fd4c318b520f..a64fb6ce915d 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -27,7 +27,7 @@
#define NAND_DEV0_CFG0 0x20
#define NAND_DEV0_CFG1 0x24
#define NAND_DEV0_ECC_CFG 0x28
-#define NAND_DEV1_ECC_CFG 0x2c
+#define NAND_AUTO_STATUS_EN 0x2c
#define NAND_DEV1_CFG0 0x30
#define NAND_DEV1_CFG1 0x34
#define NAND_READ_ID 0x40
@@ -48,6 +48,10 @@
#define NAND_READ_LOCATION_1 0xf24
#define NAND_READ_LOCATION_2 0xf28
#define NAND_READ_LOCATION_3 0xf2c
+#define NAND_READ_LOCATION_LAST_CW_0 0xf40
+#define NAND_READ_LOCATION_LAST_CW_1 0xf44
+#define NAND_READ_LOCATION_LAST_CW_2 0xf48
+#define NAND_READ_LOCATION_LAST_CW_3 0xf4c
/* dummy register offsets, used by write_reg_dma */
#define NAND_DEV_CMD1_RESTORE 0xdead
@@ -181,12 +185,17 @@
#define ECC_BCH_4BIT BIT(2)
#define ECC_BCH_8BIT BIT(3)
-#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \
-nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \
- ((offset) << READ_LOCATION_OFFSET) | \
- ((size) << READ_LOCATION_SIZE) | \
- ((is_last) << READ_LOCATION_LAST))
-
+#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc) \
+nandc_set_reg(chip, reg, \
+ ((cw_offset) << READ_LOCATION_OFFSET) | \
+ ((read_size) << READ_LOCATION_SIZE) | \
+ ((is_last_read_loc) << READ_LOCATION_LAST))
+
+#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc) \
+nandc_set_reg(chip, reg, \
+ ((cw_offset) << READ_LOCATION_OFFSET) | \
+ ((read_size) << READ_LOCATION_SIZE) | \
+ ((is_last_read_loc) << READ_LOCATION_LAST))
/*
* Returns the actual register address for all NAND_DEV_ registers
* (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
@@ -316,6 +325,10 @@ struct nandc_regs {
__le32 read_location1;
__le32 read_location2;
__le32 read_location3;
+ __le32 read_location_last0;
+ __le32 read_location_last1;
+ __le32 read_location_last2;
+ __le32 read_location_last3;
__le32 erased_cw_detect_cfg_clr;
__le32 erased_cw_detect_cfg_set;
@@ -644,14 +657,23 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
return &regs->read_location2;
case NAND_READ_LOCATION_3:
return &regs->read_location3;
+ case NAND_READ_LOCATION_LAST_CW_0:
+ return &regs->read_location_last0;
+ case NAND_READ_LOCATION_LAST_CW_1:
+ return &regs->read_location_last1;
+ case NAND_READ_LOCATION_LAST_CW_2:
+ return &regs->read_location_last2;
+ case NAND_READ_LOCATION_LAST_CW_3:
+ return &regs->read_location_last3;
default:
return NULL;
}
}
-static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset,
+static void nandc_set_reg(struct nand_chip *chip, int offset,
u32 val)
{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nandc_regs *regs = nandc->regs;
__le32 *reg;
@@ -661,17 +683,43 @@ static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset,
*reg = cpu_to_le32(val);
}
+/* Helper to check the code word, whether it is last cw or not */
+static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw)
+{
+ return cw == (ecc->steps - 1);
+}
+
+/* helper to configure location register values */
+static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg,
+ int cw_offset, int read_size, int is_last_read_loc)
+{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int reg_base = NAND_READ_LOCATION_0;
+
+ if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+ reg_base = NAND_READ_LOCATION_LAST_CW_0;
+
+ reg_base += reg * 4;
+
+ if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+ return nandc_set_read_loc_last(chip, reg_base, cw_offset,
+ read_size, is_last_read_loc);
+ else
+ return nandc_set_read_loc_first(chip, reg_base, cw_offset,
+ read_size, is_last_read_loc);
+}
+
/* helper to configure address register values */
static void set_address(struct qcom_nand_host *host, u16 column, int page)
{
struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column);
- nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff);
+ nandc_set_reg(chip, NAND_ADDR0, page << 16 | column);
+ nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff);
}
/*
@@ -680,11 +728,11 @@ static void set_address(struct qcom_nand_host *host, u16 column, int page)
*
* @num_cw: number of steps for the read/write operation
* @read: read or write operation
+ * @cw : which code word
*/
-static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
+static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw)
{
struct nand_chip *chip = &host->chip;
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
u32 cmd, cfg0, cfg1, ecc_bch_cfg;
if (read) {
@@ -710,17 +758,17 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read)
ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
}
- nandc_set_reg(nandc, NAND_FLASH_CMD, cmd);
- nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0);
- nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1);
- nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
- nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
- nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
- nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+ nandc_set_reg(chip, NAND_FLASH_CMD, cmd);
+ nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0);
+ nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1);
+ nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
+ nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
+ nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus);
+ nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus);
+ nandc_set_reg(chip, NAND_EXEC_CMD, 1);
if (read)
- nandc_set_read_loc(nandc, 0, 0, host->use_ecc ?
+ nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ?
host->cw_data : host->cw_size, 1);
}
@@ -1079,8 +1127,10 @@ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
* Helper to prepare DMA descriptors for configuring registers
* before reading a NAND page.
*/
-static void config_nand_page_read(struct qcom_nand_controller *nandc)
+static void config_nand_page_read(struct nand_chip *chip)
{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
write_reg_dma(nandc, NAND_ADDR0, 2, 0);
write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
@@ -1094,11 +1144,18 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc)
* before reading each codeword in NAND page.
*/
static void
-config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc)
+config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw)
{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+ int reg = NAND_READ_LOCATION_0;
+
+ if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+ reg = NAND_READ_LOCATION_LAST_CW_0;
+
if (nandc->props->is_bam)
- write_reg_dma(nandc, NAND_READ_LOCATION_0, 4,
- NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1117,19 +1174,21 @@ config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc)
* single codeword in page
*/
static void
-config_nand_single_cw_page_read(struct qcom_nand_controller *nandc,
- bool use_ecc)
+config_nand_single_cw_page_read(struct nand_chip *chip,
+ bool use_ecc, int cw)
{
- config_nand_page_read(nandc);
- config_nand_cw_read(nandc, use_ecc);
+ config_nand_page_read(chip);
+ config_nand_cw_read(chip, use_ecc, cw);
}
/*
* Helper to prepare DMA descriptors used to configure registers needed for
* before writing a NAND page.
*/
-static void config_nand_page_write(struct qcom_nand_controller *nandc)
+static void config_nand_page_write(struct nand_chip *chip)
{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
write_reg_dma(nandc, NAND_ADDR0, 2, 0);
write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
@@ -1140,8 +1199,10 @@ static void config_nand_page_write(struct qcom_nand_controller *nandc)
* Helper to prepare DMA descriptors for configuring registers
* before writing each codeword in NAND page.
*/
-static void config_nand_cw_write(struct qcom_nand_controller *nandc)
+static void config_nand_cw_write(struct nand_chip *chip)
{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1168,44 +1229,44 @@ static int nandc_param(struct qcom_nand_host *host)
* bytes to read onfi params
*/
if (nandc->props->qpic_v2)
- nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ |
+ nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ |
PAGE_ACC | LAST_PAGE);
else
- nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ |
+ nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ |
PAGE_ACC | LAST_PAGE);
- nandc_set_reg(nandc, NAND_ADDR0, 0);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
+ nandc_set_reg(chip, NAND_ADDR0, 0);
+ nandc_set_reg(chip, NAND_ADDR1, 0);
+ nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
| 512 << UD_SIZE_BYTES
| 5 << NUM_ADDR_CYCLES
| 0 << SPARE_SIZE_BYTES);
- nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
+ nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
| 0 << CS_ACTIVE_BSY
| 17 << BAD_BLOCK_BYTE_NUM
| 1 << BAD_BLOCK_IN_SPARE_AREA
| 2 << WR_RD_BSY_GAP
| 0 << WIDE_FLASH
| 1 << DEV0_CFG1_ECC_DISABLE);
- nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
+ nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
if (!nandc->props->qpic_v2) {
- nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
+ nandc_set_reg(chip, NAND_DEV_CMD_VLD,
(nandc->vld & ~READ_START_VLD));
- nandc_set_reg(nandc, NAND_DEV_CMD1,
+ nandc_set_reg(chip, NAND_DEV_CMD1,
(nandc->cmd1 & ~(0xFF << READ_ADDR))
| NAND_CMD_PARAM << READ_ADDR);
}
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+ nandc_set_reg(chip, NAND_EXEC_CMD, 1);
if (!nandc->props->qpic_v2) {
- nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
- nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
+ nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
+ nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
}
- nandc_set_read_loc(nandc, 0, 0, 512, 1);
+ nandc_set_read_loc(chip, 0, 0, 0, 512, 1);
if (!nandc->props->qpic_v2) {
write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
@@ -1215,7 +1276,7 @@ static int nandc_param(struct qcom_nand_host *host)
nandc->buf_count = 512;
memset(nandc->data_buffer, 0xff, nandc->buf_count);
- config_nand_single_cw_page_read(nandc, false);
+ config_nand_single_cw_page_read(chip, false, 0);
read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
nandc->buf_count, 0);
@@ -1235,16 +1296,16 @@ static int erase_block(struct qcom_nand_host *host, int page_addr)
struct nand_chip *chip = &host->chip;
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- nandc_set_reg(nandc, NAND_FLASH_CMD,
+ nandc_set_reg(chip, NAND_FLASH_CMD,
OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE);
- nandc_set_reg(nandc, NAND_ADDR0, page_addr);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_DEV0_CFG0,
+ nandc_set_reg(chip, NAND_ADDR0, page_addr);
+ nandc_set_reg(chip, NAND_ADDR1, 0);
+ nandc_set_reg(chip, NAND_DEV0_CFG0,
host->cfg0_raw & ~(7 << CW_PER_PAGE));
- nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
- nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus);
- nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus);
+ nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw);
+ nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+ nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus);
+ nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus);
write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
@@ -1267,12 +1328,12 @@ static int read_id(struct qcom_nand_host *host, int column)
if (column == -1)
return 0;
- nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID);
- nandc_set_reg(nandc, NAND_ADDR0, column);
- nandc_set_reg(nandc, NAND_ADDR1, 0);
- nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT,
+ nandc_set_reg(chip, NAND_FLASH_CMD, OP_FETCH_ID);
+ nandc_set_reg(chip, NAND_ADDR0, column);
+ nandc_set_reg(chip, NAND_ADDR1, 0);
+ nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT,
nandc->props->is_bam ? 0 : DM_EN);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+ nandc_set_reg(chip, NAND_EXEC_CMD, 1);
write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1288,8 +1349,8 @@ static int reset(struct qcom_nand_host *host)
struct nand_chip *chip = &host->chip;
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE);
- nandc_set_reg(nandc, NAND_EXEC_CMD, 1);
+ nandc_set_reg(chip, NAND_FLASH_CMD, OP_RESET_DEVICE);
+ nandc_set_reg(chip, NAND_EXEC_CMD, 1);
write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1492,7 +1553,7 @@ static void qcom_nandc_command(struct nand_chip *chip, unsigned int command,
host->use_ecc = true;
set_address(host, 0, page_addr);
- update_rw_regs(host, ecc->steps, true);
+ update_rw_regs(host, ecc->steps, true, 0);
break;
case NAND_CMD_SEQIN:
@@ -1616,13 +1677,13 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
clear_bam_transaction(nandc);
set_address(host, host->cw_size * cw, page);
- update_rw_regs(host, 1, true);
- config_nand_page_read(nandc);
+ update_rw_regs(host, 1, true, cw);
+ config_nand_page_read(chip);
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
oob_size1 = host->bbm_size;
- if (cw == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, cw)) {
data_size2 = ecc->size - data_size1 -
((ecc->steps - 1) * 4);
oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
@@ -1633,19 +1694,19 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
if (nandc->props->is_bam) {
- nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0);
+ nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0);
read_loc += data_size1;
- nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0);
+ nandc_set_read_loc(chip, cw, 1, read_loc, oob_size1, 0);
read_loc += oob_size1;
- nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0);
+ nandc_set_read_loc(chip, cw, 2, read_loc, data_size2, 0);
read_loc += data_size2;
- nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1);
+ nandc_set_read_loc(chip, cw, 3, read_loc, oob_size2, 1);
}
- config_nand_cw_read(nandc, false);
+ config_nand_cw_read(chip, false, cw);
read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
reg_off += data_size1;
@@ -1703,7 +1764,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
}
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
- if (cw == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, cw)) {
data_size = ecc->size - ((ecc->steps - 1) * 4);
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
} else {
@@ -1763,7 +1824,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
u32 flash, buffer, erased_cw;
int data_len, oob_len;
- if (i == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, i)) {
data_len = ecc->size - ((ecc->steps - 1) << 2);
oob_len = ecc->steps << 2;
} else {
@@ -1856,13 +1917,13 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
int i, ret;
- config_nand_page_read(nandc);
+ config_nand_page_read(chip);
/* queue cmd descs for each codeword */
for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size;
- if (i == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, i)) {
data_size = ecc->size - ((ecc->steps - 1) << 2);
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
host->spare_bytes;
@@ -1873,18 +1934,18 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
if (nandc->props->is_bam) {
if (data_buf && oob_buf) {
- nandc_set_read_loc(nandc, 0, 0, data_size, 0);
- nandc_set_read_loc(nandc, 1, data_size,
+ nandc_set_read_loc(chip, i, 0, 0, data_size, 0);
+ nandc_set_read_loc(chip, i, 1, data_size,
oob_size, 1);
} else if (data_buf) {
- nandc_set_read_loc(nandc, 0, 0, data_size, 1);
+ nandc_set_read_loc(chip, i, 0, 0, data_size, 1);
} else {
- nandc_set_read_loc(nandc, 0, data_size,
+ nandc_set_read_loc(chip, i, 0, data_size,
oob_size, 1);
}
}
- config_nand_cw_read(nandc, true);
+ config_nand_cw_read(chip, true, i);
if (data_buf)
read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
@@ -1944,9 +2005,9 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
memset(nandc->data_buffer, 0xff, size);
set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, true);
+ update_rw_regs(host, 1, true, ecc->steps - 1);
- config_nand_single_cw_page_read(nandc, host->use_ecc);
+ config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1);
read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
@@ -2011,7 +2072,7 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page)
host->use_ecc = true;
set_address(host, 0, page);
- update_rw_regs(host, ecc->steps, true);
+ update_rw_regs(host, ecc->steps, true, 0);
return read_page_ecc(host, NULL, chip->oob_poi, page);
}
@@ -2035,13 +2096,13 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
oob_buf = chip->oob_poi;
host->use_ecc = true;
- update_rw_regs(host, ecc->steps, false);
- config_nand_page_write(nandc);
+ update_rw_regs(host, ecc->steps, false, 0);
+ config_nand_page_write(chip);
for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size;
- if (i == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, i)) {
data_size = ecc->size - ((ecc->steps - 1) << 2);
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
host->spare_bytes;
@@ -2061,14 +2122,14 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
* itself. For the last codeword, we skip the bbm positions and
* write to the free oob area.
*/
- if (i == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, i)) {
oob_buf += host->bbm_size;
write_data_dma(nandc, FLASH_BUF_ACC + data_size,
oob_buf, oob_size, 0);
}
- config_nand_cw_write(nandc);
+ config_nand_cw_write(chip);
data_buf += data_size;
oob_buf += oob_size;
@@ -2106,8 +2167,8 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
oob_buf = chip->oob_poi;
host->use_ecc = false;
- update_rw_regs(host, ecc->steps, false);
- config_nand_page_write(nandc);
+ update_rw_regs(host, ecc->steps, false, 0);
+ config_nand_page_write(chip);
for (i = 0; i < ecc->steps; i++) {
int data_size1, data_size2, oob_size1, oob_size2;
@@ -2116,7 +2177,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
oob_size1 = host->bbm_size;
- if (i == (ecc->steps - 1)) {
+ if (qcom_nandc_is_last_cw(ecc, i)) {
data_size2 = ecc->size - data_size1 -
((ecc->steps - 1) << 2);
oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
@@ -2144,7 +2205,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
- config_nand_cw_write(nandc);
+ config_nand_cw_write(chip);
}
ret = submit_descs(nandc);
@@ -2189,12 +2250,12 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page)
0, mtd->oobavail);
set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, false);
+ update_rw_regs(host, 1, false, 0);
- config_nand_page_write(nandc);
+ config_nand_page_write(chip);
write_data_dma(nandc, FLASH_BUF_ACC,
nandc->data_buffer, data_size + oob_size, 0);
- config_nand_cw_write(nandc);
+ config_nand_cw_write(chip);
ret = submit_descs(nandc);
@@ -2268,12 +2329,12 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs)
/* prepare write */
host->use_ecc = false;
set_address(host, host->cw_size * (ecc->steps - 1), page);
- update_rw_regs(host, 1, false);
+ update_rw_regs(host, 1, false, ecc->steps - 1);
- config_nand_page_write(nandc);
+ config_nand_page_write(chip);
write_data_dma(nandc, FLASH_BUF_ACC,
nandc->data_buffer, host->cw_size, 0);
- config_nand_cw_write(nandc);
+ config_nand_cw_write(chip);
ret = submit_descs(nandc);
@@ -2882,6 +2943,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
if (!nandc->bam_txn) {
dev_err(nandc->dev,
"failed to allocate bam transaction\n");
+ nand_cleanup(chip);
return -ENOMEM;
}
}
@@ -2898,7 +2960,7 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
struct device *dev = nandc->dev;
struct device_node *dn = dev->of_node, *child;
struct qcom_nand_host *host;
- int ret;
+ int ret = -ENODEV;
for_each_available_child_of_node(dn, child) {
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
@@ -2916,10 +2978,7 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc)
list_add_tail(&host->node, &nandc->host_list);
}
- if (list_empty(&nandc->host_list))
- return -ENODEV;
-
- return 0;
+ return ret;
}
/* parse custom DT properties here */
@@ -2992,7 +3051,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
nandc->base_dma = dma_map_resource(dev, res->start,
resource_size(res),
DMA_BIDIRECTIONAL, 0);
- if (!nandc->base_dma)
+ if (dma_mapping_error(dev, nandc->base_dma))
return -ENXIO;
ret = qcom_nandc_alloc(nandc);
diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c
index c742354c1b0b..ebe859ca49cb 100644
--- a/drivers/mtd/nand/raw/r852.c
+++ b/drivers/mtd/nand/raw/r852.c
@@ -724,10 +724,9 @@ static irqreturn_t r852_irq(int irq, void *data)
struct r852_device *dev = (struct r852_device *)data;
uint8_t card_status, dma_status;
- unsigned long flags;
irqreturn_t ret = IRQ_NONE;
- spin_lock_irqsave(&dev->irqlock, flags);
+ spin_lock(&dev->irqlock);
/* handle card detection interrupts first */
card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
@@ -813,7 +812,7 @@ static irqreturn_t r852_irq(int irq, void *data)
dbg("strange card status = %x", card_status);
out:
- spin_unlock_irqrestore(&dev->irqlock, flags);
+ spin_unlock(&dev->irqlock);
return ret;
}
diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c
index 796b678cb108..b5405bc7ca3a 100644
--- a/drivers/mtd/nand/raw/rockchip-nand-controller.c
+++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c
@@ -159,7 +159,7 @@ struct rk_nfc_nand_chip {
u32 timing;
u8 nsels;
- u8 sels[0];
+ u8 sels[];
/* Nothing after this field. */
};
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 61d932c1b718..17f63f95f4a2 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1263,12 +1263,14 @@ static const struct spi_device_id spinand_ids[] = {
{ .name = "spi-nand" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(spi, spinand_ids);
#ifdef CONFIG_OF
static const struct of_device_id spinand_of_ids[] = {
{ .compatible = "spi-nand" },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, spinand_of_ids);
#endif
static struct spi_mem_driver spinand_drv = {
diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c
index 33c67403c4aa..1dd1c5898093 100644
--- a/drivers/mtd/nand/spi/gigadevice.c
+++ b/drivers/mtd/nand/spi/gigadevice.c
@@ -13,7 +13,10 @@
#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4)
#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4)
-#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0
+#define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4)
+#define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4)
+
+#define GD5FXGQXXEXXG_REG_STATUS2 0xf0
#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4)
#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4)
@@ -102,7 +105,7 @@ static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
-static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
+static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
@@ -114,7 +117,7 @@ static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
return 0;
}
-static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
+static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
@@ -127,9 +130,10 @@ static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
-static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = {
- .ecc = gd5fxgq4_variant2_ooblayout_ecc,
- .free = gd5fxgq4_variant2_ooblayout_free,
+/* Valid for Q4/Q5 and Q6 (untested) devices */
+static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = {
+ .ecc = gd5fxgqx_variant2_ooblayout_ecc,
+ .free = gd5fxgqx_variant2_ooblayout_free,
};
static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section,
@@ -165,7 +169,7 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
u8 status2;
- struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2,
+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2,
&status2);
int ret;
@@ -203,6 +207,43 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
+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,
+ &status2);
+ int ret;
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS:
+ /*
+ * Read status2 register to determine a more fine grained
+ * bit error status
+ */
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ /*
+ * 1 ... 4 bits are flipped (and corrected)
+ */
+ /* bits sorted this way (1...0): ECCSE1, ECCSE0 */
+ return ((status2 & STATUS_ECC_MASK) >> 4) + 1;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
u8 status)
{
@@ -282,7 +323,7 @@ static const struct spinand_info gigadevice_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4uexxg_ecc_get_status)),
SPINAND_INFO("GD5F1GQ4UFxxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
@@ -292,8 +333,18 @@ static const struct spinand_info gigadevice_spinand_table[] = {
&write_cache_variants,
&update_cache_variants),
SPINAND_HAS_QE_BIT,
- SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout,
+ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
gd5fxgq4ufxxg_ecc_get_status)),
+ SPINAND_INFO("GD5F1GQ5UExxG",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
+ NAND_MEMORG(1, 2048, 128, 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(&gd5fxgqx_variant2_ooblayout,
+ gd5fxgq5xexxg_ecc_get_status)),
};
static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {