From 6425dd136ddc40fc434156d1957a3adb367dd874 Mon Sep 17 00:00:00 2001 From: Wang Weiyang Date: Thu, 17 Mar 2022 19:39:07 +0800 Subject: mtd: rawnand: davinci: Remove redundant unsigned comparison to zero Since core_chipsel is uint32_t, comparison to zero is redundant Signed-off-by: Wang Weiyang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220317113907.96006-1-wangweiyang2@huawei.com --- drivers/mtd/nand/raw/davinci_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 45fec8c192ab..3e98e3c255bf 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -727,7 +727,7 @@ static int nand_davinci_probe(struct platform_device *pdev) return -ENODEV; /* which external chipselect will we be managing? */ - if (pdata->core_chipsel < 0 || pdata->core_chipsel > 3) + if (pdata->core_chipsel > 3) return -ENODEV; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); -- cgit v1.2.3 From a4f9dd55c5e1bb951db6f1dee20e62e0103f3438 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 20 Mar 2022 17:59:57 +0800 Subject: mtd: spinand: gigadevice: fix Quad IO for GD5F1GQ5UExxG Read From Cache Quad IO (EBH) uses 2 dummy bytes on this chip according to page 23 of the datasheet[0]. [0]: https://www.gigadevice.com/datasheet/gd5f1gq5xexxg/ Fixes: 469b99248985 ("mtd: spinand: gigadevice: Support GD5F1GQ5UExxG") Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-2-gch981213@gmail.com --- drivers/mtd/nand/spi/gigadevice.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 1dd1c5898093..da77ab20296e 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -39,6 +39,14 @@ static SPINAND_OP_VARIANTS(read_cache_variants_f, SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 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_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -339,7 +347,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { 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, + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, -- cgit v1.2.3 From 573eec222bc82fb5e724586267fbbb1aed9ffd03 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 20 Mar 2022 17:59:58 +0800 Subject: mtd: spinand: gigadevice: add support for GD5FxGQ4xExxG Add support for: GD5F1GQ4RExxG GD5F2GQ4{U,R}ExxG These chips differ from GD5F1GQ4UExxG only in chip ID, voltage and capacity. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-3-gch981213@gmail.com --- drivers/mtd/nand/spi/gigadevice.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index da77ab20296e..85a61d3d8467 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -333,6 +333,36 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1), + 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(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2), + 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(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ4RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2), + 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(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), SPINAND_INFO("GD5F1GQ4UFxxG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), -- cgit v1.2.3 From 620a988813403318023296b61228ee8f3fcdb8e0 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 20 Mar 2022 17:59:59 +0800 Subject: mtd: spinand: gigadevice: add support for GD5F1GQ5RExxG This chip is the 1.8v version of GD5F1GQ5UExxG. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-4-gch981213@gmail.com --- drivers/mtd/nand/spi/gigadevice.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 85a61d3d8467..d519bb85f0e7 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -383,6 +383,16 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ5RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &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 = { -- cgit v1.2.3 From 194ec04b3a9e7fa97d1fbef296410631bc3cf1c8 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 20 Mar 2022 18:00:00 +0800 Subject: mtd: spinand: gigadevice: add support for GD5F{2, 4}GQ5xExxG Add support for: GD5F2GQ5{U,R}ExxG GD5F4GQ6{U,R}ExxG These chips uses 4 dummy bytes for quad io and 2 dummy bytes for dual io. Besides that and memory layout, they are identical to their 1G variant. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-5-gch981213@gmail.com --- drivers/mtd/nand/spi/gigadevice.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index d519bb85f0e7..fcd1c4e474a2 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -47,6 +47,14 @@ static SPINAND_OP_VARIANTS(read_cache_variants_1gq5, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); +static SPINAND_OP_VARIANTS(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_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -393,6 +401,46 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ5UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ5RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ6UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ6RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5, + &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 = { -- cgit v1.2.3 From 54647cd003c08b714474a5b599a147ec6a160486 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 20 Mar 2022 18:00:01 +0800 Subject: mtd: spinand: gigadevice: add support for GD5FxGM7xExxG Add support for: GD5F{1,2}GM7{U,R}ExxG GD5F4GM8{U,R}ExxG These are new 27nm counterparts for the GD5FxGQ4 chips from GigaDevice with 8b/512b on-die ECC capability. These chips (and currently supported GD5FxGQ5 chips) have QIO DTR instruction for reading page cache. It isn't added in this patch because I don't have a DTR spi controller for testing. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220320100001.247905-6-gch981213@gmail.com --- drivers/mtd/nand/spi/gigadevice.c | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index fcd1c4e474a2..6b043e24855f 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -441,6 +441,66 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq5xexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GM7UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91), + 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, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GM7RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81), + 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, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GM7UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 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, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GM7RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 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, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GM8UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 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, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F4GM8RExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 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, + gd5fxgq4uexxg_ecc_get_status)), }; static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { -- cgit v1.2.3 From f7c48bccf1a0262a763f7e39f28c5c6798f2d6c3 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sat, 26 Mar 2022 17:33:04 +0100 Subject: mtd: rawnand: print offset instead of page number for bad blocks This makes printed info consistent with other kernel messages. After scanning NAND BBT create_bbt() prints offset of each bad block. This change makes is easy to verify nand_erase_nand() failure reason. Signed-off-by: Rafał Miłecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220326163304.30806-1-zajec5@gmail.com --- drivers/mtd/nand/raw/nand_base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 284fff62ac49..6b67b7dfe7ce 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4502,11 +4502,13 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, len = instr->len; while (len) { + loff_t ofs = (loff_t)page << chip->page_shift; + /* Check if we have a bad block, we do not erase bad blocks! */ if (nand_block_checkbad(chip, ((loff_t) page) << chip->page_shift, allowbbt)) { - pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", - __func__, page); + pr_warn("%s: attempt to erase a bad block at 0x%08llx\n", + __func__, (unsigned long long)ofs); ret = -EIO; goto erase_exit; } @@ -4524,8 +4526,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, if (ret) { pr_debug("%s: failed erase, page 0x%08x\n", __func__, page); - instr->fail_addr = - ((loff_t)page << chip->page_shift); + instr->fail_addr = ofs; goto erase_exit; } -- cgit v1.2.3 From 51a4a71d974c25dc102216a52462ff6d53eced15 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sat, 26 Mar 2022 10:07:26 +0200 Subject: mtd: rawnand: omap_elm: Add compatible for AM64 ELM The AM64 SoC has the Error Locator Module. Add compatible id for it. Signed-off-by: Roger Quadros Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220326080726.30372-4-rogerq@kernel.org --- drivers/mtd/nand/raw/omap_elm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index 893e9979c4a2..4796a48e1012 100644 --- a/drivers/mtd/nand/raw/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -548,6 +548,7 @@ static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); #ifdef CONFIG_OF static const struct of_device_id elm_of_match[] = { { .compatible = "ti,am3352-elm" }, + { .compatible = "ti,am64-elm" }, {}, }; MODULE_DEVICE_TABLE(of, elm_of_match); -- cgit v1.2.3 From 3a745b51cddafade99aaea1b93aad31e9614e230 Mon Sep 17 00:00:00 2001 From: Zheyu Ma Date: Mon, 11 Apr 2022 20:58:08 +0800 Subject: mtd: rawnand: denali: Use managed device resources All of the resources used by this driver has managed interfaces, so use them. Otherwise we will get the following splat: [ 4.472703] denali-nand-pci 0000:00:05.0: timeout while waiting for irq 0x1000 [ 4.474071] denali-nand-pci: probe of 0000:00:05.0 failed with error -5 [ 4.473538] nand: No NAND device found [ 4.474068] BUG: unable to handle page fault for address: ffffc90005000410 [ 4.475169] #PF: supervisor write access in kernel mode [ 4.475579] #PF: error_code(0x0002) - not-present page [ 4.478362] RIP: 0010:iowrite32+0x9/0x50 [ 4.486068] Call Trace: [ 4.486269] [ 4.486443] denali_isr+0x15b/0x300 [denali] [ 4.486788] ? denali_direct_write+0x50/0x50 [denali] [ 4.487189] __handle_irq_event_percpu+0x161/0x3b0 [ 4.487571] handle_irq_event+0x7d/0x1b0 [ 4.487884] handle_fasteoi_irq+0x2b0/0x770 [ 4.488219] __common_interrupt+0xc8/0x1b0 [ 4.488549] common_interrupt+0x9a/0xc0 Fixes: 93db446a424c ("mtd: nand: move raw NAND related code to the raw/ subdir") Signed-off-by: Zheyu Ma Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220411125808.958276-1-zheyuma97@gmail.com --- drivers/mtd/nand/raw/denali_pci.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 20c085a30adc..de7e722d3826 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -74,22 +74,21 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) return ret; } - denali->reg = ioremap(csr_base, csr_len); + denali->reg = devm_ioremap(denali->dev, csr_base, csr_len); if (!denali->reg) { dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); return -ENOMEM; } - denali->host = ioremap(mem_base, mem_len); + denali->host = devm_ioremap(denali->dev, mem_base, mem_len); if (!denali->host) { dev_err(&dev->dev, "Spectra: ioremap failed!"); - ret = -ENOMEM; - goto out_unmap_reg; + return -ENOMEM; } ret = denali_init(denali); if (ret) - goto out_unmap_host; + return ret; nsels = denali->nbanks; @@ -117,10 +116,6 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) out_remove_denali: denali_remove(denali); -out_unmap_host: - iounmap(denali->host); -out_unmap_reg: - iounmap(denali->reg); return ret; } @@ -129,8 +124,6 @@ static void denali_pci_remove(struct pci_dev *dev) struct denali_controller *denali = pci_get_drvdata(dev); denali_remove(denali); - iounmap(denali->reg); - iounmap(denali->host); } static struct pci_driver denali_pci_driver = { -- cgit v1.2.3 From 15616c7cfb968d72e614b1ba669467dc373b2582 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 11 Apr 2022 21:52:42 -0500 Subject: mtd: rawnand: gpmi: Refactor bch geometry settings function The code change refactor the bch geometry setting function, which doesn't change the default behavior, while user may choose to use chips required minimum ecc strength by DT flag "fsl,use-minimum-ecc". The default way to set bch geometry need to set the data chunk size(step_size) larger than oob size to make sure BBM locates in data chunk, then set the maximum ecc strength oob can hold. It always use unbalanced ECC layout, which ecc0 will cover both meta and data0 chunk. But the default bch setting is deprecated for large oobsize NAND (oobsize >1KB), so in the patch set, there is a split commit that introduces a new way to set bch geometry for large oob size NAND. For all other cases,set the bch geometry by chip required strength and step size, which uses the minimum ecc strength chip required. It can be explicitly enabled by DT flag "fsl,use-minimum-ecc", but need to be en/disabled in both u-boot and kernel at the same time. Signed-off-by: Han Xu Tested-by: Sean Nyekjaer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220412025246.24269-2-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 32 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 44b14c9dc9a7..66ebd569858d 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -514,24 +514,32 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) static int common_nfc_set_geometry(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(&this->nand); const struct nand_ecc_props *requirements = nanddev_get_ecc_requirements(&chip->base); + bool use_minimun_ecc; + int err; - if (chip->ecc.strength > 0 && chip->ecc.size > 0) - return set_geometry_by_ecc_info(this, chip->ecc.strength, - chip->ecc.size); - - if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) { - if (!(requirements->strength > 0 && requirements->step_size > 0)) - return -EINVAL; + use_minimun_ecc = of_property_read_bool(this->dev->of_node, + "fsl,use-minimum-ecc"); - return set_geometry_by_ecc_info(this, - requirements->strength, - requirements->step_size); + /* use legacy bch geometry settings by default*/ + if ((!use_minimun_ecc && mtd->oobsize < 1024) || + !(requirements->strength > 0 && requirements->step_size > 0)) { + dev_dbg(this->dev, "use legacy bch geometry\n"); + err = legacy_set_geometry(this); + if (!err) + return 0; } - return 0; + /* otherwise use the minimum ecc nand chip required */ + dev_dbg(this->dev, "use minimum ecc bch geometry\n"); + err = set_geometry_by_ecc_info(this, requirements->strength, + requirements->step_size); + if (err) + dev_err(this->dev, "none of the bch geometry setting works\n"); + + return err; } /* Configures the geometry for BCH. */ -- cgit v1.2.3 From d10af38a2e3b60dc46ca9727dfc24d0ea57136e3 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 11 Apr 2022 21:52:43 -0500 Subject: mtd: rawnand: gpmi: Add strict ecc strength check Add nand_ecc_is_strong_enough() check in gpmi_check_ecc() function to make sure ecc strength can meet chip requirement. Signed-off-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220412025246.24269-3-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 66ebd569858d..cee39126771e 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -240,7 +240,13 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) { + struct nand_chip *chip = &this->nand; struct bch_geometry *geo = &this->bch_geometry; + struct nand_device *nand = &chip->base; + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + + conf->step_size = geo->eccn_chunk_size; + conf->strength = geo->ecc_strength; /* Do the sanity check. */ if (GPMI_IS_MXS(this)) { @@ -248,7 +254,14 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) if (geo->gf_len == 14) return false; } - return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; + + if (geo->ecc_strength > this->devdata->bch_max_ecc_strength) + return false; + + if (!nand_ecc_is_strong_enough(nand)) + return false; + + return true; } /* -- cgit v1.2.3 From 109158579e64267439c4491845b770cf530f8cba Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 11 Apr 2022 21:52:44 -0500 Subject: mtd: rawnand: gpmi: Uninline the gpmi_check_ecc function The gpmi_check_ecc() is not small after adding more strict ecc check, uninline it. Signed-off-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220412025246.24269-4-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index cee39126771e..0f82edc97e9d 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -238,7 +238,7 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) geo->block_mark_bit_offset); } -static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) +static bool gpmi_check_ecc(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; struct bch_geometry *geo = &this->bch_geometry; -- cgit v1.2.3 From 2fb038eaeed82811eaecc4248b119218f8a3551b Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 11 Apr 2022 21:52:45 -0500 Subject: mtd: rawnand: gpmi: Rename the variable ecc_chunk_size There is only one variable ecc_chunk_size in bch_geometry data structure but two different field in BCH registers. The data0_size in BCH_FLASH0LAYOUT0 and datan_size in BCH_FLASH0LAYOUT1 should have dedicate variable since they might set to different values in some cases. For instance, if need dedicate ecc for meta area, the data0_size should be 0 rather than datan_size, but for all other cases, data0_size still equals to datan_size and it won't bring any function change. Signed-off-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220412025246.24269-5-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 52 +++++++++++++++++------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 9 +++--- 2 files changed, 34 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 0f82edc97e9d..1ac0dc8cae22 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -218,7 +218,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) "ECC Strength : %u\n" "Page Size in Bytes : %u\n" "Metadata Size in Bytes : %u\n" - "ECC Chunk Size in Bytes: %u\n" + "ECC0 Chunk Size in Bytes: %u\n" + "ECCn Chunk Size in Bytes: %u\n" "ECC Chunk Count : %u\n" "Payload Size in Bytes : %u\n" "Auxiliary Size in Bytes: %u\n" @@ -229,7 +230,8 @@ static void gpmi_dump_info(struct gpmi_nand_data *this) geo->ecc_strength, geo->page_size, geo->metadata_size, - geo->ecc_chunk_size, + geo->ecc0_chunk_size, + geo->eccn_chunk_size, geo->ecc_chunk_count, geo->payload_size, geo->auxiliary_size, @@ -293,13 +295,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, nanddev_get_ecc_requirements(&chip->base)->step_size); return -EINVAL; } - geo->ecc_chunk_size = ecc_step; + geo->ecc0_chunk_size = ecc_step; + geo->eccn_chunk_size = ecc_step; geo->ecc_strength = round_up(ecc_strength, 2); if (!gpmi_check_ecc(this)) return -EINVAL; /* Keep the C >= O */ - if (geo->ecc_chunk_size < mtd->oobsize) { + if (geo->eccn_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", ecc_step, mtd->oobsize); @@ -309,7 +312,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, /* The default value, see comment in the legacy_set_geometry(). */ geo->metadata_size = 10; - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; /* * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: @@ -431,13 +434,15 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) geo->gf_len = 13; /* The default for chunk size. */ - geo->ecc_chunk_size = 512; - while (geo->ecc_chunk_size < mtd->oobsize) { - geo->ecc_chunk_size *= 2; /* keep C >= O */ + geo->ecc0_chunk_size = 512; + geo->eccn_chunk_size = 512; + while (geo->eccn_chunk_size < mtd->oobsize) { + geo->ecc0_chunk_size *= 2; /* keep C >= O */ + geo->eccn_chunk_size *= 2; /* keep C >= O */ geo->gf_len = 14; } - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; /* We use the same ECC strength for all chunks. */ geo->ecc_strength = get_ecc_strength(this); @@ -864,7 +869,7 @@ static int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len) * we are passed in exec_op. Calculate the data length from it. */ if (this->bch) - return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size); + return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size); else return raw_len; } @@ -1256,7 +1261,7 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, /* Read ECC bytes into our internal raw_buffer */ offset = nfc_geo->metadata_size * 8; - offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); + offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1); offset -= eccbits; bitoffset = offset % 8; eccbytes = DIV_ROUND_UP(offset + eccbits, 8); @@ -1293,16 +1298,16 @@ static int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, if (i == 0) { /* The first block includes metadata */ flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, + buf + i * nfc_geo->eccn_chunk_size, + nfc_geo->eccn_chunk_size, eccbuf, eccbytes, this->auxiliary_virt, nfc_geo->metadata_size, nfc_geo->ecc_strength); } else { flips = nand_check_erased_ecc_chunk( - buf + i * nfc_geo->ecc_chunk_size, - nfc_geo->ecc_chunk_size, + buf + i * nfc_geo->eccn_chunk_size, + nfc_geo->eccn_chunk_size, eccbuf, eccbytes, NULL, 0, nfc_geo->ecc_strength); @@ -1331,20 +1336,21 @@ static void gpmi_bch_layout_std(struct gpmi_nand_data *this) struct bch_geometry *geo = &this->bch_geometry; unsigned int ecc_strength = geo->ecc_strength >> 1; unsigned int gf_len = geo->gf_len; - unsigned int block_size = geo->ecc_chunk_size; + unsigned int block0_size = geo->ecc0_chunk_size; + unsigned int blockn_size = geo->eccn_chunk_size; this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) | BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | - BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this); this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | - BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this); + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this); } static int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, @@ -1444,12 +1450,12 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | - BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->eccn_chunk_size, this); this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) | - BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this); + BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this); this->bch = true; @@ -1618,7 +1624,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; + int eccsize = nfc_geo->eccn_chunk_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; size_t src_bit_off; @@ -1703,7 +1709,7 @@ static int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct gpmi_nand_data *this = nand_get_controller_data(chip); struct bch_geometry *nfc_geo = &this->bch_geometry; - int eccsize = nfc_geo->ecc_chunk_size; + int eccsize = nfc_geo->eccn_chunk_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; uint8_t *oob = chip->oob_poi; @@ -2077,7 +2083,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->read_oob_raw = gpmi_ecc_read_oob_raw; ecc->write_oob_raw = gpmi_ecc_write_oob_raw; ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - ecc->size = bch_geo->ecc_chunk_size; + ecc->size = bch_geo->eccn_chunk_size; ecc->strength = bch_geo->ecc_strength; mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 5e1c3ddae5f8..5b217feb0ec1 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -30,9 +30,9 @@ struct resources { * @page_size: The size, in bytes, of a physical page, including * both data and OOB. * @metadata_size: The size, in bytes, of the metadata. - * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note - * the first chunk in the page includes both data and - * metadata, so it's a bit larger than this value. + * @ecc0_chunk_size: The size, in bytes, of a first ECC chunk. + * @eccn_chunk_size: The size, in bytes, of a single ECC chunk after + * the first chunk in the page. * @ecc_chunk_count: The number of ECC chunks in the page, * @payload_size: The size, in bytes, of the payload buffer. * @auxiliary_size: The size, in bytes, of the auxiliary buffer. @@ -48,7 +48,8 @@ struct bch_geometry { unsigned int ecc_strength; unsigned int page_size; unsigned int metadata_size; - unsigned int ecc_chunk_size; + unsigned int ecc0_chunk_size; + unsigned int eccn_chunk_size; unsigned int ecc_chunk_count; unsigned int payload_size; unsigned int auxiliary_size; -- cgit v1.2.3 From d9edc4bc67c4fd77b30d0a55bc84b775c2f00bab Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 11 Apr 2022 21:52:46 -0500 Subject: mtd: rawnand: gpmi: Add large oob bch setting support The code change proposes a new way to set bch geometry for large oob NAND (oobsize > 1KB). In this case, previous implementation can NOT guarantee the bad block mark always locates in data chunk, so we need a new way to do it. The general idea is, 1.Try all ECC strength from the maximum ecc that controller can support to minimum value required by NAND chip, any ECC strength makes the BBM locate in data chunk can be eligible. 2.If none of them works, using separate ECC for meta, which will add one extra ecc with the same ECC strength as other data chunks. This extra ECC can guarantee BBM located in data chunk, also we need to check if oob can afford it. Signed-off-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220412025246.24269-6-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 199 ++++++++++++++++++++++++++++- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 3 + 2 files changed, 197 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 1ac0dc8cae22..0b68d05846e1 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -266,6 +266,39 @@ static bool gpmi_check_ecc(struct gpmi_nand_data *this) return true; } +/* check if bbm locates in data chunk rather than ecc chunk */ +static bool bbm_in_data_chunk(struct gpmi_nand_data *this, + unsigned int *chunk_num) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i, j; + + if (geo->ecc0_chunk_size != geo->eccn_chunk_size) { + dev_err(this->dev, + "The size of ecc0_chunk must equal to eccn_chunk\n"); + return false; + } + + i = (mtd->writesize * 8 - geo->metadata_size * 8) / + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8); + + j = (mtd->writesize * 8 - geo->metadata_size * 8) - + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8) * i; + + if (j < geo->eccn_chunk_size * 8) { + *chunk_num = i+1; + dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n", + geo->ecc_strength, *chunk_num); + return true; + } + + return false; +} + /* * If we can get the ECC information from the nand chip, we do not * need to calculate them ourselves. @@ -415,6 +448,134 @@ static inline int get_ecc_strength(struct gpmi_nand_data *this) return round_down(ecc_strength, 2); } +static int set_geometry_for_large_oob(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); + unsigned int block_mark_bit_offset; + unsigned int max_ecc; + unsigned int bbm_chunk; + unsigned int i; + + /* sanity check for the minimum ecc nand required */ + if (!(requirements->strength > 0 && + requirements->step_size > 0)) + return -EINVAL; + geo->ecc_strength = requirements->strength; + + /* check if platform can support this nand */ + if (!gpmi_check_ecc(this)) { + dev_err(this->dev, + "unsupported NAND chip, minimum ecc required %d\n", + geo->ecc_strength); + return -EINVAL; + } + + /* calculate the maximum ecc platform can support*/ + geo->metadata_size = 10; + geo->gf_len = 14; + geo->ecc0_chunk_size = 1024; + geo->eccn_chunk_size = 1024; + geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; + max_ecc = min(get_ecc_strength(this), + this->devdata->bch_max_ecc_strength); + + /* + * search a supported ecc strength that makes bbm + * located in data chunk + */ + geo->ecc_strength = max_ecc; + while (!(geo->ecc_strength < requirements->strength)) { + if (bbm_in_data_chunk(this, &bbm_chunk)) + goto geo_setting; + geo->ecc_strength -= 2; + } + + /* if none of them works, keep using the minimum ecc */ + /* nand required but changing ecc page layout */ + geo->ecc_strength = requirements->strength; + /* add extra ecc for meta data */ + geo->ecc0_chunk_size = 0; + geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1; + geo->ecc_for_meta = 1; + /* check if oob can afford this extra ecc chunk */ + if (mtd->oobsize * 8 < geo->metadata_size * 8 + + geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) { + dev_err(this->dev, "unsupported NAND chip with new layout\n"); + return -EINVAL; + } + + /* calculate in which chunk bbm located */ + bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 - + geo->gf_len * geo->ecc_strength) / + (geo->gf_len * geo->ecc_strength + + geo->eccn_chunk_size * 8) + 1; + +geo_setting: + + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + geo->payload_size = mtd->writesize; + + /* + * The auxiliary buffer contains the metadata and the ECC status. The + * metadata is padded to the nearest 32-bit boundary. The ECC status + * contains one byte for every ECC chunk, and is also padded to the + * nearest 32-bit boundary. + */ + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return 0; + + /* calculate the number of ecc chunk behind the bbm */ + i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1; + + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + + dev_dbg(this->dev, "BCH Geometry :\n" + "GF length : %u\n" + "ECC Strength : %u\n" + "Page Size in Bytes : %u\n" + "Metadata Size in Bytes : %u\n" + "ECC0 Chunk Size in Bytes: %u\n" + "ECCn Chunk Size in Bytes: %u\n" + "ECC Chunk Count : %u\n" + "Payload Size in Bytes : %u\n" + "Auxiliary Size in Bytes: %u\n" + "Auxiliary Status Offset: %u\n" + "Block Mark Byte Offset : %u\n" + "Block Mark Bit Offset : %u\n" + "Block Mark in chunk : %u\n" + "Ecc for Meta data : %u\n", + geo->gf_len, + geo->ecc_strength, + geo->page_size, + geo->metadata_size, + geo->ecc0_chunk_size, + geo->eccn_chunk_size, + geo->ecc_chunk_count, + geo->payload_size, + geo->auxiliary_size, + geo->auxiliary_status_offset, + geo->block_mark_byte_offset, + geo->block_mark_bit_offset, + bbm_chunk, + geo->ecc_for_meta); + + return 0; +} + static int legacy_set_geometry(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; @@ -550,6 +711,14 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this) return 0; } + /* for large oob nand */ + if (mtd->oobsize > 1024) { + dev_dbg(this->dev, "use large oob bch geometry\n"); + err = set_geometry_for_large_oob(this); + if (!err) + return 0; + } + /* otherwise use the minimum ecc nand chip required */ dev_dbg(this->dev, "use minimum ecc bch geometry\n"); err = set_geometry_by_ecc_info(this, requirements->strength, @@ -1433,24 +1602,44 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, } } + /* + * if there is an ECC dedicate for meta: + * - need to add an extra ECC size when calculating col and page_size, + * if the meta size is NOT zero. + * - ecc0_chunk size need to set to the same size as other chunks, + * if the meta size is zero. + */ + meta = geo->metadata_size; if (first) { - col = meta + (size + ecc_parity_size) * first; + if (geo->ecc_for_meta) + col = meta + ecc_parity_size + + (size + ecc_parity_size) * first; + else + col = meta + (size + ecc_parity_size) * first; + meta = 0; buf = buf + first * size; } ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; - n = last - first + 1; - page_size = meta + (size + ecc_parity_size) * n; + + if (geo->ecc_for_meta && meta) + page_size = meta + ecc_parity_size + + (size + ecc_parity_size) * n; + else + page_size = meta + (size + ecc_parity_size) * n; + ecc_strength = geo->ecc_strength >> 1; - this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) | + this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS( + (geo->ecc_for_meta ? n : n - 1)) | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | - BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->eccn_chunk_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ? + 0 : geo->ecc0_chunk_size), this); this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 5b217feb0ec1..c3ff56ac62a7 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -42,6 +42,8 @@ struct resources { * which the underlying physical block mark appears. * @block_mark_bit_offset: The bit offset into the ECC-based page view at * which the underlying physical block mark appears. + * @ecc_for_meta: The flag to indicate if there is a dedicate ecc + * for meta. */ struct bch_geometry { unsigned int gf_len; @@ -56,6 +58,7 @@ struct bch_geometry { unsigned int auxiliary_status_offset; unsigned int block_mark_byte_offset; unsigned int block_mark_bit_offset; + unsigned int ecc_for_meta; /* ECC for meta data */ }; /** -- cgit v1.2.3 From f4c5c7f9d2e5ab005d57826b740b694b042a737c Mon Sep 17 00:00:00 2001 From: Felix Matouschek Date: Mon, 18 Apr 2022 15:28:03 +0200 Subject: mtd: spinand: Add support for XTX XT26G0xA Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and XTX26G04AXXXXX SPI NAND. These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC (8bit strength per 512bytes). Tested on Teltonika RUTX10 flashed with OpenWrt. Links: - http://www.xtxtech.com/download/?AId=225 - https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf Signed-off-by: Felix Matouschek Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220418132803.664103-1-felix@matouschek.org --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/core.c | 1 + drivers/mtd/nand/spi/xtx.c | 129 ++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 1 + 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/xtx.c (limited to 'drivers') diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 9662b9c1d5a9..80dabe6ff0f3 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ff8336870bc0..d5b685d1605e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { ¶gon_spinand_manufacturer, &toshiba_spinand_manufacturer, &winbond_spinand_manufacturer, + &xtx_spinand_manufacturer, }; static int spinand_manufacturer_match(struct spinand_device *spinand, diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c new file mode 100644 index 000000000000..3911520f718c --- /dev/null +++ b/drivers/mtd/nand/spi/xtx.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: + * Felix Matouschek + */ + +#include +#include +#include + +#define SPINAND_MFR_XTX 0x0B + +#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2) +#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2) +#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) +#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 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_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 48; + region->length = 16; + + return 0; +} + +static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + region->offset = 1; + region->length = 47; + + return 0; +} + +static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { + .ecc = xt26g0xa_ooblayout_ecc, + .free = xt26g0xa_ooblayout_free, +}; + +static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + status = status & XT26G0XA_STATUS_ECC_MASK; + + switch (status) { + case XT26G0XA_STATUS_ECC_NO_DETECTED: + return 0; + case XT26G0XA_STATUS_ECC_8_CORRECTED: + return 8; + case XT26G0XA_STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + default: + break; + } + + /* At this point values greater than (2 << 4) are invalid */ + if (status > XT26G0XA_STATUS_ECC_UNCOR_ERROR) + return -EINVAL; + + /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ + return status >> 2; +} + +static const struct spinand_info xtx_spinand_table[] = { + SPINAND_INFO("XT26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1), + NAND_MEMORG(1, 2048, 64, 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(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), + SPINAND_INFO("XT26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2), + NAND_MEMORG(1, 2048, 64, 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(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), + SPINAND_INFO("XT26G04A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3), + NAND_MEMORG(1, 2048, 64, 128, 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(&xt26g0xa_ooblayout, + xt26g0xa_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer xtx_spinand_manufacturer = { + .id = SPINAND_MFR_XTX, + .name = "XTX", + .chips = xtx_spinand_table, + .nchips = ARRAY_SIZE(xtx_spinand_table), + .ops = &xtx_spinand_manuf_ops, +}; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 3aa28240a77f..5584d3bb6556 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -266,6 +266,7 @@ extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer paragon_spinand_manufacturer; extern const struct spinand_manufacturer toshiba_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; +extern const struct spinand_manufacturer xtx_spinand_manufacturer; /** * struct spinand_op_variants - SPI NAND operation variants -- cgit v1.2.3 From 3380557fc7e28d9bce7607e16d98f123d36da4ca Mon Sep 17 00:00:00 2001 From: Andreas Böhler Date: Wed, 20 Apr 2022 12:40:34 +0200 Subject: mtd: rawnand: add support for Toshiba TC58NVG0S3HTA00 NAND flash The Toshiba TC58NVG0S3HTA00 is detected with 64 byte OOB while the flash has 128 bytes OOB. This adds a static NAND ID entry to correct this. Tested on FRITZ!Box 7530 flashed with OpenWrt. Signed-off-by: Andreas Böhler Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220420104034.6333-1-dev@aboehler.at --- drivers/mtd/nand/raw/nand_ids.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index 6e41902be35f..d64adbd1ce6b 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"TC58NVG0S3E 1G 3.3V 8-bit", { .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} }, SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), }, + {"TC58NVG0S3HTA00 1G 3.3V 8-bit", + { .id = {0x98, 0xf1, 0x80, 0x15} }, + SZ_2K, SZ_128, SZ_128K, 0, 4, 128, NAND_ECC_INFO(8, SZ_512), }, {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, -- cgit v1.2.3 From a28ed09dafee20da51eb26452950839633afd824 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 26 Apr 2022 16:49:11 +0800 Subject: mtd: rawnand: cadence: fix possible null-ptr-deref in cadence_nand_dt_probe() It will cause null-ptr-deref when using 'res', if platform_get_resource() returns NULL, so move using 'res' after devm_ioremap_resource() that will check it to avoid null-ptr-deref. And use devm_platform_get_and_ioremap_resource() to simplify code. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Signed-off-by: Yang Yingliang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220426084913.4021868-1-yangyingliang@huawei.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 7eec60ea9056..0d72672f8b64 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2983,11 +2983,10 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev) if (IS_ERR(cdns_ctrl->reg)) return PTR_ERR(cdns_ctrl->reg); - res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); - cdns_ctrl->io.dma = res->start; - cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res); + cdns_ctrl->io.virt = devm_platform_get_and_ioremap_resource(ofdev, 1, &res); if (IS_ERR(cdns_ctrl->io.virt)) return PTR_ERR(cdns_ctrl->io.virt); + cdns_ctrl->io.dma = res->start; dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk"); if (IS_ERR(dt->clk)) -- cgit v1.2.3 From ddf66aefd685fd46500b9917333e1b1e118276dc Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 26 Apr 2022 16:49:12 +0800 Subject: mtd: rawnand: intel: fix possible null-ptr-deref in ebu_nand_probe() It will cause null-ptr-deref when using 'res', if platform_get_resource() returns NULL, so move using 'res' after devm_ioremap_resource() that will check it to avoid null-ptr-deref. Fixes: 0b1039f016e8 ("mtd: rawnand: Add NAND controller support on Intel LGM SoC") Signed-off-by: Yang Yingliang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220426084913.4021868-2-yangyingliang@huawei.com --- drivers/mtd/nand/raw/intel-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c index 7c1c80dae826..e91b879b32bd 100644 --- a/drivers/mtd/nand/raw/intel-nand-controller.c +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -619,9 +619,9 @@ static int ebu_nand_probe(struct platform_device *pdev) resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname); ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res); - ebu_host->cs[cs].nand_pa = res->start; if (IS_ERR(ebu_host->cs[cs].chipaddr)) return PTR_ERR(ebu_host->cs[cs].chipaddr); + ebu_host->cs[cs].nand_pa = res->start; ebu_host->clk = devm_clk_get(dev, NULL); if (IS_ERR(ebu_host->clk)) -- cgit v1.2.3 From 9b2152f96f4a243bdfb89027c0d920aa0af810e7 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 26 Apr 2022 16:49:13 +0800 Subject: mtd: rawnand: tmio: check return value after calling platform_get_resource() It will cause null-ptr-deref if platform_get_resource() returns NULL, we need check the return value. Signed-off-by: Yang Yingliang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220426084913.4021868-3-yangyingliang@huawei.com --- drivers/mtd/nand/raw/tmio_nand.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index de8e919d0ebe..8f1a42bf199c 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -390,6 +390,9 @@ static int tmio_probe(struct platform_device *dev) if (data == NULL) dev_warn(&dev->dev, "NULL platform data!\n"); + if (!ccr || !fcr) + return -EINVAL; + tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); if (!tmio) return -ENOMEM; -- cgit v1.2.3 From 4fd62f15afa0d0da4823f429a2fb4c3492a84edf Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 24 Apr 2022 11:25:23 +0800 Subject: mtd: nand: make mtk_ecc.c a separated module this code will be used in mediatek snfi spi-mem controller with pipelined ECC engine. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220424032527.673605-2-gch981213@gmail.com --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/ecc-mtk.c | 592 ++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/Kconfig | 1 + drivers/mtd/nand/raw/Makefile | 2 +- drivers/mtd/nand/raw/mtk_ecc.c | 593 --------------------------------------- drivers/mtd/nand/raw/mtk_ecc.h | 47 ---- drivers/mtd/nand/raw/mtk_nand.c | 2 +- include/linux/mtd/nand-ecc-mtk.h | 47 ++++ 9 files changed, 650 insertions(+), 642 deletions(-) create mode 100644 drivers/mtd/nand/ecc-mtk.c delete mode 100644 drivers/mtd/nand/raw/mtk_ecc.c delete mode 100644 drivers/mtd/nand/raw/mtk_ecc.h create mode 100644 include/linux/mtd/nand-ecc-mtk.h (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 9b249826ef93..2f3e02ab72ed 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -53,6 +53,13 @@ config MTD_NAND_ECC_MXIC help This enables support for the hardware ECC engine from Macronix. +config MTD_NAND_ECC_MEDIATEK + tristate "Mediatek hardware ECC engine" + depends on HAS_IOMEM + select MTD_NAND_ECC + help + This enables support for the hardware ECC engine from Mediatek. + endmenu endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a4e6b7ae0614..19e1291ac4d5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,6 +2,7 @@ nandcore-objs := core.o bbt.o obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o +obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o obj-y += onenand/ obj-y += raw/ diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c new file mode 100644 index 000000000000..491c2d66f815 --- /dev/null +++ b/drivers/mtd/nand/ecc-mtk.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * MTK ECC controller driver. + * Copyright (C) 2016 MediaTek Inc. + * Authors: Xiaolei Li + * Jorge Ramirez-Ortiz + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ECC_IDLE_MASK BIT(0) +#define ECC_IRQ_EN BIT(0) +#define ECC_PG_IRQ_SEL BIT(1) +#define ECC_OP_ENABLE (1) +#define ECC_OP_DISABLE (0) + +#define ECC_ENCCON (0x00) +#define ECC_ENCCNFG (0x04) +#define ECC_MS_SHIFT (16) +#define ECC_ENCDIADDR (0x08) +#define ECC_ENCIDLE (0x0C) +#define ECC_DECCON (0x100) +#define ECC_DECCNFG (0x104) +#define DEC_EMPTY_EN BIT(31) +#define DEC_CNFG_CORRECT (0x3 << 12) +#define ECC_DECIDLE (0x10C) +#define ECC_DECENUM0 (0x114) + +#define ECC_TIMEOUT (500000) + +#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) +#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) + +struct mtk_ecc_caps { + u32 err_mask; + const u8 *ecc_strength; + const u32 *ecc_regs; + u8 num_ecc_strength; + u8 ecc_mode_shift; + u32 parity_bits; + int pg_irq_sel; +}; + +struct mtk_ecc { + struct device *dev; + const struct mtk_ecc_caps *caps; + void __iomem *regs; + struct clk *clk; + + struct completion done; + struct mutex lock; + u32 sectors; + + u8 *eccdata; +}; + +/* ecc strength that each IP supports */ +static const u8 ecc_strength_mt2701[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60 +}; + +static const u8 ecc_strength_mt2712[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60, 68, 72, 80 +}; + +static const u8 ecc_strength_mt7622[] = { + 4, 6, 8, 10, 12, 14, 16 +}; + +enum mtk_ecc_regs { + ECC_ENCPAR00, + ECC_ENCIRQ_EN, + ECC_ENCIRQ_STA, + ECC_DECDONE, + ECC_DECIRQ_EN, + ECC_DECIRQ_STA, +}; + +static int mt2701_ecc_regs[] = { + [ECC_ENCPAR00] = 0x10, + [ECC_ENCIRQ_EN] = 0x80, + [ECC_ENCIRQ_STA] = 0x84, + [ECC_DECDONE] = 0x124, + [ECC_DECIRQ_EN] = 0x200, + [ECC_DECIRQ_STA] = 0x204, +}; + +static int mt2712_ecc_regs[] = { + [ECC_ENCPAR00] = 0x300, + [ECC_ENCIRQ_EN] = 0x80, + [ECC_ENCIRQ_STA] = 0x84, + [ECC_DECDONE] = 0x124, + [ECC_DECIRQ_EN] = 0x200, + [ECC_DECIRQ_STA] = 0x204, +}; + +static int mt7622_ecc_regs[] = { + [ECC_ENCPAR00] = 0x10, + [ECC_ENCIRQ_EN] = 0x30, + [ECC_ENCIRQ_STA] = 0x34, + [ECC_DECDONE] = 0x11c, + [ECC_DECIRQ_EN] = 0x140, + [ECC_DECIRQ_STA] = 0x144, +}; + +static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, + enum mtk_ecc_operation op) +{ + struct device *dev = ecc->dev; + u32 val; + int ret; + + ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val, + val & ECC_IDLE_MASK, + 10, ECC_TIMEOUT); + if (ret) + dev_warn(dev, "%s NOT idle\n", + op == ECC_ENCODE ? "encoder" : "decoder"); +} + +static irqreturn_t mtk_ecc_irq(int irq, void *id) +{ + struct mtk_ecc *ecc = id; + u32 dec, enc; + + dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]) + & ECC_IRQ_EN; + if (dec) { + dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); + if (dec & ecc->sectors) { + /* + * Clear decode IRQ status once again to ensure that + * there will be no extra IRQ. + */ + readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]); + ecc->sectors = 0; + complete(&ecc->done); + } else { + return IRQ_HANDLED; + } + } else { + enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA]) + & ECC_IRQ_EN; + if (enc) + complete(&ecc->done); + else + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config) +{ + u32 ecc_bit, dec_sz, enc_sz; + u32 reg, i; + + for (i = 0; i < ecc->caps->num_ecc_strength; i++) { + if (ecc->caps->ecc_strength[i] == config->strength) + break; + } + + if (i == ecc->caps->num_ecc_strength) { + dev_err(ecc->dev, "invalid ecc strength %d\n", + config->strength); + return -EINVAL; + } + + ecc_bit = i; + + if (config->op == ECC_ENCODE) { + /* configure ECC encoder (in bits) */ + enc_sz = config->len << 3; + + reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); + reg |= (enc_sz << ECC_MS_SHIFT); + writel(reg, ecc->regs + ECC_ENCCNFG); + + if (config->mode != ECC_NFI_MODE) + writel(lower_32_bits(config->addr), + ecc->regs + ECC_ENCDIADDR); + + } else { + /* configure ECC decoder (in bits) */ + dec_sz = (config->len << 3) + + config->strength * ecc->caps->parity_bits; + + reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); + reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT; + reg |= DEC_EMPTY_EN; + writel(reg, ecc->regs + ECC_DECCNFG); + + if (config->sectors) + ecc->sectors = 1 << (config->sectors - 1); + } + + return 0; +} + +void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, + int sectors) +{ + u32 offset, i, err; + u32 bitflips = 0; + + stats->corrected = 0; + stats->failed = 0; + + for (i = 0; i < sectors; i++) { + offset = (i >> 2) << 2; + err = readl(ecc->regs + ECC_DECENUM0 + offset); + err = err >> ((i % 4) * 8); + err &= ecc->caps->err_mask; + if (err == ecc->caps->err_mask) { + /* uncorrectable errors */ + stats->failed++; + continue; + } + + stats->corrected += err; + bitflips = max_t(u32, bitflips, err); + } + + stats->bitflips = bitflips; +} +EXPORT_SYMBOL(mtk_ecc_get_stats); + +void mtk_ecc_release(struct mtk_ecc *ecc) +{ + clk_disable_unprepare(ecc->clk); + put_device(ecc->dev); +} +EXPORT_SYMBOL(mtk_ecc_release); + +static void mtk_ecc_hw_init(struct mtk_ecc *ecc) +{ + mtk_ecc_wait_idle(ecc, ECC_ENCODE); + writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON); + + mtk_ecc_wait_idle(ecc, ECC_DECODE); + writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON); +} + +static struct mtk_ecc *mtk_ecc_get(struct device_node *np) +{ + struct platform_device *pdev; + struct mtk_ecc *ecc; + + pdev = of_find_device_by_node(np); + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + ecc = platform_get_drvdata(pdev); + if (!ecc) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + clk_prepare_enable(ecc->clk); + mtk_ecc_hw_init(ecc); + + return ecc; +} + +struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) +{ + struct mtk_ecc *ecc = NULL; + struct device_node *np; + + np = of_parse_phandle(of_node, "ecc-engine", 0); + if (np) { + ecc = mtk_ecc_get(np); + of_node_put(np); + } + + return ecc; +} +EXPORT_SYMBOL(of_mtk_ecc_get); + +int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) +{ + enum mtk_ecc_operation op = config->op; + u16 reg_val; + int ret; + + ret = mutex_lock_interruptible(&ecc->lock); + if (ret) { + dev_err(ecc->dev, "interrupted when attempting to lock\n"); + return ret; + } + + mtk_ecc_wait_idle(ecc, op); + + ret = mtk_ecc_config(ecc, config); + if (ret) { + mutex_unlock(&ecc->lock); + return ret; + } + + if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) { + init_completion(&ecc->done); + reg_val = ECC_IRQ_EN; + /* + * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it + * means this chip can only generate one ecc irq during page + * read / write. If is 0, generate one ecc irq each ecc step. + */ + if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE) + reg_val |= ECC_PG_IRQ_SEL; + if (op == ECC_ENCODE) + writew(reg_val, ecc->regs + + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); + else + writew(reg_val, ecc->regs + + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); + } + + writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op)); + + return 0; +} +EXPORT_SYMBOL(mtk_ecc_enable); + +void mtk_ecc_disable(struct mtk_ecc *ecc) +{ + enum mtk_ecc_operation op = ECC_ENCODE; + + /* find out the running operation */ + if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE) + op = ECC_DECODE; + + /* disable it */ + mtk_ecc_wait_idle(ecc, op); + if (op == ECC_DECODE) { + /* + * Clear decode IRQ status in case there is a timeout to wait + * decode IRQ. + */ + readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); + writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); + } else { + writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); + } + + writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); + + mutex_unlock(&ecc->lock); +} +EXPORT_SYMBOL(mtk_ecc_disable); + +int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op) +{ + int ret; + + ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500)); + if (!ret) { + dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n", + (op == ECC_ENCODE) ? "encoder" : "decoder"); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL(mtk_ecc_wait_done); + +int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, + u8 *data, u32 bytes) +{ + dma_addr_t addr; + u32 len; + int ret; + + addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE); + ret = dma_mapping_error(ecc->dev, addr); + if (ret) { + dev_err(ecc->dev, "dma mapping error\n"); + return -EINVAL; + } + + config->op = ECC_ENCODE; + config->addr = addr; + ret = mtk_ecc_enable(ecc, config); + if (ret) { + dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); + return ret; + } + + ret = mtk_ecc_wait_done(ecc, ECC_ENCODE); + if (ret) + goto timeout; + + mtk_ecc_wait_idle(ecc, ECC_ENCODE); + + /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ + len = (config->strength * ecc->caps->parity_bits + 7) >> 3; + + /* write the parity bytes generated by the ECC back to temp buffer */ + __ioread32_copy(ecc->eccdata, + ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00], + round_up(len, 4)); + + /* copy into possibly unaligned OOB region with actual length */ + memcpy(data + bytes, ecc->eccdata, len); +timeout: + + dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); + mtk_ecc_disable(ecc); + + return ret; +} +EXPORT_SYMBOL(mtk_ecc_encode); + +void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p) +{ + const u8 *ecc_strength = ecc->caps->ecc_strength; + int i; + + for (i = 0; i < ecc->caps->num_ecc_strength; i++) { + if (*p <= ecc_strength[i]) { + if (!i) + *p = ecc_strength[i]; + else if (*p != ecc_strength[i]) + *p = ecc_strength[i - 1]; + return; + } + } + + *p = ecc_strength[ecc->caps->num_ecc_strength - 1]; +} +EXPORT_SYMBOL(mtk_ecc_adjust_strength); + +unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc) +{ + return ecc->caps->parity_bits; +} +EXPORT_SYMBOL(mtk_ecc_get_parity_bits); + +static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { + .err_mask = 0x3f, + .ecc_strength = ecc_strength_mt2701, + .ecc_regs = mt2701_ecc_regs, + .num_ecc_strength = 20, + .ecc_mode_shift = 5, + .parity_bits = 14, + .pg_irq_sel = 0, +}; + +static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { + .err_mask = 0x7f, + .ecc_strength = ecc_strength_mt2712, + .ecc_regs = mt2712_ecc_regs, + .num_ecc_strength = 23, + .ecc_mode_shift = 5, + .parity_bits = 14, + .pg_irq_sel = 1, +}; + +static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { + .err_mask = 0x3f, + .ecc_strength = ecc_strength_mt7622, + .ecc_regs = mt7622_ecc_regs, + .num_ecc_strength = 7, + .ecc_mode_shift = 4, + .parity_bits = 13, + .pg_irq_sel = 0, +}; + +static const struct of_device_id mtk_ecc_dt_match[] = { + { + .compatible = "mediatek,mt2701-ecc", + .data = &mtk_ecc_caps_mt2701, + }, { + .compatible = "mediatek,mt2712-ecc", + .data = &mtk_ecc_caps_mt2712, + }, { + .compatible = "mediatek,mt7622-ecc", + .data = &mtk_ecc_caps_mt7622, + }, + {}, +}; + +static int mtk_ecc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_ecc *ecc; + u32 max_eccdata_size; + int irq, ret; + + ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); + if (!ecc) + return -ENOMEM; + + ecc->caps = of_device_get_match_data(dev); + + max_eccdata_size = ecc->caps->num_ecc_strength - 1; + max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; + max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3; + max_eccdata_size = round_up(max_eccdata_size, 4); + ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL); + if (!ecc->eccdata) + return -ENOMEM; + + ecc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ecc->regs)) + return PTR_ERR(ecc->regs); + + ecc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ecc->clk)) { + dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk)); + return PTR_ERR(ecc->clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "failed to set DMA mask\n"); + return ret; + } + + ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc); + if (ret) { + dev_err(dev, "failed to request irq\n"); + return -EINVAL; + } + + ecc->dev = dev; + mutex_init(&ecc->lock); + platform_set_drvdata(pdev, ecc); + dev_info(dev, "probed\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_ecc_suspend(struct device *dev) +{ + struct mtk_ecc *ecc = dev_get_drvdata(dev); + + clk_disable_unprepare(ecc->clk); + + return 0; +} + +static int mtk_ecc_resume(struct device *dev) +{ + struct mtk_ecc *ecc = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ecc->clk); + if (ret) { + dev_err(dev, "failed to enable clk\n"); + return ret; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume); +#endif + +MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match); + +static struct platform_driver mtk_ecc_driver = { + .probe = mtk_ecc_probe, + .driver = { + .name = "mtk-ecc", + .of_match_table = mtk_ecc_dt_match, +#ifdef CONFIG_PM_SLEEP + .pm = &mtk_ecc_pm_ops, +#endif + }, +}; + +module_platform_driver(mtk_ecc_driver); + +MODULE_AUTHOR("Xiaolei Li "); +MODULE_DESCRIPTION("MTK Nand ECC Driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 9b078e78f3fa..8b6d7a515445 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -374,6 +374,7 @@ config MTD_NAND_QCOM config MTD_NAND_MTK tristate "MTK NAND controller" + depends on MTD_NAND_ECC_MEDIATEK depends on ARCH_MEDIATEK || COMPILE_TEST depends on HAS_IOMEM help diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 88a566513c56..fa1d00120310 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -48,7 +48,7 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o -obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o +obj-$(CONFIG_MTD_NAND_MTK) += mtk_nand.o obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c deleted file mode 100644 index e7df3dac705e..000000000000 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ /dev/null @@ -1,593 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 OR MIT -/* - * MTK ECC controller driver. - * Copyright (C) 2016 MediaTek Inc. - * Authors: Xiaolei Li - * Jorge Ramirez-Ortiz - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_ecc.h" - -#define ECC_IDLE_MASK BIT(0) -#define ECC_IRQ_EN BIT(0) -#define ECC_PG_IRQ_SEL BIT(1) -#define ECC_OP_ENABLE (1) -#define ECC_OP_DISABLE (0) - -#define ECC_ENCCON (0x00) -#define ECC_ENCCNFG (0x04) -#define ECC_MS_SHIFT (16) -#define ECC_ENCDIADDR (0x08) -#define ECC_ENCIDLE (0x0C) -#define ECC_DECCON (0x100) -#define ECC_DECCNFG (0x104) -#define DEC_EMPTY_EN BIT(31) -#define DEC_CNFG_CORRECT (0x3 << 12) -#define ECC_DECIDLE (0x10C) -#define ECC_DECENUM0 (0x114) - -#define ECC_TIMEOUT (500000) - -#define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) -#define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) - -struct mtk_ecc_caps { - u32 err_mask; - const u8 *ecc_strength; - const u32 *ecc_regs; - u8 num_ecc_strength; - u8 ecc_mode_shift; - u32 parity_bits; - int pg_irq_sel; -}; - -struct mtk_ecc { - struct device *dev; - const struct mtk_ecc_caps *caps; - void __iomem *regs; - struct clk *clk; - - struct completion done; - struct mutex lock; - u32 sectors; - - u8 *eccdata; -}; - -/* ecc strength that each IP supports */ -static const u8 ecc_strength_mt2701[] = { - 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, - 40, 44, 48, 52, 56, 60 -}; - -static const u8 ecc_strength_mt2712[] = { - 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, - 40, 44, 48, 52, 56, 60, 68, 72, 80 -}; - -static const u8 ecc_strength_mt7622[] = { - 4, 6, 8, 10, 12, 14, 16 -}; - -enum mtk_ecc_regs { - ECC_ENCPAR00, - ECC_ENCIRQ_EN, - ECC_ENCIRQ_STA, - ECC_DECDONE, - ECC_DECIRQ_EN, - ECC_DECIRQ_STA, -}; - -static int mt2701_ecc_regs[] = { - [ECC_ENCPAR00] = 0x10, - [ECC_ENCIRQ_EN] = 0x80, - [ECC_ENCIRQ_STA] = 0x84, - [ECC_DECDONE] = 0x124, - [ECC_DECIRQ_EN] = 0x200, - [ECC_DECIRQ_STA] = 0x204, -}; - -static int mt2712_ecc_regs[] = { - [ECC_ENCPAR00] = 0x300, - [ECC_ENCIRQ_EN] = 0x80, - [ECC_ENCIRQ_STA] = 0x84, - [ECC_DECDONE] = 0x124, - [ECC_DECIRQ_EN] = 0x200, - [ECC_DECIRQ_STA] = 0x204, -}; - -static int mt7622_ecc_regs[] = { - [ECC_ENCPAR00] = 0x10, - [ECC_ENCIRQ_EN] = 0x30, - [ECC_ENCIRQ_STA] = 0x34, - [ECC_DECDONE] = 0x11c, - [ECC_DECIRQ_EN] = 0x140, - [ECC_DECIRQ_STA] = 0x144, -}; - -static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc, - enum mtk_ecc_operation op) -{ - struct device *dev = ecc->dev; - u32 val; - int ret; - - ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val, - val & ECC_IDLE_MASK, - 10, ECC_TIMEOUT); - if (ret) - dev_warn(dev, "%s NOT idle\n", - op == ECC_ENCODE ? "encoder" : "decoder"); -} - -static irqreturn_t mtk_ecc_irq(int irq, void *id) -{ - struct mtk_ecc *ecc = id; - u32 dec, enc; - - dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]) - & ECC_IRQ_EN; - if (dec) { - dec = readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); - if (dec & ecc->sectors) { - /* - * Clear decode IRQ status once again to ensure that - * there will be no extra IRQ. - */ - readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_STA]); - ecc->sectors = 0; - complete(&ecc->done); - } else { - return IRQ_HANDLED; - } - } else { - enc = readl(ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_STA]) - & ECC_IRQ_EN; - if (enc) - complete(&ecc->done); - else - return IRQ_NONE; - } - - return IRQ_HANDLED; -} - -static int mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config) -{ - u32 ecc_bit, dec_sz, enc_sz; - u32 reg, i; - - for (i = 0; i < ecc->caps->num_ecc_strength; i++) { - if (ecc->caps->ecc_strength[i] == config->strength) - break; - } - - if (i == ecc->caps->num_ecc_strength) { - dev_err(ecc->dev, "invalid ecc strength %d\n", - config->strength); - return -EINVAL; - } - - ecc_bit = i; - - if (config->op == ECC_ENCODE) { - /* configure ECC encoder (in bits) */ - enc_sz = config->len << 3; - - reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); - reg |= (enc_sz << ECC_MS_SHIFT); - writel(reg, ecc->regs + ECC_ENCCNFG); - - if (config->mode != ECC_NFI_MODE) - writel(lower_32_bits(config->addr), - ecc->regs + ECC_ENCDIADDR); - - } else { - /* configure ECC decoder (in bits) */ - dec_sz = (config->len << 3) + - config->strength * ecc->caps->parity_bits; - - reg = ecc_bit | (config->mode << ecc->caps->ecc_mode_shift); - reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT; - reg |= DEC_EMPTY_EN; - writel(reg, ecc->regs + ECC_DECCNFG); - - if (config->sectors) - ecc->sectors = 1 << (config->sectors - 1); - } - - return 0; -} - -void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats, - int sectors) -{ - u32 offset, i, err; - u32 bitflips = 0; - - stats->corrected = 0; - stats->failed = 0; - - for (i = 0; i < sectors; i++) { - offset = (i >> 2) << 2; - err = readl(ecc->regs + ECC_DECENUM0 + offset); - err = err >> ((i % 4) * 8); - err &= ecc->caps->err_mask; - if (err == ecc->caps->err_mask) { - /* uncorrectable errors */ - stats->failed++; - continue; - } - - stats->corrected += err; - bitflips = max_t(u32, bitflips, err); - } - - stats->bitflips = bitflips; -} -EXPORT_SYMBOL(mtk_ecc_get_stats); - -void mtk_ecc_release(struct mtk_ecc *ecc) -{ - clk_disable_unprepare(ecc->clk); - put_device(ecc->dev); -} -EXPORT_SYMBOL(mtk_ecc_release); - -static void mtk_ecc_hw_init(struct mtk_ecc *ecc) -{ - mtk_ecc_wait_idle(ecc, ECC_ENCODE); - writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON); - - mtk_ecc_wait_idle(ecc, ECC_DECODE); - writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON); -} - -static struct mtk_ecc *mtk_ecc_get(struct device_node *np) -{ - struct platform_device *pdev; - struct mtk_ecc *ecc; - - pdev = of_find_device_by_node(np); - if (!pdev) - return ERR_PTR(-EPROBE_DEFER); - - ecc = platform_get_drvdata(pdev); - if (!ecc) { - put_device(&pdev->dev); - return ERR_PTR(-EPROBE_DEFER); - } - - clk_prepare_enable(ecc->clk); - mtk_ecc_hw_init(ecc); - - return ecc; -} - -struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) -{ - struct mtk_ecc *ecc = NULL; - struct device_node *np; - - np = of_parse_phandle(of_node, "ecc-engine", 0); - if (np) { - ecc = mtk_ecc_get(np); - of_node_put(np); - } - - return ecc; -} -EXPORT_SYMBOL(of_mtk_ecc_get); - -int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config) -{ - enum mtk_ecc_operation op = config->op; - u16 reg_val; - int ret; - - ret = mutex_lock_interruptible(&ecc->lock); - if (ret) { - dev_err(ecc->dev, "interrupted when attempting to lock\n"); - return ret; - } - - mtk_ecc_wait_idle(ecc, op); - - ret = mtk_ecc_config(ecc, config); - if (ret) { - mutex_unlock(&ecc->lock); - return ret; - } - - if (config->mode != ECC_NFI_MODE || op != ECC_ENCODE) { - init_completion(&ecc->done); - reg_val = ECC_IRQ_EN; - /* - * For ECC_NFI_MODE, if ecc->caps->pg_irq_sel is 1, then it - * means this chip can only generate one ecc irq during page - * read / write. If is 0, generate one ecc irq each ecc step. - */ - if (ecc->caps->pg_irq_sel && config->mode == ECC_NFI_MODE) - reg_val |= ECC_PG_IRQ_SEL; - if (op == ECC_ENCODE) - writew(reg_val, ecc->regs + - ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); - else - writew(reg_val, ecc->regs + - ecc->caps->ecc_regs[ECC_DECIRQ_EN]); - } - - writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op)); - - return 0; -} -EXPORT_SYMBOL(mtk_ecc_enable); - -void mtk_ecc_disable(struct mtk_ecc *ecc) -{ - enum mtk_ecc_operation op = ECC_ENCODE; - - /* find out the running operation */ - if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE) - op = ECC_DECODE; - - /* disable it */ - mtk_ecc_wait_idle(ecc, op); - if (op == ECC_DECODE) { - /* - * Clear decode IRQ status in case there is a timeout to wait - * decode IRQ. - */ - readw(ecc->regs + ecc->caps->ecc_regs[ECC_DECDONE]); - writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_DECIRQ_EN]); - } else { - writew(0, ecc->regs + ecc->caps->ecc_regs[ECC_ENCIRQ_EN]); - } - - writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op)); - - mutex_unlock(&ecc->lock); -} -EXPORT_SYMBOL(mtk_ecc_disable); - -int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op) -{ - int ret; - - ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500)); - if (!ret) { - dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n", - (op == ECC_ENCODE) ? "encoder" : "decoder"); - return -ETIMEDOUT; - } - - return 0; -} -EXPORT_SYMBOL(mtk_ecc_wait_done); - -int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config, - u8 *data, u32 bytes) -{ - dma_addr_t addr; - u32 len; - int ret; - - addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE); - ret = dma_mapping_error(ecc->dev, addr); - if (ret) { - dev_err(ecc->dev, "dma mapping error\n"); - return -EINVAL; - } - - config->op = ECC_ENCODE; - config->addr = addr; - ret = mtk_ecc_enable(ecc, config); - if (ret) { - dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); - return ret; - } - - ret = mtk_ecc_wait_done(ecc, ECC_ENCODE); - if (ret) - goto timeout; - - mtk_ecc_wait_idle(ecc, ECC_ENCODE); - - /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */ - len = (config->strength * ecc->caps->parity_bits + 7) >> 3; - - /* write the parity bytes generated by the ECC back to temp buffer */ - __ioread32_copy(ecc->eccdata, - ecc->regs + ecc->caps->ecc_regs[ECC_ENCPAR00], - round_up(len, 4)); - - /* copy into possibly unaligned OOB region with actual length */ - memcpy(data + bytes, ecc->eccdata, len); -timeout: - - dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE); - mtk_ecc_disable(ecc); - - return ret; -} -EXPORT_SYMBOL(mtk_ecc_encode); - -void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p) -{ - const u8 *ecc_strength = ecc->caps->ecc_strength; - int i; - - for (i = 0; i < ecc->caps->num_ecc_strength; i++) { - if (*p <= ecc_strength[i]) { - if (!i) - *p = ecc_strength[i]; - else if (*p != ecc_strength[i]) - *p = ecc_strength[i - 1]; - return; - } - } - - *p = ecc_strength[ecc->caps->num_ecc_strength - 1]; -} -EXPORT_SYMBOL(mtk_ecc_adjust_strength); - -unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc) -{ - return ecc->caps->parity_bits; -} -EXPORT_SYMBOL(mtk_ecc_get_parity_bits); - -static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { - .err_mask = 0x3f, - .ecc_strength = ecc_strength_mt2701, - .ecc_regs = mt2701_ecc_regs, - .num_ecc_strength = 20, - .ecc_mode_shift = 5, - .parity_bits = 14, - .pg_irq_sel = 0, -}; - -static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { - .err_mask = 0x7f, - .ecc_strength = ecc_strength_mt2712, - .ecc_regs = mt2712_ecc_regs, - .num_ecc_strength = 23, - .ecc_mode_shift = 5, - .parity_bits = 14, - .pg_irq_sel = 1, -}; - -static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { - .err_mask = 0x3f, - .ecc_strength = ecc_strength_mt7622, - .ecc_regs = mt7622_ecc_regs, - .num_ecc_strength = 7, - .ecc_mode_shift = 4, - .parity_bits = 13, - .pg_irq_sel = 0, -}; - -static const struct of_device_id mtk_ecc_dt_match[] = { - { - .compatible = "mediatek,mt2701-ecc", - .data = &mtk_ecc_caps_mt2701, - }, { - .compatible = "mediatek,mt2712-ecc", - .data = &mtk_ecc_caps_mt2712, - }, { - .compatible = "mediatek,mt7622-ecc", - .data = &mtk_ecc_caps_mt7622, - }, - {}, -}; - -static int mtk_ecc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mtk_ecc *ecc; - u32 max_eccdata_size; - int irq, ret; - - ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); - if (!ecc) - return -ENOMEM; - - ecc->caps = of_device_get_match_data(dev); - - max_eccdata_size = ecc->caps->num_ecc_strength - 1; - max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; - max_eccdata_size = (max_eccdata_size * ecc->caps->parity_bits + 7) >> 3; - max_eccdata_size = round_up(max_eccdata_size, 4); - ecc->eccdata = devm_kzalloc(dev, max_eccdata_size, GFP_KERNEL); - if (!ecc->eccdata) - return -ENOMEM; - - ecc->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ecc->regs)) - return PTR_ERR(ecc->regs); - - ecc->clk = devm_clk_get(dev, NULL); - if (IS_ERR(ecc->clk)) { - dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk)); - return PTR_ERR(ecc->clk); - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - ret = dma_set_mask(dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(dev, "failed to set DMA mask\n"); - return ret; - } - - ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc); - if (ret) { - dev_err(dev, "failed to request irq\n"); - return -EINVAL; - } - - ecc->dev = dev; - mutex_init(&ecc->lock); - platform_set_drvdata(pdev, ecc); - dev_info(dev, "probed\n"); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int mtk_ecc_suspend(struct device *dev) -{ - struct mtk_ecc *ecc = dev_get_drvdata(dev); - - clk_disable_unprepare(ecc->clk); - - return 0; -} - -static int mtk_ecc_resume(struct device *dev) -{ - struct mtk_ecc *ecc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(ecc->clk); - if (ret) { - dev_err(dev, "failed to enable clk\n"); - return ret; - } - - return 0; -} - -static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume); -#endif - -MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match); - -static struct platform_driver mtk_ecc_driver = { - .probe = mtk_ecc_probe, - .driver = { - .name = "mtk-ecc", - .of_match_table = mtk_ecc_dt_match, -#ifdef CONFIG_PM_SLEEP - .pm = &mtk_ecc_pm_ops, -#endif - }, -}; - -module_platform_driver(mtk_ecc_driver); - -MODULE_AUTHOR("Xiaolei Li "); -MODULE_DESCRIPTION("MTK Nand ECC Driver"); -MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/drivers/mtd/nand/raw/mtk_ecc.h deleted file mode 100644 index 0e48c36e6ca0..000000000000 --- a/drivers/mtd/nand/raw/mtk_ecc.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR MIT */ -/* - * MTK SDG1 ECC controller - * - * Copyright (c) 2016 Mediatek - * Authors: Xiaolei Li - * Jorge Ramirez-Ortiz - */ - -#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__ -#define __DRIVERS_MTD_NAND_MTK_ECC_H__ - -#include - -enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1}; -enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE}; - -struct device_node; -struct mtk_ecc; - -struct mtk_ecc_stats { - u32 corrected; - u32 bitflips; - u32 failed; -}; - -struct mtk_ecc_config { - enum mtk_ecc_operation op; - enum mtk_ecc_mode mode; - dma_addr_t addr; - u32 strength; - u32 sectors; - u32 len; -}; - -int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32); -void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int); -int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation); -int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *); -void mtk_ecc_disable(struct mtk_ecc *); -void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p); -unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc); - -struct mtk_ecc *of_mtk_ecc_get(struct device_node *); -void mtk_ecc_release(struct mtk_ecc *); - -#endif diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 66f04c693c87..d540454cbbdf 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -17,7 +17,7 @@ #include #include #include -#include "mtk_ecc.h" +#include /* NAND controller register definition */ #define NFI_CNFG (0x00) diff --git a/include/linux/mtd/nand-ecc-mtk.h b/include/linux/mtd/nand-ecc-mtk.h new file mode 100644 index 000000000000..0e48c36e6ca0 --- /dev/null +++ b/include/linux/mtd/nand-ecc-mtk.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * MTK SDG1 ECC controller + * + * Copyright (c) 2016 Mediatek + * Authors: Xiaolei Li + * Jorge Ramirez-Ortiz + */ + +#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__ +#define __DRIVERS_MTD_NAND_MTK_ECC_H__ + +#include + +enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1}; +enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE}; + +struct device_node; +struct mtk_ecc; + +struct mtk_ecc_stats { + u32 corrected; + u32 bitflips; + u32 failed; +}; + +struct mtk_ecc_config { + enum mtk_ecc_operation op; + enum mtk_ecc_mode mode; + dma_addr_t addr; + u32 strength; + u32 sectors; + u32 len; +}; + +int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32); +void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int); +int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation); +int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *); +void mtk_ecc_disable(struct mtk_ecc *); +void mtk_ecc_adjust_strength(struct mtk_ecc *ecc, u32 *p); +unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc); + +struct mtk_ecc *of_mtk_ecc_get(struct device_node *); +void mtk_ecc_release(struct mtk_ecc *); + +#endif -- cgit v1.2.3 From 4c5bf4b51c9857e20c5f5e9d74b86aa1bd1def40 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sun, 24 Apr 2022 11:25:25 +0800 Subject: mtd: nand: mtk-ecc: also parse nand-ecc-engine if available The recently added ECC engine support introduced a generic property named nand-ecc-engine for ecc engine phandle. This patch adds support for this new property. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220424032527.673605-4-gch981213@gmail.com --- drivers/mtd/nand/ecc-mtk.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index 491c2d66f815..02c13778514d 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -278,7 +278,10 @@ struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node) struct mtk_ecc *ecc = NULL; struct device_node *np; - np = of_parse_phandle(of_node, "ecc-engine", 0); + np = of_parse_phandle(of_node, "nand-ecc-engine", 0); + /* for backward compatibility */ + if (!np) + np = of_parse_phandle(of_node, "ecc-engine", 0); if (np) { ecc = mtk_ecc_get(np); of_node_put(np); -- cgit v1.2.3 From 773898127ebff6056d207c9b5901e97573999b74 Mon Sep 17 00:00:00 2001 From: Rickard x Andersson Date: Fri, 29 Apr 2022 10:39:31 +0200 Subject: mtd: rawnand: kioxia: Add support for TH58NVG3S0HBAI4 Add timings for Kioxia/Toshiba TH58NVG3S0HBAI4. Timings for this memory matches the timings selected for TH58NVG2S3HBAI4. This patch increases eraseblock write speed from 5248 KiB/s to 6864 KiB/s and erase block read speed from 8542 KiB/s to 18360 KiB/s Tested on i.MX6SX. Signed-off-by: Rickard x Andersson Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220429083931.26795-1-rickaran@axis.com --- drivers/mtd/nand/raw/nand_ids.c | 3 +++ drivers/mtd/nand/raw/nand_toshiba.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index d64adbd1ce6b..88c2440b47d8 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -61,6 +61,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"TH58NVG2S3HBAI4 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x91, 0x15, 0x76} }, SZ_2K, SZ_512, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) }, + {"TH58NVG3S0HBAI4 8G 3.3V 8-bit", + { .id = {0x98, 0xd3, 0x91, 0x26, 0x76} }, + SZ_4K, SZ_1K, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512)}, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index cf4f37959421..d3d34d71921f 100644 --- a/drivers/mtd/nand/raw/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -287,8 +287,10 @@ static int toshiba_nand_init(struct nand_chip *chip) if (!strncmp("TC58NVG0S3E", chip->parameters.model, sizeof("TC58NVG0S3E") - 1)) tc58nvg0s3e_init(chip); - if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model, - sizeof("TH58NVG2S3HBAI4") - 1)) + if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model, + sizeof("TH58NVG2S3HBAI4") - 1)) || + (!strncmp("TH58NVG3S0HBAI4", chip->parameters.model, + sizeof("TH58NVG3S0HBAI4") - 1))) th58nvg2s3hbai4_init(chip); return 0; -- cgit v1.2.3 From c96f824af0e9f88299430db8360dfc9e6c40df36 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Thu, 5 May 2022 02:23:54 +0000 Subject: mtd: rawnand: cs553x: simplify the return expression of cs553x_write_ctrl_byte() Simplify the return expression. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220505022354.61458-1-chi.minghao@zte.com.cn --- drivers/mtd/nand/raw/cs553x_nand.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 6edf78c16fc8..f0a15717cf05 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -104,17 +104,12 @@ static int cs553x_write_ctrl_byte(struct cs553x_nand_controller *cs553x, u32 ctl, u8 data) { u8 status; - int ret; writeb(ctl, cs553x->mmio + MM_NAND_CTL); writeb(data, cs553x->mmio + MM_NAND_IO); - ret = readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status, + return readb_poll_timeout_atomic(cs553x->mmio + MM_NAND_STS, status, !(status & CS_NAND_CTLR_BUSY), 1, 100000); - if (ret) - return ret; - - return 0; } static void cs553x_data_in(struct cs553x_nand_controller *cs553x, void *buf, -- cgit v1.2.3 From 66d7a40beb413815a5b1adbc1558200f5b18d817 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 9 May 2022 15:50:02 +0200 Subject: mtd: nand: MTD_NAND_ECC_MEDIATEK should depend on ARCH_MEDIATEK The MediaTek Hardware ECC Engine is only present on MediaTek MT27xx and MT76xx SoCs. The driver for this engine is a dependency for the MediaTek NAND controller (MTD_NAND_MTK) and the MediaTek SPI NAND Flash Interface (SPI_MTK_SNFI) drivers, both of which already depend on ARCH_MEDIATEK. Hence add a dependency on ARCH_MEDIATEK to the Hardware ECC Engine driver, too, to prevent asking the user about this driver when configuring a kernel without MediaTek SoC support. Fixes: 4fd62f15afa0d0da ("mtd: nand: make mtk_ecc.c a separated module") Signed-off-by: Geert Uytterhoeven Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/bb9568e825d4bc7506870b03836baa91bcc4b725.1652104136.git.geert+renesas@glider.be --- drivers/mtd/nand/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2f3e02ab72ed..5b0c2c95f10c 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -56,6 +56,7 @@ config MTD_NAND_ECC_MXIC config MTD_NAND_ECC_MEDIATEK tristate "Mediatek hardware ECC engine" depends on HAS_IOMEM + depends on ARCH_MEDIATEK || COMPILE_TEST select MTD_NAND_ECC help This enables support for the hardware ECC engine from Mediatek. -- cgit v1.2.3 From 6879854d16341ab67d61580fa988ad1b7e7cc040 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Thu, 12 May 2022 19:45:58 +0100 Subject: mtd: rawnand: rockchip: Check before clk_disable_unprepare() not needed All code in clk_disable_unprepare() already checks the clk ptr using IS_ERR_OR_NULL so there is no need to check it again before calling it. A lot of other drivers already rely on this behaviour, so it's safe to do so here. Signed-off-by: Phil Edworthy Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220512184558.45966-1-phil.edworthy@renesas.com --- drivers/mtd/nand/raw/rockchip-nand-controller.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index cbaa4f1c83da..f133985cc053 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -911,8 +911,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc) ret = clk_prepare_enable(nfc->ahb_clk); if (ret) { dev_err(dev, "failed to enable ahb clk\n"); - if (!IS_ERR(nfc->nfc_clk)) - clk_disable_unprepare(nfc->nfc_clk); + clk_disable_unprepare(nfc->nfc_clk); return ret; } @@ -921,8 +920,7 @@ static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc) static void rk_nfc_disable_clks(struct rk_nfc *nfc) { - if (!IS_ERR(nfc->nfc_clk)) - clk_disable_unprepare(nfc->nfc_clk); + clk_disable_unprepare(nfc->nfc_clk); clk_disable_unprepare(nfc->ahb_clk); } -- cgit v1.2.3 From 5794465b6fcfbaed304c8ebe8990f858eb2ed9a2 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Thu, 12 May 2022 19:50:33 +0100 Subject: mtd: rawnand: mpc5121: Check before clk_disable_unprepare() not needed All code in clk_disable_unprepare() already checks the clk ptr using IS_ERR_OR_NULL so there is no need to check it again before calling it. A lot of other drivers already rely on this behaviour, so it's safe to do so here. Signed-off-by: Phil Edworthy Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220512185033.46901-1-phil.edworthy@renesas.com --- drivers/mtd/nand/raw/mpc5121_nfc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 5b9271b9c326..800d774aed8e 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -595,8 +595,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - if (prv->clk) - clk_disable_unprepare(prv->clk); + clk_disable_unprepare(prv->clk); if (prv->csreg) iounmap(prv->csreg); -- cgit v1.2.3 From 6a2277a0ebe71b45e1d5508a2d7aecd28c98e3d3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 May 2022 12:49:57 +0200 Subject: mtd: rawnand: renesas: Use runtime PM instead of the raw clock API This NAND controller is part of a well defined power domain handled by the runtime PM core. Let's keep the harmony with the other RZ/N1 drivers and exclusively use the runtime PM API to enable/disable the clocks. We still need to retrieve the external clock rate in order to derive the NAND timings, but that is not a big deal, we can still do that in the probe and just save this value to reuse it later. Signed-off-by: Miquel Raynal Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/linux-mtd/20220513104957.257721-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/renesas-nand-controller.c | 51 ++++++++++++-------------- 1 file changed, 23 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c index 6db063b230a9..1620e25a1147 100644 --- a/drivers/mtd/nand/raw/renesas-nand-controller.c +++ b/drivers/mtd/nand/raw/renesas-nand-controller.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #define COMMAND_REG 0x00 @@ -216,8 +217,7 @@ struct rnandc { struct nand_controller controller; struct device *dev; void __iomem *regs; - struct clk *hclk; - struct clk *eclk; + unsigned long ext_clk_rate; unsigned long assigned_cs; struct list_head chips; struct nand_chip *selected_chip; @@ -891,7 +891,7 @@ static int rnandc_setup_interface(struct nand_chip *chip, int chipnr, { struct rnand_chip *rnand = to_rnand(chip); struct rnandc *rnandc = to_rnandc(chip->controller); - unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk); + unsigned int period_ns = 1000000000 / rnandc->ext_clk_rate; const struct nand_sdr_timings *sdr; unsigned int cyc, cle, ale, bef_dly, ca_to_data; @@ -1319,6 +1319,7 @@ cleanup_chips: static int rnandc_probe(struct platform_device *pdev) { struct rnandc *rnandc; + struct clk *eclk; int irq, ret; rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL); @@ -1335,29 +1336,26 @@ static int rnandc_probe(struct platform_device *pdev) if (IS_ERR(rnandc->regs)) return PTR_ERR(rnandc->regs); - /* APB clock */ - rnandc->hclk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(rnandc->hclk)) - return PTR_ERR(rnandc->hclk); - - /* External NAND bus clock */ - rnandc->eclk = devm_clk_get(&pdev->dev, "eclk"); - if (IS_ERR(rnandc->eclk)) - return PTR_ERR(rnandc->eclk); - - ret = clk_prepare_enable(rnandc->hclk); - if (ret) + devm_pm_runtime_enable(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret < 0) return ret; - ret = clk_prepare_enable(rnandc->eclk); - if (ret) - goto disable_hclk; + /* The external NAND bus clock rate is needed for computing timings */ + eclk = clk_get(&pdev->dev, "eclk"); + if (IS_ERR(eclk)) { + ret = PTR_ERR(eclk); + goto dis_runtime_pm; + } + + rnandc->ext_clk_rate = clk_get_rate(eclk); + clk_put(eclk); rnandc_dis_interrupts(rnandc); irq = platform_get_irq_optional(pdev, 0); if (irq == -EPROBE_DEFER) { ret = irq; - goto disable_eclk; + goto dis_runtime_pm; } else if (irq < 0) { dev_info(&pdev->dev, "No IRQ found, fallback to polling\n"); rnandc->use_polling = true; @@ -1365,12 +1363,12 @@ static int rnandc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0, "renesas-nand-controller", rnandc); if (ret < 0) - goto disable_eclk; + goto dis_runtime_pm; } ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) - goto disable_eclk; + goto dis_runtime_pm; rnandc_clear_fifo(rnandc); @@ -1378,14 +1376,12 @@ static int rnandc_probe(struct platform_device *pdev) ret = rnandc_chips_init(rnandc); if (ret) - goto disable_eclk; + goto dis_runtime_pm; return 0; -disable_eclk: - clk_disable_unprepare(rnandc->eclk); -disable_hclk: - clk_disable_unprepare(rnandc->hclk); +dis_runtime_pm: + pm_runtime_put(&pdev->dev); return ret; } @@ -1396,8 +1392,7 @@ static int rnandc_remove(struct platform_device *pdev) rnandc_chips_cleanup(rnandc); - clk_disable_unprepare(rnandc->eclk); - clk_disable_unprepare(rnandc->hclk); + pm_runtime_put(&pdev->dev); return 0; } -- cgit v1.2.3