summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/bch-regs.h12
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c441
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h24
-rw-r--r--drivers/mtd/spi-nor/macronix.c199
-rw-r--r--drivers/mtd/spi-nor/spansion.c2
-rw-r--r--drivers/mtd/ubi/build.c2
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