diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0001.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0002.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0020.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_probe.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_util.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/gen_probe.c | 2 | ||||
-rw-r--r-- | drivers/mtd/chips/jedec_probe.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/map_ram.c | 3 | ||||
-rw-r--r-- | drivers/mtd/chips/map_rom.c | 3 | ||||
-rw-r--r-- | drivers/mtd/devices/st_spi_fsm.c | 28 | ||||
-rw-r--r-- | drivers/mtd/maps/pismo.c | 2 | ||||
-rw-r--r-- | drivers/mtd/mtdcore.c | 21 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/arasan-nand-controller.c | 15 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/internals.h | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/meson_nand.c | 134 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_ids.c | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_macronix.c | 167 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_sandisk.c | 26 | ||||
-rw-r--r-- | drivers/mtd/nand/spi/gigadevice.c | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/spi/macronix.c | 20 | ||||
-rw-r--r-- | drivers/mtd/sm_ftl.c | 2 |
23 files changed, 398 insertions, 61 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 54f92d09d9cf..11b06fefaa0e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Flash Interface support: * Intel Extended Vendor Command Set (ID 0x0001) * - * (C) 2000 Red Hat. GPL'd + * (C) 2000 Red Hat. * * * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 67453f59c69c..153fb8d0008e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Flash Interface support: * AMD & Fujitsu Standard Vendor Command Set (ID 0x0002) @@ -16,8 +17,6 @@ * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0 * * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com - * - * This code is GPL */ #include <linux/module.h> diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d35df526e0a6..60c7f6f751c7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -1,8 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Flash Interface support: * ST Advanced Architecture Command Set (ID 0x0020) * - * (C) 2000 Red Hat. GPL'd + * (C) 2000 Red Hat. * * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> * - completely revamped method functions so they are aware and diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index cf426956454c..a04b6174181c 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* Common Flash Interface probe code. - (C) 2000 Red Hat. GPL'd. + (C) 2000 Red Hat. */ #include <linux/module.h> diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 6a6a2a21d2ed..140c69a67e82 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -1,11 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Flash Interface support: * Generic utility functions not dependent on command set * * Copyright (C) 2002 Red Hat * Copyright (C) 2003 STMicroelectronics Limited - * - * This code is covered by the GPL. */ #include <linux/module.h> diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 4d4f97841016..9e53fcd7600d 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. - * GPL'd */ #include <linux/kernel.h> diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 6f7e7e1b3fe5..23c32fe584b7 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* Common Flash Interface probe code. - (C) 2000 Red Hat. GPL'd. + (C) 2000 Red Hat. See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index c37fce926864..e8dd6496927e 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common code to handle map devices which are simple RAM - * (C) 2000 Red Hat. GPL'd. + * (C) 2000 Red Hat. */ #include <linux/module.h> diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 20e3604b4d71..0823b15aaadb 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common code to handle map devices which are simple ROM - * (C) 2000 Red Hat. GPL'd. + * (C) 2000 Red Hat. */ #include <linux/module.h> diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 54861d889c30..3dbb1aa80bfa 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2046,34 +2046,26 @@ static int stfsm_probe(struct platform_device *pdev) return PTR_ERR(fsm->base); } - fsm->clk = devm_clk_get(&pdev->dev, NULL); + fsm->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(fsm->clk)) { dev_err(fsm->dev, "Couldn't find EMI clock.\n"); return PTR_ERR(fsm->clk); } - ret = clk_prepare_enable(fsm->clk); - if (ret) { - dev_err(fsm->dev, "Failed to enable EMI clock.\n"); - return ret; - } - mutex_init(&fsm->lock); ret = stfsm_init(fsm); if (ret) { dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); - goto err_clk_unprepare; + return ret; } stfsm_fetch_platform_configs(pdev); /* Detect SPI FLASH device */ info = stfsm_jedec_probe(fsm); - if (!info) { - ret = -ENODEV; - goto err_clk_unprepare; - } + if (!info) + return -ENODEV; fsm->info = info; /* Use device size to determine address width */ @@ -2089,7 +2081,7 @@ static int stfsm_probe(struct platform_device *pdev) else ret = stfsm_prepare_rwe_seqs_default(fsm); if (ret) - goto err_clk_unprepare; + return ret; fsm->mtd.name = info->name; fsm->mtd.dev.parent = &pdev->dev; @@ -2112,13 +2104,7 @@ static int stfsm_probe(struct platform_device *pdev) (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); - ret = mtd_device_register(&fsm->mtd, NULL, 0); - if (ret) { -err_clk_unprepare: - clk_disable_unprepare(fsm->clk); - } - - return ret; + return mtd_device_register(&fsm->mtd, NULL, 0); } static int stfsm_remove(struct platform_device *pdev) @@ -2127,8 +2113,6 @@ static int stfsm_remove(struct platform_device *pdev) WARN_ON(mtd_device_unregister(&fsm->mtd)); - clk_disable_unprepare(fsm->clk); - return 0; } diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index 3e0fff3f129e..ecf68922da73 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -259,7 +259,7 @@ static struct i2c_driver pismo_driver = { .driver = { .name = "pismo", }, - .probe_new = pismo_probe, + .probe = pismo_probe, .remove = pismo_remove, .id_table = pismo_id, }; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 60670b2f70b9..e00b12aa5ec9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -23,6 +23,7 @@ #include <linux/idr.h> #include <linux/backing-dev.h> #include <linux/gfp.h> +#include <linux/random.h> #include <linux/slab.h> #include <linux/reboot.h> #include <linux/leds.h> @@ -966,6 +967,26 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd) } if (size > 0) { + /* + * The factory OTP contains thing such as a unique serial + * number and is small, so let's read it out and put it + * into the entropy pool. + */ + void *otp; + + otp = kmalloc(size, GFP_KERNEL); + if (!otp) { + err = -ENOMEM; + goto err; + } + err = mtd_nvmem_fact_otp_reg_read(mtd, 0, otp, size); + if (err < 0) { + kfree(otp); + goto err; + } + add_device_randomness(otp, err); + kfree(otp); + nvmem = mtd_otp_nvmem_register(mtd, "factory-otp", size, mtd_nvmem_fact_otp_reg_read); if (IS_ERR(nvmem)) { diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 85f5ee6f06fc..a46affbb037d 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -326,7 +326,6 @@ static int __mtd_del_partition(struct mtd_info *mtd) static int __del_mtd_partitions(struct mtd_info *mtd) { struct mtd_info *child, *next; - LIST_HEAD(tmp_list); int ret, err = 0; list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 917cdfb815b9..d93e861d8ba7 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -67,5 +67,6 @@ nand-objs += nand_esmt.o nand-objs += nand_hynix.o nand-objs += nand_macronix.o nand-objs += nand_micron.o +nand-objs += nand_sandisk.o nand-objs += nand_samsung.o nand-objs += nand_toshiba.o diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index d513d2db3549..906eef70cb6d 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -973,21 +973,6 @@ static int anfc_setup_interface(struct nand_chip *chip, int target, nvddr = nand_get_nvddr_timings(conf); if (IS_ERR(nvddr)) return PTR_ERR(nvddr); - - /* - * The controller only supports data payload requests which are - * a multiple of 4. In practice, most data accesses are 4-byte - * aligned and this is not an issue. However, rounding up will - * simply be refused by the controller if we reached the end of - * the device *and* we are using the NV-DDR interface(!). In - * this situation, unaligned data requests ending at the device - * boundary will confuse the controller and cannot be performed. - * - * This is something that happens in nand_read_subpage() when - * selecting software ECC support and must be avoided. - */ - if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) - return -ENOTSUPP; } else { sdr = nand_get_sdr_timings(conf); if (IS_ERR(sdr)) diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index 7016e0f38398..e9932da18bdd 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -73,6 +73,7 @@ extern const struct nand_manufacturer_ops hynix_nand_manuf_ops; extern const struct nand_manufacturer_ops macronix_nand_manuf_ops; extern const struct nand_manufacturer_ops micron_nand_manuf_ops; extern const struct nand_manufacturer_ops samsung_nand_manuf_ops; +extern const struct nand_manufacturer_ops sandisk_nand_manuf_ops; extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops; /* MLC pairing schemes */ diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 1feea7d82252..d3faf8086631 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -38,6 +38,7 @@ #define NFC_CMD_SCRAMBLER_DISABLE 0 #define NFC_CMD_SHORTMODE_DISABLE 0 #define NFC_CMD_RB_INT BIT(14) +#define NFC_CMD_RB_INT_NO_PIN ((0xb << 10) | BIT(18) | BIT(16)) #define NFC_CMD_GET_SIZE(x) (((x) >> 22) & GENMASK(4, 0)) @@ -76,6 +77,7 @@ #define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff)) #define DMA_DIR(dir) ((dir) ? NFC_CMD_N2M : NFC_CMD_M2N) +#define DMA_ADDR_ALIGN 8 #define ECC_CHECK_RETURN_FF (-1) @@ -108,6 +110,11 @@ #define PER_INFO_BYTE 8 +#define NFC_CMD_RAW_LEN GENMASK(13, 0) + +#define NFC_COLUMN_ADDR_0 0 +#define NFC_COLUMN_ADDR_1 0 + struct meson_nfc_nand_chip { struct list_head node; struct nand_chip nand; @@ -179,6 +186,7 @@ struct meson_nfc { u32 info_bytes; unsigned long assigned_cs; + bool no_rb_pin; }; enum { @@ -280,7 +288,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir, if (raw) { len = mtd->writesize + mtd->oobsize; - cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir); + cmd = len | scrambler | DMA_DIR(dir); writel(cmd, nfc->reg_base + NFC_REG_CMD); return; } @@ -392,7 +400,42 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand, } } -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms) +static int meson_nfc_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms, + bool need_cmd_read0) +{ + u32 cmd, cfg; + + meson_nfc_cmd_idle(nfc, nfc->timing.twb); + meson_nfc_drain_cmd(nfc); + meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); + + cfg = readl(nfc->reg_base + NFC_REG_CFG); + cfg |= NFC_RB_IRQ_EN; + writel(cfg, nfc->reg_base + NFC_REG_CFG); + + reinit_completion(&nfc->completion); + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + /* use the max erase time as the maximum clock for waiting R/B */ + cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + + if (!wait_for_completion_timeout(&nfc->completion, + msecs_to_jiffies(timeout_ms))) + return -ETIMEDOUT; + + if (need_cmd_read0) { + cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0; + writel(cmd, nfc->reg_base + NFC_REG_CMD); + meson_nfc_drain_cmd(nfc); + meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); + } + + return 0; +} + +static int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms) { u32 cmd, cfg; int ret = 0; @@ -420,6 +463,27 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms) return ret; } +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, + bool need_cmd_read0) +{ + if (nfc->no_rb_pin) { + /* This mode is used when there is no wired R/B pin. + * It works like 'nand_soft_waitrdy()', but instead of + * polling NAND_CMD_STATUS bit in the software loop, + * it will wait for interrupt - controllers checks IO + * bus and when it detects NAND_CMD_STATUS on it, it + * raises interrupt. After interrupt, NAND_CMD_READ0 is + * sent as terminator of the ready waiting procedure if + * needed (for all cases except page programming - this + * is reason of 'need_cmd_read0' flag). + */ + return meson_nfc_wait_no_rb_pin(nfc, timeout_ms, + need_cmd_read0); + } else { + return meson_nfc_wait_rb_pin(nfc, timeout_ms); + } +} + static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf) { struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); @@ -544,7 +608,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len) if (ret) goto out; - cmd = NFC_CMD_N2M | (len & GENMASK(13, 0)); + cmd = NFC_CMD_N2M | len; writel(cmd, nfc->reg_base + NFC_REG_CMD); meson_nfc_drain_cmd(nfc); @@ -568,7 +632,7 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len) if (ret) return ret; - cmd = NFC_CMD_M2N | (len & GENMASK(13, 0)); + cmd = NFC_CMD_M2N | len; writel(cmd, nfc->reg_base + NFC_REG_CMD); meson_nfc_drain_cmd(nfc); @@ -595,12 +659,12 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN; nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0; - addrs[0] = cs | NFC_CMD_ALE | 0; + addrs[0] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_0; if (mtd->writesize <= 512) { cmd_num--; row_start = 1; } else { - addrs[1] = cs | NFC_CMD_ALE | 0; + addrs[1] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_1; row_start = 2; } @@ -623,7 +687,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, if (in) { nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART; writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD); - meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max)); + meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), true); } else { meson_nfc_cmd_idle(nfc, nfc->timing.tadl); } @@ -669,7 +733,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand, cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; writel(cmd, nfc->reg_base + NFC_REG_CMD); - meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max)); + meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), false); meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE); @@ -842,6 +906,9 @@ static int meson_nfc_read_oob(struct nand_chip *nand, int page) static bool meson_nfc_is_buffer_dma_safe(const void *buffer) { + if ((uintptr_t)buffer % DMA_ADDR_ALIGN) + return false; + if (virt_addr_valid(buffer) && (!object_is_on_stack(buffer))) return true; return false; @@ -899,6 +966,31 @@ meson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr, kfree(buf); } +static int meson_nfc_check_op(struct nand_chip *chip, + const struct nand_operation *op) +{ + int op_id; + + for (op_id = 0; op_id < op->ninstrs; op_id++) { + const struct nand_op_instr *instr; + + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_DATA_IN_INSTR: + case NAND_OP_DATA_OUT_INSTR: + if (instr->ctx.data.len > NFC_CMD_RAW_LEN) + return -ENOTSUPP; + + break; + default: + break; + } + } + + return 0; +} + static int meson_nfc_exec_op(struct nand_chip *nand, const struct nand_operation *op, bool check_only) { @@ -907,8 +999,13 @@ static int meson_nfc_exec_op(struct nand_chip *nand, const struct nand_op_instr *instr = NULL; void *buf; u32 op_id, delay_idle, cmd; + int err; int i; + err = meson_nfc_check_op(nand, op); + if (err) + return err; + if (check_only) return 0; @@ -952,7 +1049,8 @@ static int meson_nfc_exec_op(struct nand_chip *nand, break; case NAND_OP_WAITRDY_INSTR: - meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms); + meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, + true); if (instr->delay_ns) meson_nfc_cmd_idle(nfc, delay_idle); break; @@ -1181,6 +1279,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand) struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand); struct mtd_info *mtd = nand_to_mtd(nand); int nsectors = mtd->writesize / 1024; + int raw_writesize; int ret; if (!mtd->name) { @@ -1192,6 +1291,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand) return -ENOMEM; } + raw_writesize = mtd->writesize + mtd->oobsize; + if (raw_writesize > NFC_CMD_RAW_LEN) { + dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n", + raw_writesize, NFC_CMD_RAW_LEN); + return -EINVAL; + } + if (nand->bbt_options & NAND_BBT_USE_FLASH) nand->bbt_options |= NAND_BBT_NO_OOB; @@ -1248,6 +1354,7 @@ meson_nfc_nand_chip_init(struct device *dev, struct mtd_info *mtd; int ret, i; u32 tmp, nsels; + u32 nand_rb_val = 0; nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32)); if (!nsels || nsels > MAX_CE_NUM) { @@ -1287,6 +1394,15 @@ meson_nfc_nand_chip_init(struct device *dev, mtd->owner = THIS_MODULE; mtd->dev.parent = dev; + ret = of_property_read_u32(np, "nand-rb", &nand_rb_val); + if (ret == -EINVAL) + nfc->no_rb_pin = true; + else if (ret) + return ret; + + if (nand_rb_val) + return -EINVAL; + ret = nand_scan(nand, nsels); if (ret) return ret; diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index dacc5529b3df..650351c62af6 100644 --- a/drivers/mtd/nand/raw/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c @@ -44,6 +44,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"TC58NVG6D2 64G 3.3V 8-bit", { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNQGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x57} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, {"SDTNRGAMA 64G 3.3V 8-bit", { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, @@ -188,7 +191,7 @@ static const struct nand_manufacturer_desc nand_manufacturer_descs[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops}, - {NAND_MFR_SANDISK, "SanDisk"}, + {NAND_MFR_SANDISK, "SanDisk", &sandisk_nand_manuf_ops}, {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops}, {NAND_MFR_WINBOND, "Winbond"}, diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 385957eb6762..e229de32ff50 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -6,6 +6,7 @@ * Author: Boris Brezillon <boris.brezillon@free-electrons.com> */ +#include <linux/slab.h> #include "linux/delay.h" #include "internals.h" @@ -31,6 +32,16 @@ #define MXIC_CMD_POWER_DOWN 0xB9 +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2 +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ + (MACRONIX_30LFXG18AC_OTP_PAGES * \ + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) + +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -315,6 +326,161 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) chip->ops.resume = mxic_nand_resume; } +static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, + struct otp_info *buf) +{ + if (len < sizeof(*buf)) + return -EINVAL; + + /* Always report that OTP is unlocked. Reason is that this + * type of flash chip doesn't provide way to check that OTP + * is locked or not: subfeature parameter is implemented as + * volatile register. Technically OTP region could be locked + * and become readonly, but as there is no way to check it, + * don't allow to lock it ('_lock_user_prot_reg' callback + * always returns -EOPNOTSUPP) and thus we report that OTP + * is unlocked. + */ + buf->locked = 0; + buf->start = 0; + buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES; + + *retlen = sizeof(*buf); + + return 0; +} + +static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand) +{ + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; + + feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN; + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, + feature_buf); +} + +static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand) +{ + u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 }; + + return nand_set_features(nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, + feature_buf); +} + +static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd, + loff_t offs_in_flash, + size_t len, size_t *retlen, + u_char *buf, bool write) +{ + struct nand_chip *nand; + size_t bytes_handled; + off_t offs_in_page; + u64 page; + int ret; + + nand = mtd_to_nand(mtd); + nand_select_target(nand, 0); + + ret = macronix_30lfxg18ac_otp_enable(nand); + if (ret) + goto out_otp; + + page = offs_in_flash; + /* 'page' will be result of division. */ + offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE); + bytes_handled = 0; + + while (bytes_handled < len && + page < MACRONIX_30LFXG18AC_OTP_PAGES) { + size_t bytes_to_handle; + u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE; + + bytes_to_handle = min_t(size_t, len - bytes_handled, + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE - + offs_in_page); + + if (write) + ret = nand_prog_page_op(nand, phys_page, offs_in_page, + &buf[bytes_handled], bytes_to_handle); + else + ret = nand_read_page_op(nand, phys_page, offs_in_page, + &buf[bytes_handled], bytes_to_handle); + if (ret) + goto out_otp; + + bytes_handled += bytes_to_handle; + offs_in_page = 0; + page++; + } + + *retlen = bytes_handled; + +out_otp: + if (ret) + dev_err(&mtd->dev, "failed to perform OTP IO: %i\n", ret); + + ret = macronix_30lfxg18ac_otp_disable(nand); + if (ret) + dev_err(&mtd->dev, "failed to leave OTP mode after %s\n", + write ? "write" : "read"); + + nand_deselect_target(nand); + + return ret; +} + +static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to, + size_t len, size_t *rlen, + const u_char *buf) +{ + return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf, + true); +} + +static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from, + size_t len, size_t *rlen, + u_char *buf) +{ + return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false); +} + +static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from, + size_t len) +{ + /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */ + return -EOPNOTSUPP; +} + +static void macronix_nand_setup_otp(struct nand_chip *chip) +{ + static const char * const supported_otp_models[] = { + "MX30LF1G18AC", + "MX30LF2G18AC", + "MX30LF4G18AC", + }; + struct mtd_info *mtd; + + if (match_string(supported_otp_models, + ARRAY_SIZE(supported_otp_models), + chip->parameters.model) < 0) + return; + + if (!chip->parameters.supports_set_get_features) + return; + + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + + mtd = nand_to_mtd(chip); + mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info; + mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp; + mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp; + mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -324,6 +490,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_onfi_init(chip); macronix_nand_block_protection_support(chip); macronix_nand_deep_power_down_support(chip); + macronix_nand_setup_otp(chip); return 0; } diff --git a/drivers/mtd/nand/raw/nand_sandisk.c b/drivers/mtd/nand/raw/nand_sandisk.c new file mode 100644 index 000000000000..7c66e4187dc7 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_sandisk.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "internals.h" + +static int +sdtnqgama_choose_interface_config(struct nand_chip *chip, + struct nand_interface_config *iface) +{ + onfi_fill_interface_config(chip, iface, NAND_SDR_IFACE, 0); + + return nand_choose_best_sdr_timings(chip, iface, NULL); +} + +static int sandisk_nand_init(struct nand_chip *chip) +{ + if (!strncmp("SDTNQGAMA", chip->parameters.model, + sizeof("SDTNQGAMA") - 1)) + chip->ops.choose_interface_config = + &sdtnqgama_choose_interface_config; + + return 0; +} + +const struct nand_manufacturer_ops sandisk_nand_manuf_ops = { + .init = sandisk_nand_init, +}; diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 6b043e24855f..cfd7c3b26dc4 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -501,6 +501,16 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F2GQ5xExxH", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), + NAND_MEMORG(1, 2048, 64, 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, + gd5fxgq4uexxg_ecc_get_status)), }; static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 722a9738ba37..3dfc7e1e5241 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -299,6 +299,26 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX31LF2GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2e), + 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(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX3UF2GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae), + 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(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), }; static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 4cfec3b7b446..b5b3c4c44a94 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -981,7 +981,7 @@ restart: /* Update the FTL table */ zone->lba_to_phys_table[ftl->cache_block] = write_sector; - /* Write succesfull, so erase and free the old block */ + /* Write successful, so erase and free the old block */ if (block_num > 0) sm_erase_block(ftl, zone_num, block_num, 1); |