diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/bch-regs.h | 12 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 441 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 24 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/macronix.c | 199 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spansion.c | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 2 |
6 files changed, 598 insertions, 82 deletions
diff --git a/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h index a22b8a506241..1b05476e8566 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/raw/gpmi-nand/bch-regs.h @@ -41,7 +41,7 @@ #define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 #define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) #define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ - (GPMI_IS_MX6(x) \ + ((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) \ ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ @@ -52,7 +52,7 @@ #define MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14 \ (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) #define BF_BCH_FLASH0LAYOUT0_GF(v, x) \ - ((GPMI_IS_MX6(x) && ((v) == 14)) \ + (((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) && ((v) == 14)) \ ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \ & MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \ : 0 \ @@ -64,7 +64,7 @@ #define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) #define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ - (GPMI_IS_MX6(x) \ + ((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) \ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ ) @@ -83,7 +83,7 @@ #define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 #define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) #define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ - (GPMI_IS_MX6(x) \ + ((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) \ ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ @@ -94,7 +94,7 @@ #define MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14 \ (0x1 << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) #define BF_BCH_FLASH0LAYOUT1_GF(v, x) \ - ((GPMI_IS_MX6(x) && ((v) == 14)) \ + (((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) && ((v) == 14)) \ ? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \ & MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \ : 0 \ @@ -106,7 +106,7 @@ #define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) #define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ - (GPMI_IS_MX6(x) \ + ((GPMI_IS_MX6(x) || GPMI_IS_MX8(x)) \ ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ ) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index aef722dfdef5..2636b6bdb677 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL- .0+ /* * Freescale GPMI NAND Flash Driver * @@ -14,12 +14,18 @@ #include <linux/mtd/partitions.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/busfreq-imx.h> #include <linux/pm_runtime.h> +#include <linux/debugfs.h> +#include <linux/pinctrl/consumer.h> #include <linux/dma/mxs-dma.h> #include "gpmi-nand.h" #include "gpmi-regs.h" #include "bch-regs.h" +/* export the bch geometry to dbgfs */ +static struct debugfs_blob_wrapper dbg_bch_geo; + /* Resource names for the GPMI NAND driver. */ #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" #define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" @@ -112,7 +118,7 @@ static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) return 0; error: - pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); + pr_err("%s(%px): module reset timeout\n", __func__, reset_addr); return -ETIMEDOUT; } @@ -146,7 +152,7 @@ err_clk: static int gpmi_init(struct gpmi_nand_data *this) { struct resources *r = &this->resources; - int ret; + int ret = 0; ret = pm_runtime_get_sync(this->dev); if (ret < 0) { @@ -218,7 +224,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" + "ECC Chunk0 Size in Bytes: %u\n" + "ECC Chunkn Size in Bytes: %u\n" "ECC Chunk Count : %u\n" "Payload Size in Bytes : %u\n" "Auxiliary Size in Bytes: %u\n" @@ -229,7 +236,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->ecc_chunk0_size, + geo->ecc_chunkn_size, geo->ecc_chunk_count, geo->payload_size, geo->auxiliary_size, @@ -251,6 +259,37 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; } +static inline 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->ecc_chunk0_size != geo->ecc_chunkn_size) { + dev_err(this->dev, "The size of chunk0 must equal to chunkn\n"); + return false; + } + + i = (mtd->writesize * 8 - geo->metadata_size * 8) / + (geo->gf_len * geo->ecc_strength + + geo->ecc_chunkn_size * 8); + + j = (mtd->writesize * 8 - geo->metadata_size * 8) - + (geo->gf_len * geo->ecc_strength + + geo->ecc_chunkn_size * 8) * i; + + if (j < geo->ecc_chunkn_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. @@ -280,13 +319,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->ecc_chunk0_size = ecc_step; + geo->ecc_chunkn_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->ecc_chunkn_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", ecc_step, mtd->oobsize); @@ -296,7 +336,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->ecc_chunkn_size; /* * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: @@ -399,6 +439,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->ecc_chunk0_size = 1024; + geo->ecc_chunkn_size = 1024; + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_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 = requirements->strength; + while (!(geo->ecc_strength > max_ecc)) { + 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->ecc_chunk0_size = 0; + geo->ecc_chunk_count = (mtd->writesize / geo->ecc_chunkn_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->ecc_chunkn_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->ecc_chunkn_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" + "ECC Chunk0 Size in Bytes: %u\n" + "ECC Chunkn 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->ecc_chunk0_size, + geo->ecc_chunkn_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; @@ -418,13 +586,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->ecc_chunk0_size = 512; + geo->ecc_chunkn_size = 512; + while (geo->ecc_chunkn_size < mtd->oobsize) { + geo->ecc_chunk0_size *= 2; /* keep C >= O */ + geo->ecc_chunkn_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->ecc_chunkn_size; /* We use the same ECC strength for all chunks. */ geo->ecc_strength = get_ecc_strength(this); @@ -514,12 +684,19 @@ 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); - if (chip->ecc.strength > 0 && chip->ecc.size > 0) - return set_geometry_by_ecc_info(this, chip->ecc.strength, - chip->ecc.size); + if (requirements->strength > 0 && requirements->step_size > 0) { + if (mtd->oobsize > 1024 + || requirements->step_size < mtd->oobsize) + return set_geometry_for_large_oob(this); + else + return set_geometry_by_ecc_info(this, + requirements->strength, + requirements->step_size); + } if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) || legacy_set_geometry(this)) { @@ -534,11 +711,40 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this) return 0; } +int bch_create_debugfs(struct gpmi_nand_data *this) +{ + struct bch_geometry *bch_geo = &this->bch_geometry; + struct dentry *dbg_root; + + dbg_root = debugfs_create_dir("gpmi-nand", NULL); + if (!dbg_root) { + dev_err(this->dev, "failed to create debug directory\n"); + return -EINVAL; + } + + dbg_bch_geo.data = (void *)bch_geo; + dbg_bch_geo.size = sizeof(struct bch_geometry); + if (!debugfs_create_blob("bch_geometry", S_IRUGO, + dbg_root, &dbg_bch_geo)) { + dev_err(this->dev, "failed to create debug bch geometry\n"); + return -EINVAL; + } + + /* create raw mode flag */ + if (!debugfs_create_file("raw_mode", S_IRUGO, + dbg_root, NULL, NULL)) { + dev_err(this->dev, "failed to create raw mode flag\n"); + return -EINVAL; + } + + return 0; +} + /* Configures the geometry for BCH. */ static int bch_set_geometry(struct gpmi_nand_data *this) { struct resources *r = &this->resources; - int ret; + int ret = 0; ret = common_nfc_set_geometry(this); if (ret) @@ -562,7 +768,6 @@ static int bch_set_geometry(struct gpmi_nand_data *this) /* Set *all* chip selects to use layout 0. */ writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); - ret = 0; err_out: pm_runtime_mark_last_busy(this->dev); pm_runtime_put_autosuspend(this->dev); @@ -697,7 +902,7 @@ static int gpmi_nfc_compute_timings(struct gpmi_nand_data *this, hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); - hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); + hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(DIV_ROUND_UP(busy_timeout_cycles, 4096)); /* * Derive NFC ideal delay from {3}: @@ -744,6 +949,9 @@ static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this) if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) clk_disable_unprepare(r->clock[0]); + if (GPMI_IS_MX6SX(this) && hw->clk_rate > 88000000) + hw->clk_rate = 88000000; + ret = clk_set_rate(r->clock[0], hw->clk_rate); if (ret) { dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret); @@ -790,7 +998,7 @@ static int gpmi_setup_interface(struct nand_chip *chip, int chipnr, return PTR_ERR(sdr); /* Only MX6 GPMI controller can reach EDO timings */ - if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) + if (sdr->tRC_min <= 25000 && !(GPMI_IS_MX6(this) || GPMI_IS_MX8(this))) return -ENOTSUPP; /* Stop here if this call was just a check */ @@ -845,7 +1053,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.ecc_chunkn_size); else return raw_len; } @@ -965,6 +1173,14 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = { .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), }; +static const struct gpmi_devdata gpmi_devdata_imx6qp = { + .type = IS_MX6QP, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + static const struct gpmi_devdata gpmi_devdata_imx6sx = { .type = IS_MX6SX, .bch_max_ecc_strength = 62, @@ -973,6 +1189,22 @@ static const struct gpmi_devdata gpmi_devdata_imx6sx = { .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), }; +static const struct gpmi_devdata gpmi_devdata_imx6ul = { + .type = IS_MX6UL, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + +static const struct gpmi_devdata gpmi_devdata_imx6ull = { + .type = IS_MX6ULL, + .bch_max_ecc_strength = 40, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx6, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), +}; + static const char * const gpmi_clks_for_mx7d[] = { "gpmi_io", "gpmi_bch_apb", }; @@ -984,6 +1216,17 @@ static const struct gpmi_devdata gpmi_devdata_imx7d = { .clks = gpmi_clks_for_mx7d, .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), }; +static const char * gpmi_clks_for_mx8qxp[GPMI_CLK_MAX] = { + "gpmi_clk", "gpmi_apb_clk", "bch_clk", "bch_apb_clk", +}; + +static const struct gpmi_devdata gpmi_devdata_imx8qxp = { + .type = IS_MX8QXP, + .bch_max_ecc_strength = 62, + .max_chain_delay = 12000, + .clks = gpmi_clks_for_mx8qxp, + .clks_count = ARRAY_SIZE(gpmi_clks_for_mx8qxp), +}; static int acquire_register_block(struct gpmi_nand_data *this, const char *res_name) @@ -1073,6 +1316,15 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) r->clock[i] = clk; } + if (GPMI_IS_MX6(this) || GPMI_IS_MX8(this)) + /* + * Set the default value for the gpmi clock. + * + * If you want to use the ONFI nand which is in the + * Synchronous Mode, you should change the clock as you need. + */ + clk_set_rate(r->clock[0], 22000000); + return 0; err_clock: @@ -1096,10 +1348,6 @@ static int acquire_resources(struct gpmi_nand_data *this) if (ret) goto exit_regs; - ret = acquire_dma_channels(this); - if (ret) - goto exit_regs; - ret = gpmi_get_clks(this); if (ret) goto exit_clock; @@ -1242,7 +1490,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->ecc_chunkn_size) + eccbits) * (i + 1); offset -= eccbits; bitoffset = offset % 8; eccbytes = DIV_ROUND_UP(offset + eccbits, 8); @@ -1279,16 +1527,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->ecc_chunkn_size, + nfc_geo->ecc_chunkn_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->ecc_chunkn_size, + nfc_geo->ecc_chunkn_size, eccbuf, eccbytes, NULL, 0, nfc_geo->ecc_strength); @@ -1317,20 +1565,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->ecc_chunk0_size; + unsigned int blockn_size = geo->ecc_chunkn_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, @@ -1413,9 +1662,22 @@ 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. + * - chunk0 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; } @@ -1423,19 +1685,27 @@ static int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, 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->ecc_chunk_size, this); + BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ? + 0: geo->ecc_chunk0_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->ecc_chunkn_size, this); this->bch = true; @@ -1607,7 +1877,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->ecc_chunkn_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; size_t src_bit_off; @@ -1692,7 +1962,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->ecc_chunkn_size; int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; u8 *tmp_buf = this->raw_buffer; uint8_t *oob = chip->oob_poi; @@ -1984,7 +2254,7 @@ static int mx23_boot_init(struct gpmi_nand_data *this) */ chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); page = block << (chip->phys_erase_shift - chip->page_shift); - byte = block << chip->phys_erase_shift; + byte = (loff_t)block << chip->phys_erase_shift; /* Send the command to read the conventional block mark. */ nand_select_target(chip, chipnr); @@ -2056,6 +2326,11 @@ static int gpmi_init_last(struct gpmi_nand_data *this) if (ret) return ret; + /* Save the geometry to debugfs*/ + ret = bch_create_debugfs(this); + if (ret) + return ret; + /* Init the nand_ecc_ctrl{} */ ecc->read_page = gpmi_ecc_read_page; ecc->write_page = gpmi_ecc_write_page; @@ -2066,7 +2341,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->ecc_chunkn_size; ecc->strength = bch_geo->ecc_strength; mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); @@ -2075,7 +2350,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) * (1) the chip is imx6, and * (2) the size of the ECC parity is byte aligned. */ - if (GPMI_IS_MX6(this) && + if ((GPMI_IS_MX6(this) || GPMI_IS_MX8(this)) && ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { ecc->read_subpage = gpmi_ecc_read_subpage; chip->options |= NAND_SUBPAGE_READ; @@ -2363,11 +2638,11 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip, &direct); break; } + } - if (!desc) { - ret = -ENXIO; - goto unmap; - } + if (!desc) { + ret = -ENXIO; + goto unmap; } dev_dbg(this->dev, "%s setup done\n", __func__); @@ -2458,6 +2733,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; struct mtd_info *mtd = nand_to_mtd(chip); + u32 max_cs; int ret; /* init the MTD data structures */ @@ -2488,7 +2764,12 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) this->base.ops = &gpmi_nand_controller_ops; chip->controller = &this->base; - ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); + max_cs = (GPMI_IS_MX6(this) || GPMI_IS_MX8(this)) ? 2 : 1; + + /* override the max_cs if board has other limitations */ + of_property_read_u32(this->pdev->dev.of_node, "fsl,max-nand-cs", &max_cs); + + ret = nand_scan(chip, max_cs); if (ret) goto err_out; @@ -2515,8 +2796,12 @@ static const struct of_device_id gpmi_nand_id_table[] = { { .compatible = "fsl,imx23-gpmi-nand", .data = &gpmi_devdata_imx23, }, { .compatible = "fsl,imx28-gpmi-nand", .data = &gpmi_devdata_imx28, }, { .compatible = "fsl,imx6q-gpmi-nand", .data = &gpmi_devdata_imx6q, }, + { .compatible = "fsl,imx6qp-gpmi-nand", .data = &gpmi_devdata_imx6qp, }, { .compatible = "fsl,imx6sx-gpmi-nand", .data = &gpmi_devdata_imx6sx, }, - { .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d,}, + { .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d, }, + { .compatible = "fsl,imx6ul-gpmi-nand", .data = &gpmi_devdata_imx6ul, }, + { .compatible = "fsl,imx6ull-gpmi-nand", .data = &gpmi_devdata_imx6ull, }, + { .compatible = "fsl,imx8qxp-gpmi-nand", .data = &gpmi_devdata_imx8qxp, }, {} }; MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); @@ -2539,15 +2824,9 @@ static int gpmi_nand_probe(struct platform_device *pdev) if (ret) goto exit_acquire_resources; - ret = __gpmi_enable_clk(this, true); - if (ret) - goto exit_acquire_resources; - + pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); ret = gpmi_init(this); if (ret) @@ -2557,15 +2836,12 @@ static int gpmi_nand_probe(struct platform_device *pdev) if (ret) goto exit_nfc_init; - pm_runtime_mark_last_busy(&pdev->dev); - pm_runtime_put_autosuspend(&pdev->dev); - dev_info(this->dev, "driver registered.\n"); return 0; exit_nfc_init: - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); release_resources(this); exit_acquire_resources: @@ -2579,7 +2855,6 @@ static int gpmi_nand_remove(struct platform_device *pdev) struct nand_chip *chip = &this->nand; int ret; - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); ret = mtd_device_unregister(nand_to_mtd(chip)); @@ -2593,10 +2868,12 @@ static int gpmi_nand_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int gpmi_pm_suspend(struct device *dev) { - struct gpmi_nand_data *this = dev_get_drvdata(dev); + int ret; - release_dma_channels(this); - return 0; + pinctrl_pm_select_sleep_state(dev); + ret = pm_runtime_force_suspend(dev); + + return ret; } static int gpmi_pm_resume(struct device *dev) @@ -2604,9 +2881,13 @@ static int gpmi_pm_resume(struct device *dev) struct gpmi_nand_data *this = dev_get_drvdata(dev); int ret; - ret = acquire_dma_channels(this); - if (ret < 0) + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(this->dev, "Error in resume %d\n", ret); return ret; + } + + pinctrl_pm_select_default_state(dev); /* re-init the GPMI registers */ ret = gpmi_init(this); @@ -2626,22 +2907,44 @@ static int gpmi_pm_resume(struct device *dev) return ret; } + /* re-apply the timing setting */ + this->hw.must_apply_timings = true; + return 0; } #endif /* CONFIG_PM_SLEEP */ -static int __maybe_unused gpmi_runtime_suspend(struct device *dev) +#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true) +#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false) + +static int gpmi_runtime_suspend(struct device *dev) { struct gpmi_nand_data *this = dev_get_drvdata(dev); - return __gpmi_enable_clk(this, false); + gpmi_disable_clk(this); + release_bus_freq(BUS_FREQ_HIGH); + release_dma_channels(this); + + return 0; } -static int __maybe_unused gpmi_runtime_resume(struct device *dev) +static int gpmi_runtime_resume(struct device *dev) { struct gpmi_nand_data *this = dev_get_drvdata(dev); + int ret; + + ret = gpmi_enable_clk(this); + if (ret) + return ret; + + request_bus_freq(BUS_FREQ_HIGH); + + ret = acquire_dma_channels(this); + if (ret < 0) + return ret; + + return 0; - return __gpmi_enable_clk(this, true); } static const struct dev_pm_ops gpmi_pm_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..8efd3c94c454 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. + * @ecc_chunk0_size: The size, in bytes, of a first ECC chunk. + * @ecc_chunkn_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. @@ -42,19 +42,23 @@ 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; unsigned int ecc_strength; unsigned int page_size; unsigned int metadata_size; - unsigned int ecc_chunk_size; + unsigned int ecc_chunk0_size; + unsigned int ecc_chunkn_size; unsigned int ecc_chunk_count; unsigned int payload_size; unsigned int auxiliary_size; 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 */ }; /** @@ -72,8 +76,12 @@ enum gpmi_type { IS_MX23, IS_MX28, IS_MX6Q, + IS_MX6QP, IS_MX6SX, IS_MX7D, + IS_MX6UL, + IS_MX6ULL, + IS_MX8QXP, }; struct gpmi_devdata { @@ -166,10 +174,16 @@ struct gpmi_nand_data { #define GPMI_IS_MX23(x) ((x)->devdata->type == IS_MX23) #define GPMI_IS_MX28(x) ((x)->devdata->type == IS_MX28) #define GPMI_IS_MX6Q(x) ((x)->devdata->type == IS_MX6Q) +#define GPMI_IS_MX6QP(x) ((x)->devdata->type == IS_MX6QP) #define GPMI_IS_MX6SX(x) ((x)->devdata->type == IS_MX6SX) #define GPMI_IS_MX7D(x) ((x)->devdata->type == IS_MX7D) +#define GPMI_IS_MX6UL(x) ((x)->devdata->type == IS_MX6UL) +#define GPMI_IS_MX6ULL(x) ((x)->devdata->type == IS_MX6ULL) +#define GPMI_IS_MX8QXP(x) ((x)->devdata->type == IS_MX8QXP) #define GPMI_IS_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x) || \ - GPMI_IS_MX7D(x)) + GPMI_IS_MX7D(x) || GPMI_IS_MX6UL(x) || \ + GPMI_IS_MX6ULL(x) || GPMI_IS_MX6QP(x)) +#define GPMI_IS_MX8(x) (GPMI_IS_MX8QXP(x)) #define GPMI_IS_MXS(x) (GPMI_IS_MX23(x) || GPMI_IS_MX28(x)) #endif diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 27498ed0cc0d..2c6ed978c69b 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -8,6 +8,13 @@ #include "core.h" +#define SPINOR_OP_RD_CR2 0x71 /* Read configuration register 2 */ +#define SPINOR_OP_WR_CR2 0x72 /* Write configuration register 2 */ +#define SPINOR_REG_MXIC_CR2_MODE 0x00000000 /* For setting octal DTR mode */ +#define SPINOR_REG_MXIC_OPI_DTR_EN 0x2 /* Enable Octal DTR */ +#define SPINOR_REG_MXIC_SPI_EN 0x0 /* Enable SPI */ +#define SPINOR_OP_OPI_DTR_RD 0xEE /* OPI DTR first read opcode */ + static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -32,6 +39,98 @@ static struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, }; +/** + * spi_nor_macronix_octal_dtr_enable() - Enable octal DTR on Macronix flashes. + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable Octal DTR or switch back to SPI + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_macronix_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf, i; + int ret; + + /* Set/unset the octal and DTR enable bits. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (enable) { + buf[0] = SPINOR_REG_MXIC_OPI_DTR_EN; + } else { + /* + * The register is 1-byte wide, but 1-byte transactions are not + * allowed in 8D-8D-8D mode. Since there is no register at the + * next location, just initialize the value to 0 and let the + * transaction go on. + */ + buf[0] = SPINOR_REG_MXIC_SPI_EN; + buf[1] = 0x0; + } + + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_CR2, 1), + SPI_MEM_OP_ADDR(4, SPINOR_REG_MXIC_CR2_MODE, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(enable ? 1 : 2, buf, 1)); + + if (!enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1), + SPI_MEM_OP_DUMMY(enable ? 4 : 0, 1), + SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, buf, 1)); + + if (enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (enable) { + for (i = 0; i < nor->info->id_len; i++) + if (buf[i * 2] != nor->info->id[i]) + return -EINVAL; + } else { + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; + } + + return 0; +} + +static void octaflash_default_init(struct spi_nor *nor) +{ + nor->params->octal_dtr_enable = spi_nor_macronix_octal_dtr_enable; +} + +static struct spi_nor_fixups octaflash_fixups = { + .default_init = octaflash_default_init, +}; + +static void mx25uw51345g_post_sfdp_fixup(struct spi_nor *nor) +{ + nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_OPI_DTR_RD, + SNOR_PROTO_8_8_8_DTR); +} + +static struct spi_nor_fixups mx25uw51345g_fixups = { + .default_init = octaflash_default_init, + .post_sfdp = mx25uw51345g_post_sfdp_fixup, +}; + static const struct flash_info macronix_parts[] = { /* Macronix */ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, @@ -87,6 +186,106 @@ static const struct flash_info macronix_parts[] = { { "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "mx66lm2g45g", INFO(0xc2853c, 0, 64 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66lm1g45g", INFO(0xc2853b, 0, 32 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66lw1g45g", INFO(0xc2863b, 0, 32 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25lm51245g", INFO(0xc2853a, 0, 16 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25lw51245g", INFO(0xc2863a, 0, 16 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25lm25645g", INFO(0xc28539, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25lw25645g", INFO(0xc28639, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66um2g45g", INFO(0xc2803c, 0, 64 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66uw2g345g", INFO(0xc2843c, 0, 64 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66uw2g345gx0", INFO(0xc2943c, 0, 64 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66um1g45g", INFO(0xc2803b, 0, 32 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66um1g45g40", INFO(0xc2808b, 0, 32 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx66uw1g45g", INFO(0xc2813b, 0, 32 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25um51245g", INFO(0xc2803a, 0, 16 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw51245g", INFO(0xc2813a, 0, 16 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw51345g", INFO(0xc2843a, 0, 16 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &mx25uw51345g_fixups }, + { "mx25um25645g", INFO(0xc28039, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw25645g", INFO(0xc28139, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25um25345g", INFO(0xc28339, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw25345g", INFO(0xc28439, 0, 8 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw12845g", INFO(0xc28138, 0, 4 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw12a45g", INFO(0xc28938, 0, 4 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw12345g", INFO(0xc28438, 0, 4 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw6445g", INFO(0xc28137, 0, 2 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, + { "mx25uw6345g", INFO(0xc28437, 0, 2 * 1024, 4096, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | SPI_NOR_4B_OPCODES) + .fixups = &octaflash_fixups }, }; static void macronix_default_init(struct spi_nor *nor) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index ee82dcd75310..6b8b2be93999 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -227,7 +227,7 @@ static const struct flash_info spansion_parts[] = { SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) + SPI_NOR_4B_OPCODES | USE_CLSR) .fixups = &s25fs_s_fixups, }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a32050fecabf..33b26d4326d8 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -594,7 +594,7 @@ static int io_init(struct ubi_device *ubi, int max_beb_per1024) dbg_gen("sizeof(struct ubi_ainf_peb) %zu", sizeof(struct ubi_ainf_peb)); dbg_gen("sizeof(struct ubi_wl_entry) %zu", sizeof(struct ubi_wl_entry)); - if (ubi->mtd->numeraseregions != 0) { + if (ubi->mtd->numeraseregions > 1) { /* * Some flashes have several erase regions. Different regions * may have different eraseblock size and other |