summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Makefile2
-rw-r--r--drivers/mtd/nand/gpmi-nand/bch-regs.h24
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c111
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c458
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h37
-rw-r--r--drivers/mtd/spi-nor/Kconfig6
-rw-r--r--drivers/mtd/spi-nor/Makefile1
-rw-r--r--drivers/mtd/spi-nor/fsl-flexspi.c1471
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c329
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c104
-rw-r--r--drivers/mtd/ubi/build.c2
11 files changed, 2405 insertions, 140 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 99bb9a1f6e16..f46b1ccff165 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -30,7 +30,7 @@ obj-$(CONFIG_MTD_SWAP) += mtdswap.o
nftl-objs := nftlcore.o nftlmount.o
inftl-objs := inftlcore.o inftlmount.o
+obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
-obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
obj-$(CONFIG_MTD_UBI) += ubi/
diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h
index 05bb91f2f4c4..bebe633d191c 100644
--- a/drivers/mtd/nand/gpmi-nand/bch-regs.h
+++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h
@@ -1,7 +1,7 @@
/*
* Freescale GPMI NAND Flash Driver
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2008-2015 Freescale Semiconductor, Inc.
* Copyright 2008 Embedded Alley Solutions, Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -30,7 +30,13 @@
#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0)
#define HW_BCH_STATUS0 0x00000010
+
#define HW_BCH_MODE 0x00000020
+#define BP_BCH_MODE_ERASE_THRESHOLD 0
+#define BM_BCH_MODE_ERASE_THRESHOLD (0xff << BP_BCH_MODE_ERASE_THRESHOLD)
+#define BF_BCH_MODE_ERASE_THRESHOLD(v) \
+ (((v) << BP_BCH_MODE_ERASE_THRESHOLD) & BM_BCH_MODE_ERASE_THRESHOLD)
+
#define HW_BCH_ENCODEPTR 0x00000030
#define HW_BCH_DATAPTR 0x00000040
#define HW_BCH_METAPTR 0x00000050
@@ -54,7 +60,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_MX7(x) || GPMI_IS_MX8(x)) \
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \
& MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \
: (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \
@@ -65,7 +71,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_MX7(x) || GPMI_IS_MX8(x)) && ((v) == 14))\
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT0_GF_13_14) \
& MX6Q_BM_BCH_FLASH0LAYOUT0_GF_13_14) \
: 0 \
@@ -77,7 +83,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_MX7(x) || GPMI_IS_MX8(x)) \
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
: ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \
)
@@ -96,7 +102,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_MX7(x) || GPMI_IS_MX8(x)) \
? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \
& MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \
: (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \
@@ -107,7 +113,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_MX7(x) || GPMI_IS_MX8(x)) && ((v) == 14))\
? (((1) << MX6Q_BP_BCH_FLASH0LAYOUT1_GF_13_14) \
& MX6Q_BM_BCH_FLASH0LAYOUT1_GF_13_14) \
: 0 \
@@ -119,10 +125,14 @@
#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_MX7(x) || GPMI_IS_MX8(x)) \
? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
: ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \
)
#define HW_BCH_VERSION 0x00000160
+#define HW_BCH_DEBUG1 0x00000170
+#define BP_BCH_DEBUG1_ERASED_ZERO_COUNT 0
+#define BM_BCH_DEBUG1_ERASED_ZERO_COUNT \
+ (0x1ff << BP_BCH_DEBUG1_ERASED_ZERO_COUNT)
#endif
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index b9509230ce4d..8a533039715a 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -1,8 +1,9 @@
/*
* Freescale GPMI NAND Flash Driver
*
- * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ * Copyright (C) 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,11 +22,17 @@
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.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;
+
static struct timing_threshod timing_default_threshold = {
.max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >>
BP_GPMI_TIMING0_DATA_SETUP),
@@ -124,7 +131,7 @@ error:
return -ETIMEDOUT;
}
-static int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
+int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
{
struct clk *clk;
int ret;
@@ -151,17 +158,17 @@ err_clk:
return ret;
}
-#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
-#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
-
int gpmi_init(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
int ret;
- ret = gpmi_enable_clk(this);
- if (ret)
+ ret = pm_runtime_get_sync(this->dev);
+ if (ret < 0) {
+ dev_err(this->dev, "Failed to enable clock\n");
return ret;
+ }
+
ret = gpmi_reset_block(r->gpmi_regs, false);
if (ret)
goto err_out;
@@ -195,10 +202,10 @@ int gpmi_init(struct gpmi_nand_data *this)
*/
writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET);
- gpmi_disable_clk(this);
- return 0;
err_out:
- gpmi_disable_clk(this);
+ pm_runtime_mark_last_busy(this->dev);
+ pm_runtime_put_autosuspend(this->dev);
+
return ret;
}
@@ -227,7 +234,8 @@ 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"
@@ -238,7 +246,8 @@ 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,
@@ -247,13 +256,43 @@ void gpmi_dump_info(struct gpmi_nand_data *this)
geo->block_mark_bit_offset);
}
+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. */
int bch_set_geometry(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
struct bch_geometry *bch_geo = &this->bch_geometry;
unsigned int block_count;
- unsigned int block_size;
+ unsigned int block0_size;
+ unsigned int blockn_size;
unsigned int metadata_size;
unsigned int ecc_strength;
unsigned int page_size;
@@ -264,15 +303,18 @@ int bch_set_geometry(struct gpmi_nand_data *this)
return !0;
block_count = bch_geo->ecc_chunk_count - 1;
- block_size = bch_geo->ecc_chunk_size;
+ block0_size = bch_geo->ecc_chunk0_size;
+ blockn_size = bch_geo->ecc_chunkn_size;
metadata_size = bch_geo->metadata_size;
ecc_strength = bch_geo->ecc_strength >> 1;
page_size = bch_geo->page_size;
gf_len = bch_geo->gf_len;
- ret = gpmi_enable_clk(this);
- if (ret)
+ ret = pm_runtime_get_sync(this->dev);
+ if (ret < 0) {
+ dev_err(this->dev, "Failed to enable clock\n");
return ret;
+ }
/*
* Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
@@ -289,15 +331,20 @@ int bch_set_geometry(struct gpmi_nand_data *this)
| BF_BCH_FLASH0LAYOUT0_META_SIZE(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),
r->bch_regs + HW_BCH_FLASH0LAYOUT0);
writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(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),
r->bch_regs + HW_BCH_FLASH0LAYOUT1);
+ /* Set erase threshold to ecc strength for mx6ul, mx6qp and mx7 */
+ if (GPMI_IS_MX6QP(this) || GPMI_IS_MX7(this) || GPMI_IS_MX6UL(this))
+ writel(BF_BCH_MODE_ERASE_THRESHOLD(ecc_strength),
+ r->bch_regs + HW_BCH_MODE);
+
/* Set *all* chip selects to use layout 0. */
writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
@@ -305,10 +352,10 @@ int bch_set_geometry(struct gpmi_nand_data *this)
writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
r->bch_regs + HW_BCH_CTRL_SET);
- gpmi_disable_clk(this);
- return 0;
err_out:
- gpmi_disable_clk(this);
+ pm_runtime_mark_last_busy(this->dev);
+ pm_runtime_put_autosuspend(this->dev);
+
return ret;
}
@@ -947,9 +994,14 @@ static int enable_edo_mode(struct gpmi_nand_data *this, int mode)
nand->select_chip(mtd, -1);
+ pm_runtime_get_sync(this->dev);
+ clk_disable_unprepare(r->clock[0]);
/* [3] set the main IO clock, 100MHz for mode 5, 80MHz for mode 4. */
rate = (mode == 5) ? 100000000 : 80000000;
clk_set_rate(r->clock[0], rate);
+ clk_prepare_enable(r->clock[0]);
+ pm_runtime_mark_last_busy(this->dev);
+ pm_runtime_put_autosuspend(this->dev);
/* Let the gpmi_begin() re-compute the timing again. */
this->flags &= ~GPMI_TIMING_INIT_OK;
@@ -972,7 +1024,8 @@ int gpmi_extra_init(struct gpmi_nand_data *this)
struct nand_chip *chip = &this->nand;
/* Enable the asynchronous EDO feature. */
- if (GPMI_IS_MX6(this) && chip->onfi_version) {
+ if ((GPMI_IS_MX6(this) || GPMI_IS_MX7(this) || GPMI_IS_MX8(this))
+ && chip->onfi_version) {
int mode = onfi_get_async_timing_mode(chip);
/* We only support the timing mode 4 and mode 5. */
@@ -1000,9 +1053,9 @@ void gpmi_begin(struct gpmi_nand_data *this)
int ret;
/* Enable the clock. */
- ret = gpmi_enable_clk(this);
- if (ret) {
- dev_err(this->dev, "We failed in enable the clk\n");
+ ret = pm_runtime_get_sync(this->dev);
+ if (ret < 0) {
+ dev_err(this->dev, "Failed to enable clock\n");
goto err_out;
}
@@ -1074,7 +1127,8 @@ err_out:
void gpmi_end(struct gpmi_nand_data *this)
{
- gpmi_disable_clk(this);
+ pm_runtime_mark_last_busy(this->dev);
+ pm_runtime_put_autosuspend(this->dev);
}
/* Clears a BCH interrupt. */
@@ -1094,12 +1148,13 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
if (GPMI_IS_MX23(this)) {
mask = MX23_BM_GPMI_DEBUG_READY0 << chip;
reg = readl(r->gpmi_regs + HW_GPMI_DEBUG);
- } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this)) {
+ } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6(this) ||
+ GPMI_IS_MX7(this) || GPMI_IS_MX8(this)) {
/*
* In the imx6, all the ready/busy pins are bound
* together. So we only need to check chip 0.
*/
- if (GPMI_IS_MX6(this))
+ if (GPMI_IS_MX6(this) || GPMI_IS_MX7(this) || GPMI_IS_MX8(this))
chip = 0;
/* MX28 shares the same R/B register as MX6Q. */
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index d9dab4275859..9446a1f9a539 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -1,7 +1,7 @@
/*
* Freescale GPMI NAND Flash Driver
*
- * Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2016 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,8 @@
#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 "gpmi-nand.h"
#include "bch-regs.h"
@@ -33,6 +35,8 @@
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch"
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch"
+#define GPMI_RPM_TIMEOUT 50 /* ms */
+
/* add our owner bbt descriptor */
static uint8_t scan_ff_pattern[] = { 0xff };
static struct nand_bbt_descr gpmi_bbt_descr = {
@@ -104,12 +108,42 @@ static const struct gpmi_devdata gpmi_devdata_imx6q = {
.max_chain_delay = 12,
};
+static const struct gpmi_devdata gpmi_devdata_imx6qp = {
+ .type = IS_MX6QP,
+ .bch_max_ecc_strength = 40,
+ .max_chain_delay = 12,
+};
+
static const struct gpmi_devdata gpmi_devdata_imx6sx = {
.type = IS_MX6SX,
.bch_max_ecc_strength = 62,
.max_chain_delay = 12,
};
+static const struct gpmi_devdata gpmi_devdata_imx7d = {
+ .type = IS_MX7D,
+ .bch_max_ecc_strength = 62,
+ .max_chain_delay = 12,
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx6ul = {
+ .type = IS_MX6UL,
+ .bch_max_ecc_strength = 40,
+ .max_chain_delay = 12,
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx6ull = {
+ .type = IS_MX6ULL,
+ .bch_max_ecc_strength = 40,
+ .max_chain_delay = 12,
+};
+
+static const struct gpmi_devdata gpmi_devdata_imx8qxp = {
+ .type = IS_MX8QXP,
+ .bch_max_ecc_strength = 62,
+ .max_chain_delay = 12,
+};
+
static irqreturn_t bch_irq(int irq, void *cookie)
{
struct gpmi_nand_data *this = cookie;
@@ -163,6 +197,36 @@ 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 mtd_info *mtd = &this->nand.mtd;
+ 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.
@@ -192,13 +256,14 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
chip->ecc_strength_ds, chip->ecc_step_ds);
return -EINVAL;
}
- geo->ecc_chunk_size = chip->ecc_step_ds;
+ geo->ecc_chunk0_size = chip->ecc_step_ds;
+ geo->ecc_chunkn_size = chip->ecc_step_ds;
geo->ecc_strength = round_up(chip->ecc_strength_ds, 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",
chip->ecc_step_ds, mtd->oobsize);
@@ -208,7 +273,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:
@@ -280,6 +345,132 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
return 0;
}
+static int set_geometry_for_large_oob(struct gpmi_nand_data *this)
+{
+ struct bch_geometry *geo = &this->bch_geometry;
+ struct mtd_info *mtd = &this->nand.mtd;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ 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 (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+ geo->ecc_strength = chip->ecc_strength_ds;
+
+ /* 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 = chip->ecc_strength_ds;
+ 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 = chip->ecc_strength_ds;
+ /* 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;
@@ -299,20 +490,22 @@ 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);
if (!gpmi_check_ecc(this)) {
dev_err(this->dev,
"ecc strength: %d cannot be supported by the controller (%d)\n"
- "try to use minimum ecc strength that NAND chip required\n",
+ "try to use maximum ecc strength that NAND chip required\n",
geo->ecc_strength,
this->devdata->bch_max_ecc_strength);
return -EINVAL;
@@ -394,11 +587,26 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
- if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
- || legacy_set_geometry(this))
- return set_geometry_by_ecc_info(this);
+ struct mtd_info *mtd = &this->nand.mtd;
+ struct nand_chip *chip = mtd_to_nand(mtd);
- return 0;
+ if (chip->ecc_strength_ds > this->devdata->bch_max_ecc_strength) {
+ dev_err(this->dev,
+ "unsupported NAND chip, minimum ecc required %d\n"
+ , chip->ecc_strength_ds);
+ return -EINVAL;
+ }
+
+ if ((!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) &&
+ (mtd->oobsize < 1024)) || this->legacy_bch_geometry) {
+ dev_warn(this->dev, "use legacy bch geometry\n");
+ return legacy_set_geometry(this);
+ }
+
+ if (mtd->oobsize > 1024 || chip->ecc_step_ds < mtd->oobsize)
+ return set_geometry_for_large_oob(this);
+
+ return set_geometry_by_ecc_info(this);
}
struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
@@ -582,6 +790,9 @@ static int acquire_dma_channels(struct gpmi_nand_data *this)
{
struct platform_device *pdev = this->pdev;
struct dma_chan *dma_chan;
+ struct device_node *np = pdev->dev.of_node;
+
+ of_dma_configure(&pdev->dev, np);
/* request dma channel */
dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx");
@@ -602,6 +813,14 @@ static char *extra_clks_for_mx6q[GPMI_CLK_MAX] = {
"gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
};
+static char *extra_clks_for_mx7d[GPMI_CLK_MAX] = {
+ "gpmi_bch_apb",
+};
+
+static char *extra_clks_for_mx8qxp[GPMI_CLK_MAX] = {
+ "gpmi_apb", "gpmi_bch", "gpmi_apb_bch",
+};
+
static int gpmi_get_clks(struct gpmi_nand_data *this)
{
struct resources *r = &this->resources;
@@ -619,6 +838,11 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
/* Get extra clocks */
if (GPMI_IS_MX6(this))
extra_clks = extra_clks_for_mx6q;
+ if (GPMI_IS_MX7(this))
+ extra_clks = extra_clks_for_mx7d;
+ if (GPMI_IS_MX8(this))
+ extra_clks = extra_clks_for_mx8qxp;
+
if (!extra_clks)
return 0;
@@ -635,7 +859,7 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
r->clock[i] = clk;
}
- if (GPMI_IS_MX6(this))
+ if (GPMI_IS_MX6(this) || GPMI_IS_MX7(this) || GPMI_IS_MX8(this))
/*
* Set the default value for the gpmi clock.
*
@@ -651,6 +875,15 @@ err_clock:
return err;
}
+static int init_rpm(struct gpmi_nand_data *this)
+{
+ pm_runtime_enable(this->dev);
+ pm_runtime_set_autosuspend_delay(this->dev, GPMI_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(this->dev);
+
+ return 0;
+}
+
static int acquire_resources(struct gpmi_nand_data *this)
{
int ret;
@@ -667,13 +900,10 @@ 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;
+
return 0;
exit_clock:
@@ -1026,6 +1256,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
{
struct gpmi_nand_data *this = nand_get_controller_data(chip);
struct bch_geometry *nfc_geo = &this->bch_geometry;
+ void __iomem *bch_regs = this->resources.bch_regs;
void *payload_virt;
dma_addr_t payload_phys;
void *auxiliary_virt;
@@ -1034,6 +1265,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
unsigned char *status;
unsigned int max_bitflips = 0;
int ret;
+ int flag = 0;
dev_dbg(this->dev, "page number is : %d\n", page);
ret = read_page_prepare(this, buf, nfc_geo->payload_size,
@@ -1068,8 +1300,16 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
payload_virt, payload_phys);
for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
- if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+ if (*status == STATUS_GOOD)
+ continue;
+
+ if (*status == STATUS_ERASED) {
+ if (GPMI_IS_MX6QP(this) || GPMI_IS_MX7(this) ||
+ GPMI_IS_MX6UL(this))
+ if (readl(bch_regs + HW_BCH_DEBUG1))
+ flag = 1;
continue;
+ }
if (*status == STATUS_UNCORRECTABLE) {
int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
@@ -1080,7 +1320,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* 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);
@@ -1117,16 +1357,16 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
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,
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);
@@ -1165,6 +1405,10 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
}
+ /* if bitflip occurred in erased page, change data to all 0xff */
+ if (flag)
+ memset(buf, 0xff, nfc_geo->payload_size);
+
return max_bitflips;
}
@@ -1209,9 +1453,23 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
}
}
+ /*
+ * 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;
+
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
meta = 0;
@@ -1224,21 +1482,37 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
/* change the BCH registers and bch_geometry{} */
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;
r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
BM_BCH_FLASH0LAYOUT0_META_SIZE);
- r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+ r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(
+ (geo->ecc_for_meta && meta) ? n : n - 1)
| BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+
+ /* set chunk0 size if meta size is 0 */
+ if (!meta) {
+ if (GPMI_IS_MX6(this) || GPMI_IS_MX7(this) || GPMI_IS_MX8(this))
+ r1_new &= ~MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE;
+ else
+ r1_new &= ~BM_BCH_FLASH0LAYOUT0_DATA0_SIZE;
+ r1_new |= BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(size, this);
+ }
writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
- geo->ecc_chunk_count = n;
+ geo->ecc_chunk_count = (geo->ecc_for_meta && meta) ? n + 1 : n;
geo->payload_size = n * size;
geo->page_size = page_size;
+ geo->metadata_size = meta;
geo->auxiliary_status_offset = ALIGN(meta, 4);
dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
@@ -1460,7 +1734,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
{
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;
@@ -1468,6 +1742,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
size_t oob_byte_off;
uint8_t *oob = chip->oob_poi;
int step;
+ int ecc_chunk_count;
chip->read_buf(mtd, tmp_buf,
mtd->writesize + mtd->oobsize);
@@ -1495,9 +1770,21 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
oob_bit_off = nfc_geo->metadata_size * 8;
src_bit_off = oob_bit_off;
+ ecc_chunk_count = nfc_geo->ecc_chunk_count;
+ /* if bch requires dedicate ecc for meta */
+ if (nfc_geo->ecc_for_meta) {
+ if (oob_required)
+ gpmi_copy_bits(oob, oob_bit_off,
+ tmp_buf, src_bit_off,
+ eccbits);
+
+ src_bit_off += eccbits;
+ oob_bit_off += eccbits;
+ ecc_chunk_count = nfc_geo->ecc_chunk_count - 1;
+ }
/* Extract interleaved payload data and ECC bits */
- for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+ for (step = 0; step < ecc_chunk_count; step++) {
if (buf)
gpmi_copy_bits(buf, step * eccsize * 8,
tmp_buf, src_bit_off,
@@ -1505,7 +1792,7 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
src_bit_off += eccsize * 8;
/* Align last ECC block to align a byte boundary */
- if (step == nfc_geo->ecc_chunk_count - 1 &&
+ if (step == ecc_chunk_count - 1 &&
(oob_bit_off + eccbits) % 8)
eccbits += 8 - ((oob_bit_off + eccbits) % 8);
@@ -1549,7 +1836,7 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
{
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;
@@ -1557,6 +1844,7 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
size_t oob_bit_off;
size_t oob_byte_off;
int step;
+ int ecc_chunk_count;
/*
* Initialize all bits to 1 in case we don't have a buffer for the
@@ -1573,16 +1861,28 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
memcpy(tmp_buf, oob, nfc_geo->metadata_size);
oob_bit_off = nfc_geo->metadata_size * 8;
dst_bit_off = oob_bit_off;
+ ecc_chunk_count = nfc_geo->ecc_chunk_count;
+
+ /* if bch requires dedicate ecc for meta */
+ if (nfc_geo->ecc_for_meta) {
+ if (oob_required)
+ gpmi_copy_bits(tmp_buf, dst_bit_off,
+ oob, oob_bit_off, eccbits);
+
+ dst_bit_off += eccbits;
+ oob_bit_off += eccbits;
+ ecc_chunk_count = nfc_geo->ecc_chunk_count - 1;
+ }
/* Interleave payload data and ECC bits */
- for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+ for (step = 0; step < ecc_chunk_count; step++) {
if (buf)
gpmi_copy_bits(tmp_buf, dst_bit_off,
buf, step * eccsize * 8, eccsize * 8);
dst_bit_off += eccsize * 8;
/* Align last ECC block to align a byte boundary */
- if (step == nfc_geo->ecc_chunk_count - 1 &&
+ if (step == ecc_chunk_count - 1 &&
(oob_bit_off + eccbits) % 8)
eccbits += 8 - ((oob_bit_off + eccbits) % 8);
@@ -1872,7 +2172,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. */
chip->select_chip(mtd, chipnr);
@@ -1947,6 +2247,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;
@@ -1957,7 +2262,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->mode = NAND_ECC_HW;
- 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);
@@ -1966,7 +2271,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_MX7(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;
@@ -2022,7 +2327,8 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
if (ret)
goto err_out;
- ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) ? 2 : 1, NULL);
+ ret = nand_scan_ident(mtd, GPMI_IS_MX6(this) || GPMI_IS_MX7(this)\
+ || GPMI_IS_MX8(this) ? 2 : 1, NULL);
if (ret)
goto err_out;
@@ -2032,6 +2338,10 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
if (of_property_read_bool(this->dev->of_node,
"fsl,no-blockmark-swap"))
this->swap_block_mark = false;
+
+ if (of_property_read_bool(this->dev->of_node,
+ "fsl,legacy-bch-geometry"))
+ this->legacy_bch_geometry = true;
}
dev_dbg(this->dev, "Blockmark swapping %sabled\n",
this->swap_block_mark ? "en" : "dis");
@@ -2075,9 +2385,24 @@ static const struct of_device_id gpmi_nand_id_table[] = {
.compatible = "fsl,imx6q-gpmi-nand",
.data = &gpmi_devdata_imx6q,
}, {
+ .compatible = "fsl,imx6qp-gpmi-nand",
+ .data = (void *)&gpmi_devdata_imx6qp,
+ }, {
.compatible = "fsl,imx6sx-gpmi-nand",
.data = &gpmi_devdata_imx6sx,
- }, {}
+ }, {
+ .compatible = "fsl,imx6ul-gpmi-nand",
+ .data = (void *)&gpmi_devdata_imx6ul,
+ }, {
+ .compatible = "fsl,imx7d-gpmi-nand",
+ .data = (void *)&gpmi_devdata_imx7d,
+ }, {
+ .compatible = "fsl,imx6ull-gpmi-nand",
+ .data = (void *)&gpmi_devdata_imx6ull,
+ }, {
+ .compatible = "fsl,imx8qxp-gpmi-nand",
+ .data = (void *)&gpmi_devdata_imx8qxp,
+ }, { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
@@ -2107,6 +2432,10 @@ static int gpmi_nand_probe(struct platform_device *pdev)
if (ret)
goto exit_acquire_resources;
+ ret = init_rpm(this);
+ if (ret)
+ goto exit_nfc_init;
+
ret = init_hardware(this);
if (ret)
goto exit_nfc_init;
@@ -2131,6 +2460,7 @@ static int gpmi_nand_remove(struct platform_device *pdev)
struct gpmi_nand_data *this = platform_get_drvdata(pdev);
gpmi_nand_exit(this);
+ pm_runtime_disable(this->dev);
release_resources(this);
return 0;
}
@@ -2138,10 +2468,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)
@@ -2149,9 +2481,14 @@ 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)
+ /* enable clock, acquire dma */
+ 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 */
this->flags &= ~GPMI_TIMING_INIT_OK;
@@ -2175,7 +2512,40 @@ static int gpmi_pm_resume(struct device *dev)
}
#endif /* CONFIG_PM_SLEEP */
+#define gpmi_enable_clk(x) __gpmi_enable_clk(x, true)
+#define gpmi_disable_clk(x) __gpmi_enable_clk(x, false)
+
+int gpmi_runtime_suspend(struct device *dev)
+{
+ struct gpmi_nand_data *this = dev_get_drvdata(dev);
+
+ gpmi_disable_clk(this);
+ release_bus_freq(BUS_FREQ_HIGH);
+ release_dma_channels(this);
+
+ return 0;
+}
+
+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;
+}
+
static const struct dev_pm_ops gpmi_pm_ops = {
+ SET_RUNTIME_PM_OPS(gpmi_runtime_suspend, gpmi_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
};
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 4e49a1f5fa27..f92616b99871 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -1,7 +1,7 @@
/*
* Freescale GPMI NAND Flash Driver
*
- * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2010-2016 Freescale Semiconductor, Inc.
* Copyright (C) 2008 Embedded Alley Solutions, Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -39,9 +39,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.
@@ -51,19 +51,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 */
};
/**
@@ -123,7 +127,12 @@ enum gpmi_type {
IS_MX23,
IS_MX28,
IS_MX6Q,
- IS_MX6SX
+ IS_MX6QP,
+ IS_MX6SX,
+ IS_MX7D,
+ IS_MX6UL,
+ IS_MX6ULL,
+ IS_MX8QXP,
};
struct gpmi_devdata {
@@ -189,6 +198,8 @@ struct gpmi_nand_data {
dma_addr_t auxiliary_phys;
void *raw_buffer;
+ /* legacy bch geometry flag */
+ bool legacy_bch_geometry;
/* DMA channels */
#define DMA_CHANS 8
@@ -275,10 +286,12 @@ extern int start_dma_with_bch_irq(struct gpmi_nand_data *,
struct dma_async_tx_descriptor *);
/* GPMI-NAND helper function library */
+extern int __gpmi_enable_clk(struct gpmi_nand_data *, bool v);
extern int gpmi_init(struct gpmi_nand_data *);
extern int gpmi_extra_init(struct gpmi_nand_data *);
extern void gpmi_clear_bch(struct gpmi_nand_data *);
extern void gpmi_dump_info(struct gpmi_nand_data *);
+extern int bch_create_debugfs(struct gpmi_nand_data *);
extern int bch_set_geometry(struct gpmi_nand_data *);
extern int gpmi_is_ready(struct gpmi_nand_data *, unsigned chip);
extern int gpmi_send_command(struct gpmi_nand_data *);
@@ -304,7 +317,15 @@ void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
#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_MX6(x) (GPMI_IS_MX6Q(x) || GPMI_IS_MX6SX(x))
+#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_MX6QP(x)\
+ || GPMI_IS_MX6SX(x) || GPMI_IS_MX6UL(x) || GPMI_IS_MX6ULL(x))
+#define GPMI_IS_MX7(x) (GPMI_IS_MX7D(x))
+#define GPMI_IS_MX8(x) (GPMI_IS_MX8QXP(x))
#endif
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index b4f6cadd28fe..c57ad3a8937b 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -76,4 +76,10 @@ config SPI_NXP_SPIFI
Flash. Enable this option if you have a device with a SPIFI
controller and want to access the Flash as a mtd device.
+config SPI_FSL_FLEXSPI
+ tristate "Freescale Flex SPI controller"
+ help
+ This enables support for the Flex SPI controller in master mode.
+ We only connect the NOR to this controller now.
+
endif # MTD_SPI_NOR
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 121695e83542..5bace3c031ab 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
+obj-$(CONFIG_SPI_FSL_FLEXSPI) += fsl-flexspi.o
diff --git a/drivers/mtd/spi-nor/fsl-flexspi.c b/drivers/mtd/spi-nor/fsl-flexspi.c
new file mode 100644
index 000000000000..1aff05fd7459
--- /dev/null
+++ b/drivers/mtd/spi-nor/fsl-flexspi.c
@@ -0,0 +1,1471 @@
+/*
+ * Freescale FlexSPI driver.
+ *
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/completion.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+#include <linux/pci.h>
+#include <soc/imx8/sc/sci.h>
+#include <linux/pm_runtime.h>
+
+/* Board only enabled up to Quad mode, not Octal*/
+#define FLEXSPI_QUIRK_QUAD_ONLY (1 << 0)
+
+/* runtime pm timeout */
+#define FSL_FLEXSPI_RPM_TIMEOUT 50 /* 50ms */
+
+/* The registers */
+#define FLEXSPI_MCR0 0x00
+#define FLEXSPI_MCR0_AHB_TIMEOUT_SHIFT 24
+#define FLEXSPI_MCR0_AHB_TIMEOUT_MASK (0xFF << FLEXSPI_MCR0_AHB_TIMEOUT_SHIFT)
+#define FLEXSPI_MCR0_IP_TIMEOUT_SHIFT 16
+#define FLEXSPI_MCR0_IP_TIMEOUT_MASK (0xFF << FLEXSPI_MCR0_IP_TIMEOUT_SHIFT)
+#define FLEXSPI_MCR0_LEARN_EN_SHIFT 15
+#define FLEXSPI_MCR0_LEARN_EN_MASK (1 << FLEXSPI_MCR0_LEARN_EN_SHIFT)
+#define FLEXSPI_MCR0_SCRFRUN_EN_SHIFT 14
+#define FLEXSPI_MCR0_SCRFRUN_EN_MASK (1 << FLEXSPI_MCR0_SCRFRUN_EN_SHIFT)
+#define FLEXSPI_MCR0_OCTCOMB_EN_SHIFT 13
+#define FLEXSPI_MCR0_OCTCOMB_EN_MASK (1 << FLEXSPI_MCR0_OCTCOMB_EN_SHIFT)
+#define FLEXSPI_MCR0_DOZE_EN_SHIFT 12
+#define FLEXSPI_MCR0_DOZE_EN_MASK (1 << FLEXSPI_MCR0_DOZE_EN_SHIFT)
+#define FLEXSPI_MCR0_HSEN_SHIFT 11
+#define FLEXSPI_MCR0_HSEN_MASK (1 << FLEXSPI_MCR0_HSEN_SHIFT)
+#define FLEXSPI_MCR0_SERCLKDIV_SHIFT 8
+#define FLEXSPI_MCR0_SERCLKDIV_MASK (7 << FLEXSPI_MCR0_SERCLKDIV_SHIFT)
+#define FLEXSPI_MCR0_ATDF_EN_SHIFT 7
+#define FLEXSPI_MCR0_ATDF_EN_MASK (1 << FLEXSPI_MCR0_ATDF_EN_SHIFT)
+#define FLEXSPI_MCR0_ARDF_EN_SHIFT 6
+#define FLEXSPI_MCR0_ARDF_EN_MASK (1 << FLEXSPI_MCR0_ARDF_EN_SHIFT)
+#define FLEXSPI_MCR0_RXCLKSRC_SHIFT 4
+#define FLEXSPI_MCR0_RXCLKSRC_MASK (3 << FLEXSPI_MCR0_RXCLKSRC_SHIFT)
+#define FLEXSPI_MCR0_END_CFG_SHIFT 2
+#define FLEXSPI_MCR0_END_CFG_MASK (3 << FLEXSPI_MCR0_END_CFG_SHIFT)
+#define FLEXSPI_MCR0_MDIS_SHIFT 1
+#define FLEXSPI_MCR0_MDIS_MASK (1 << FLEXSPI_MCR0_MDIS_SHIFT)
+#define FLEXSPI_MCR0_SWRST_SHIFT 0
+#define FLEXSPI_MCR0_SWRST_MASK (1 << FLEXSPI_MCR0_SWRST_SHIFT)
+
+#define FLEXSPI_MCR1 0x04
+#define FLEXSPI_MCR1_SEQ_TIMEOUT_SHIFT 16
+#define FLEXSPI_MCR1_SEQ_TIMEOUT_MASK \
+ (0xFFFF << FLEXSPI_MCR1_SEQ_TIMEOUT_SHIFT)
+#define FLEXSPI_MCR1_AHB_TIMEOUT_SHIFT 0
+#define FLEXSPI_MCR1_AHB_TIMEOUT_MASK \
+ (0xFFFF << FLEXSPI_MCR1_AHB_TIMEOUT_SHIFT)
+
+#define FLEXSPI_MCR2 0x08
+#define FLEXSPI_MCR2_IDLE_WAIT_SHIFT 24
+#define FLEXSPI_MCR2_IDLE_WAIT_MASK (0xFF << FLEXSPI_MCR2_IDLE_WAIT_SHIFT)
+#define FLEXSPI_MCR2_SAMEFLASH_SHIFT 15
+#define FLEXSPI_MCR2_SAMEFLASH_MASK (1 << FLEXSPI_MCR2_SAMEFLASH_SHIFT)
+#define FLEXSPI_MCR2_CLRLRPHS_SHIFT 14
+#define FLEXSPI_MCR2_CLRLRPHS_MASK (1 << FLEXSPI_MCR2_CLRLRPHS_SHIFT)
+#define FLEXSPI_MCR2_ABRDATSZ_SHIFT 8
+#define FLEXSPI_MCR2_ABRDATSZ_MASK (1 << FLEXSPI_MCR2_ABRDATSZ_SHIFT)
+#define FLEXSPI_MCR2_ABRLEARN_SHIFT 7
+#define FLEXSPI_MCR2_ABRLEARN_MASK (1 << FLEXSPI_MCR2_ABRLEARN_SHIFT)
+#define FLEXSPI_MCR2_ABR_READ_SHIFT 6
+#define FLEXSPI_MCR2_ABR_READ_MASK (1 << FLEXSPI_MCR2_ABR_READ_SHIFT)
+#define FLEXSPI_MCR2_ABRWRITE_SHIFT 5
+#define FLEXSPI_MCR2_ABRWRITE_MASK (1 << FLEXSPI_MCR2_ABRWRITE_SHIFT)
+#define FLEXSPI_MCR2_ABRDUMMY_SHIFT 4
+#define FLEXSPI_MCR2_ABRDUMMY_MASK (1 << FLEXSPI_MCR2_ABRDUMMY_SHIFT)
+#define FLEXSPI_MCR2_ABR_MODE_SHIFT 3
+#define FLEXSPI_MCR2_ABR_MODE_MASK (1 << FLEXSPI_MCR2_ABR_MODE_SHIFT)
+#define FLEXSPI_MCR2_ABRCADDR_SHIFT 2
+#define FLEXSPI_MCR2_ABRCADDR_MASK (1 << FLEXSPI_MCR2_ABRCADDR_SHIFT)
+#define FLEXSPI_MCR2_ABRRADDR_SHIFT 1
+#define FLEXSPI_MCR2_ABRRADDR_MASK (1 << FLEXSPI_MCR2_ABRRADDR_SHIFT)
+#define FLEXSPI_MCR2_ABR_CMD_SHIFT 0
+#define FLEXSPI_MCR2_ABR_CMD_MASK (1 << FLEXSPI_MCR2_ABR_CMD_SHIFT)
+
+#define FLEXSPI_AHBCR 0x0c
+#define FLEXSPI_AHBCR_RDADDROPT_SHIFT 6
+#define FLEXSPI_AHBCR_RDADDROPT_MASK (1 << FLEXSPI_AHBCR_RDADDROPT_SHIFT)
+#define FLEXSPI_AHBCR_PREF_EN_SHIFT 5
+#define FLEXSPI_AHBCR_PREF_EN_MASK (1 << FLEXSPI_AHBCR_PREF_EN_SHIFT)
+#define FLEXSPI_AHBCR_BUFF_EN_SHIFT 4
+#define FLEXSPI_AHBCR_BUFF_EN_MASK (1 << FLEXSPI_AHBCR_BUFF_EN_SHIFT)
+#define FLEXSPI_AHBCR_CACH_EN_SHIFT 3
+#define FLEXSPI_AHBCR_CACH_EN_MASK (1 << FLEXSPI_AHBCR_CACH_EN_SHIFT)
+#define FLEXSPI_AHBCR_CLRTXBUF_SHIFT 2
+#define FLEXSPI_AHBCR_CLRTXBUF_MASK (1 << FLEXSPI_AHBCR_CLRTXBUF_SHIFT)
+#define FLEXSPI_AHBCR_CLRRXBUF_SHIFT 1
+#define FLEXSPI_AHBCR_CLRRXBUF_MASK (1 << FLEXSPI_AHBCR_CLRRXBUF_SHIFT)
+#define FLEXSPI_AHBCR_PAR_EN_SHIFT 0
+#define FLEXSPI_AHBCR_PAR_EN_MASK (1 << FLEXSPI_AHBCR_PAR_EN_SHIFT)
+
+#define FLEXSPI_INTEN 0x10
+#define FLEXSPI_INTEN_SCLKSBWR_SHIFT 9
+#define FLEXSPI_INTEN_SCLKSBWR_MASK (1 << FLEXSPI_INTEN_SCLKSBWR_SHIFT)
+#define FLEXSPI_INTEN_SCLKSBRD_SHIFT 8
+#define FLEXSPI_INTEN_SCLKSBRD_MASK (1 << FLEXSPI_INTEN_SCLKSBRD_SHIFT)
+#define FLEXSPI_INTEN_DATALRNFL_SHIFT 7
+#define FLEXSPI_INTEN_DATALRNFL_MASK (1 << FLEXSPI_INTEN_DATALRNFL_SHIFT)
+#define FLEXSPI_INTEN_IPTXWE_SHIFT 6
+#define FLEXSPI_INTEN_IPTXWE_MASK (1 << FLEXSPI_INTEN_IPTXWE_SHIFT)
+#define FLEXSPI_INTEN_IPRXWA_SHIFT 5
+#define FLEXSPI_INTEN_IPRXWA_MASK (1 << FLEXSPI_INTEN_IPRXWA_SHIFT)
+#define FLEXSPI_INTEN_AHBCMDERR_SHIFT 4
+#define FLEXSPI_INTEN_AHBCMDERR_MASK (1 << FLEXSPI_INTEN_AHBCMDERR_SHIFT)
+#define FLEXSPI_INTEN_IPCMDERR_SHIFT 3
+#define FLEXSPI_INTEN_IPCMDERR_MASK (1 << FLEXSPI_INTEN_IPCMDERR_SHIFT)
+#define FLEXSPI_INTEN_AHBCMDGE_SHIFT 2
+#define FLEXSPI_INTEN_AHBCMDGE_MASK (1 << FLEXSPI_INTEN_AHBCMDGE_SHIFT)
+#define FLEXSPI_INTEN_IPCMDGE_SHIFT 1
+#define FLEXSPI_INTEN_IPCMDGE_MASK (1 << FLEXSPI_INTEN_IPCMDGE_SHIFT)
+#define FLEXSPI_INTEN_IPCMDDONE_SHIFT 0
+#define FLEXSPI_INTEN_IPCMDDONE_MASK (1 << FLEXSPI_INTEN_IPCMDDONE_SHIFT)
+
+#define FLEXSPI_INTR 0x14
+#define FLEXSPI_INTR_SCLKSBWR_SHIFT 9
+#define FLEXSPI_INTR_SCLKSBWR_MASK (1 << FLEXSPI_INTR_SCLKSBWR_SHIFT)
+#define FLEXSPI_INTR_SCLKSBRD_SHIFT 8
+#define FLEXSPI_INTR_SCLKSBRD_MASK (1 << FLEXSPI_INTR_SCLKSBRD_SHIFT)
+#define FLEXSPI_INTR_DATALRNFL_SHIFT 7
+#define FLEXSPI_INTR_DATALRNFL_MASK (1 << FLEXSPI_INTR_DATALRNFL_SHIFT)
+#define FLEXSPI_INTR_IPTXWE_SHIFT 6
+#define FLEXSPI_INTR_IPTXWE_MASK (1 << FLEXSPI_INTR_IPTXWE_SHIFT)
+#define FLEXSPI_INTR_IPRXWA_SHIFT 5
+#define FLEXSPI_INTR_IPRXWA_MASK (1 << FLEXSPI_INTR_IPRXWA_SHIFT)
+#define FLEXSPI_INTR_AHBCMDERR_SHIFT 4
+#define FLEXSPI_INTR_AHBCMDERR_MASK (1 << FLEXSPI_INTR_AHBCMDERR_SHIFT)
+#define FLEXSPI_INTR_IPCMDERR_SHIFT 3
+#define FLEXSPI_INTR_IPCMDERR_MASK (1 << FLEXSPI_INTR_IPCMDERR_SHIFT)
+#define FLEXSPI_INTR_AHBCMDGE_SHIFT 2
+#define FLEXSPI_INTR_AHBCMDGE_MASK (1 << FLEXSPI_INTR_AHBCMDGE_SHIFT)
+#define FLEXSPI_INTR_IPCMDGE_SHIFT 1
+#define FLEXSPI_INTR_IPCMDGE_MASK (1 << FLEXSPI_INTR_IPCMDGE_SHIFT)
+#define FLEXSPI_INTR_IPCMDDONE_SHIFT 0
+#define FLEXSPI_INTR_IPCMDDONE_MASK (1 << FLEXSPI_INTR_IPCMDDONE_SHIFT)
+
+#define FLEXSPI_LUTKEY 0x18
+#define FLEXSPI_LUTKEY_VALUE 0x5AF05AF0
+
+#define FLEXSPI_LCKCR 0x1C
+#define FLEXSPI_LCKER_LOCK 0x1
+#define FLEXSPI_LCKER_UNLOCK 0x2
+
+#define FLEXSPI_BUFXCR_INVALID_MSTRID 0xe
+#define FLEXSPI_AHBRX_BUF0CR0 0x20
+#define FLEXSPI_AHBRX_BUF1CR0 0x24
+#define FLEXSPI_AHBRX_BUF2CR0 0x28
+#define FLEXSPI_AHBRX_BUF3CR0 0x2C
+#define FLEXSPI_AHBRX_BUF4CR0 0x30
+#define FLEXSPI_AHBRX_BUF5CR0 0x34
+#define FLEXSPI_AHBRX_BUF6CR0 0x38
+#define FLEXSPI_AHBRX_BUF7CR0 0x3C
+#define FLEXSPI_AHBRXBUF0CR7_PREF_SHIFT 31
+#define FLEXSPI_AHBRXBUF0CR7_PREF_MASK (1 << FLEXSPI_AHBRXBUF0CR7_PREF_SHIFT)
+
+#define FLEXSPI_AHBRX_BUF0CR1 0x40
+#define FLEXSPI_AHBRX_BUF1CR1 0x44
+#define FLEXSPI_AHBRX_BUF2CR1 0x48
+#define FLEXSPI_AHBRX_BUF3CR1 0x4C
+#define FLEXSPI_AHBRX_BUF4CR1 0x50
+#define FLEXSPI_AHBRX_BUF5CR1 0x54
+#define FLEXSPI_AHBRX_BUF6CR1 0x58
+#define FLEXSPI_AHBRX_BUF7CR1 0x5C
+#define FLEXSPI_BUFXCR1_MSID_SHIFT 0
+#define FLEXSPI_BUFXCR1_MSID_MASK (0xF << FLEXSPI_BUFXCR1_MSID_SHIFT)
+#define FLEXSPI_BUFXCR1_PRIO_SHIFT 8
+#define FLEXSPI_BUFXCR1_PRIO_MASK (0x7 << FLEXSPI_BUFXCR1_PRIO_SHIFT)
+
+#define FLEXSPI_FLSHA1CR0 0x60
+#define FLEXSPI_FLSHA2CR0 0x64
+#define FLEXSPI_FLSHB1CR0 0x68
+#define FLEXSPI_FLSHB2CR0 0x6C
+#define FLEXSPI_FLSHXCR0_SZ_SHIFT 10
+#define FLEXSPI_FLSHXCR0_SZ_MASK (0x3FFFFF << FLEXSPI_FLSHXCR0_SZ_SHIFT)
+
+#define FLEXSPI_FLSHA1CR1 0x70
+#define FLEXSPI_FLSHA2CR1 0x74
+#define FLEXSPI_FLSHB1CR1 0x78
+#define FLEXSPI_FLSHB2CR1 0x7C
+#define FLEXSPI_FLSHXCR1_CSINTR_SHIFT 16
+#define FLEXSPI_FLSHXCR1_CSINTR_MASK \
+ (0xFFFF << FLEXSPI_FLSHXCR1_CSINTR_SHIFT)
+#define FLEXSPI_FLSHXCR1_CAS_SHIFT 11
+#define FLEXSPI_FLSHXCR1_CAS_MASK (0xF << FLEXSPI_FLSHXCR1_CAS_SHIFT)
+#define FLEXSPI_FLSHXCR1_WA_SHIFT 10
+#define FLEXSPI_FLSHXCR1_WA_MASK (1 << FLEXSPI_FLSHXCR1_WA_SHIFT)
+#define FLEXSPI_FLSHXCR1_TCSH_SHIFT 5
+#define FLEXSPI_FLSHXCR1_TCSH_MASK (0x1F << FLEXSPI_FLSHXCR1_TCSH_SHIFT)
+#define FLEXSPI_FLSHXCR1_TCSS_SHIFT 0
+#define FLEXSPI_FLSHXCR1_TCSS_MASK (0x1F << FLEXSPI_FLSHXCR1_TCSS_SHIFT)
+
+#define FLEXSPI_FLSHA1CR2 0x80
+#define FLEXSPI_FLSHA2CR2 0x84
+#define FLEXSPI_FLSHB1CR2 0x88
+#define FLEXSPI_FLSHB2CR2 0x8C
+#define FLEXSPI_FLSHXCR2_CLRINSP_SHIFT 24
+#define FLEXSPI_FLSHXCR2_CLRINSP_MASK (1 << FLEXSPI_FLSHXCR2_CLRINSP_SHIFT)
+#define FLEXSPI_FLSHXCR2_AWRWAIT_SHIFT 16
+#define FLEXSPI_FLSHXCR2_AWRWAIT_MASK (0xFF << FLEXSPI_FLSHXCR2_AWRWAIT_SHIFT)
+#define FLEXSPI_FLSHXCR2_AWRSEQN_SHIFT 13
+#define FLEXSPI_FLSHXCR2_AWRSEQN_MASK (0x7 << FLEXSPI_FLSHXCR2_AWRSEQN_SHIFT)
+#define FLEXSPI_FLSHXCR2_AWRSEQI_SHIFT 8
+#define FLEXSPI_FLSHXCR2_AWRSEQI_MASK (0xF << FLEXSPI_FLSHXCR2_AWRSEQI_SHIFT)
+#define FLEXSPI_FLSHXCR2_ARDSEQN_SHIFT 5
+#define FLEXSPI_FLSHXCR2_ARDSEQN_MASK (0x7 << FLEXSPI_FLSHXCR2_ARDSEQN_SHIFT)
+#define FLEXSPI_FLSHXCR2_ARDSEQI_SHIFT 0
+#define FLEXSPI_FLSHXCR2_ARDSEQI_MASK (0xF << FLEXSPI_FLSHXCR2_ARDSEQI_SHIFT)
+
+#define FLEXSPI_IPCR0 0xA0
+
+#define FLEXSPI_IPCR1 0xA4
+#define FLEXSPI_IPCR1_IPAREN_SHIFT 31
+#define FLEXSPI_IPCR1_IPAREN_MASK (1 << FLEXSPI_IPCR1_IPAREN_SHIFT)
+#define FLEXSPI_IPCR1_SEQNUM_SHIFT 24
+#define FLEXSPI_IPCR1_SEQNUM_MASK (0xF << FLEXSPI_IPCR1_SEQNUM_SHIFT)
+#define FLEXSPI_IPCR1_SEQID_SHIFT 16
+#define FLEXSPI_IPCR1_SEQID_MASK (0xF << FLEXSPI_IPCR1_SEQID_SHIFT)
+#define FLEXSPI_IPCR1_IDATSZ_SHIFT 0
+#define FLEXSPI_IPCR1_IDATSZ_MASK (0xFFFF << FLEXSPI_IPCR1_IDATSZ_SHIFT)
+
+#define FLEXSPI_IPCMD 0xB0
+#define FLEXSPI_IPCMD_TRG_SHIFT 0
+#define FLEXSPI_IPCMD_TRG_MASK (1 << FLEXSPI_IPCMD_TRG_SHIFT)
+
+#define FLEXSPI_DLPR 0xB4
+
+#define FLEXSPI_IPRXFCR 0xB8
+#define FLEXSPI_IPRXFCR_CLR_SHIFT 0
+#define FLEXSPI_IPRXFCR_CLR_MASK (1 << FLEXSPI_IPRXFCR_CLR_SHIFT)
+#define FLEXSPI_IPRXFCR_DMA_EN_SHIFT 1
+#define FLEXSPI_IPRXFCR_DMA_EN_MASK (1 << FLEXSPI_IPRXFCR_DMA_EN_SHIFT)
+#define FLEXSPI_IPRXFCR_WMRK_SHIFT 2
+#define FLEXSPI_IPRXFCR_WMRK_MASK (0x1F << FLEXSPI_IPRXFCR_WMRK_SHIFT)
+
+#define FLEXSPI_IPTXFCR 0xBC
+#define FLEXSPI_IPTXFCR_CLR_SHIFT 0
+#define FLEXSPI_IPTXFCR_CLR_MASK (1 << FLEXSPI_IPTXFCR_CLR_SHIFT)
+#define FLEXSPI_IPTXFCR_DMA_EN_SHIFT 1
+#define FLEXSPI_IPTXFCR_DMA_EN_MASK (1 << FLEXSPI_IPTXFCR_DMA_EN_SHIFT)
+#define FLEXSPI_IPTXFCR_WMRK_SHIFT 2
+#define FLEXSPI_IPTXFCR_WMRK_MASK (0x1F << FLEXSPI_IPTXFCR_WMRK_SHIFT)
+
+#define FLEXSPI_STS0 0xE0
+#define FLEXSPI_STS0_DLPHA_SHIFT 9
+#define FLEXSPI_STS0_DLPHA_MASK (0x1F << FLEXSPI_STS0_DLPHA_SHIFT)
+#define FLEXSPI_STS0_DLPHB_SHIFT 4
+#define FLEXSPI_STS0_DLPHB_MASK (0x1F << FLEXSPI_STS0_DLPHB_SHIFT)
+#define FLEXSPI_STS0_CMD_SRC_SHIFT 2
+#define FLEXSPI_STS0_CMD_SRC_MASK (3 << FLEXSPI_STS0_CMD_SRC_SHIFT)
+#define FLEXSPI_STS0_ARB_IDLE_SHIFT 1
+#define FLEXSPI_STS0_ARB_IDLE_MASK (1 << FLEXSPI_STS0_ARB_IDLE_SHIFT)
+#define FLEXSPI_STS0_SEQ_IDLE_SHIFT 0
+#define FLEXSPI_STS0_SEQ_IDLE_MASK (1 << FLEXSPI_STS0_SEQ_IDLE_SHIFT)
+
+#define FLEXSPI_STS1 0xE4
+#define FLEXSPI_STS1_IP_ERRCD_SHIFT 24
+#define FLEXSPI_STS1_IP_ERRCD_MASK (0xF << FLEXSPI_STS1_IP_ERRCD_SHIFT)
+#define FLEXSPI_STS1_IP_ERRID_SHIFT 16
+#define FLEXSPI_STS1_IP_ERRID_MASK (0xF << FLEXSPI_STS1_IP_ERRID_SHIFT)
+#define FLEXSPI_STS1_AHB_ERRCD_SHIFT 8
+#define FLEXSPI_STS1_AHB_ERRCD_MASK (0xF << FLEXSPI_STS1_AHB_ERRCD_SHIFT)
+#define FLEXSPI_STS1_AHB_ERRID_SHIFT 0
+#define FLEXSPI_STS1_AHB_ERRID_MASK (0xF << FLEXSPI_STS1_AHB_ERRID_SHIFT)
+
+#define FLEXSPI_AHBSPNST 0xEC
+#define FLEXSPI_AHBSPNST_DATLFT_SHIFT 16
+#define FLEXSPI_AHBSPNST_DATLFT_MASK \
+ (0xFFFF << FLEXSPI_AHBSPNST_DATLFT_SHIFT)
+#define FLEXSPI_AHBSPNST_BUFID_SHIFT 1
+#define FLEXSPI_AHBSPNST_BUFID_MASK (7 << FLEXSPI_AHBSPNST_BUFID_SHIFT)
+#define FLEXSPI_AHBSPNST_ACTIVE_SHIFT 0
+#define FLEXSPI_AHBSPNST_ACTIVE_MASK (1 << FLEXSPI_AHBSPNST_ACTIVE_SHIFT)
+
+#define FLEXSPI_IPRXFSTS 0xF0
+#define FLEXSPI_IPRXFSTS_RDCNTR_SHIFT 16
+#define FLEXSPI_IPRXFSTS_RDCNTR_MASK \
+ (0xFFFF << FLEXSPI_IPRXFSTS_RDCNTR_SHIFT)
+#define FLEXSPI_IPRXFSTS_FILL_SHIFT 0
+#define FLEXSPI_IPRXFSTS_FILL_MASK (0xFF << FLEXSPI_IPRXFSTS_FILL_SHIFT)
+
+#define FLEXSPI_IPTXFSTS 0xF4
+#define FLEXSPI_IPTXFSTS_WRCNTR_SHIFT 16
+#define FLEXSPI_IPTXFSTS_WRCNTR_MASK \
+ (0xFFFF << FLEXSPI_IPTXFSTS_WRCNTR_SHIFT)
+#define FLEXSPI_IPTXFSTS_FILL_SHIFT 0
+#define FLEXSPI_IPTXFSTS_FILL_MASK (0xFF << FLEXSPI_IPTXFSTS_FILL_SHIFT)
+
+#define FLEXSPI_RFDR 0x100
+#define FLEXSPI_TFDR 0x180
+
+#define FLEXSPI_LUT_BASE 0x200
+
+/* register map end */
+
+/*
+ * The definition of the LUT register shows below:
+ *
+ * ---------------------------------------------------
+ * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
+ * ---------------------------------------------------
+ */
+#define OPRND0_SHIFT 0
+#define PAD0_SHIFT 8
+#define INSTR0_SHIFT 10
+#define OPRND1_SHIFT 16
+
+/* Instruction set for the LUT register. */
+
+#define LUT_STOP 0x00
+#define LUT_CMD 0x01
+#define LUT_ADDR 0x02
+#define LUT_CADDR_SDR 0x03
+#define LUT_MODE 0x04
+#define LUT_MODE2 0x05
+#define LUT_MODE4 0x06
+#define LUT_MODE8 0x07
+#define LUT_FSL_WRITE 0x08
+#define LUT_FSL_READ 0x09
+#define LUT_LEARN_SDR 0x0A
+#define LUT_DATSZ_SDR 0x0B
+#define LUT_DUMMY 0x0C
+#define LUT_DUMMY_RWDS_SDR 0x0D
+#define LUT_JMP_ON_CS 0x1F
+#define LUT_CMD_DDR 0x21
+#define LUT_ADDR_DDR 0x22
+#define LUT_CADDR_DDR 0x23
+#define LUT_MODE_DDR 0x24
+#define LUT_MODE2_DDR 0x25
+#define LUT_MODE4_DDR 0x26
+#define LUT_MODE8_DDR 0x27
+#define LUT_WRITE_DDR 0x28
+#define LUT_READ_DDR 0x29
+#define LUT_LEARN_DDR 0x2A
+#define LUT_DATSZ_DDR 0x2B
+#define LUT_DUMMY_DDR 0x2C
+#define LUT_DUMMY_RWDS_DDR 0x2D
+
+
+/*
+ * The PAD definitions for LUT register.
+ *
+ * The pad stands for the lines number of IO[0:3].
+ * For example, the Quad read need four IO lines, so you should
+ * set LUT_PAD4 which means we use four IO lines.
+ */
+#define LUT_PAD1 0
+#define LUT_PAD2 1
+#define LUT_PAD4 2
+#define LUT_PAD8 3
+
+/* Oprands for the LUT register. */
+#define ADDR24BIT 0x18
+#define ADDR32BIT 0x20
+
+/* Macros for constructing the LUT register. */
+#define LUT0(ins, pad, opr) \
+ (((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \
+ ((LUT_##ins) << INSTR0_SHIFT))
+
+#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr) << OPRND1_SHIFT)
+
+/* other macros for LUT register. */
+#define FLEXSPI_LUT(x) (FLEXSPI_LUT_BASE + (x) * 4)
+#define FLEXSPI_LUT_NUM 64
+
+/* SEQID -- we can have 16 seqids at most. */
+#define SEQID_QUAD_READ 0
+#define SEQID_WREN 1
+#define SEQID_WRDI 2
+#define SEQID_RDSR 3
+#define SEQID_SE 4
+#define SEQID_CHIP_ERASE 5
+#define SEQID_PP 6
+#define SEQID_RDID 7
+#define SEQID_WRSR 8
+#define SEQID_RDCR 9
+#define SEQID_EN4B 10
+#define SEQID_BRWR 11
+#define SEQID_RD_EVCR 12
+#define SEQID_WD_EVCR 13
+
+#define FLEXSPI_MIN_IOMAP SZ_4M
+
+enum fsl_flexspi_devtype {
+ FSL_FLEXSPI_IMX8QM,
+ FSL_FLEXSPI_IMX8QXP,
+ FSL_FLEXSPI_IMX8MM,
+};
+
+struct fsl_flexspi_devtype_data {
+ enum fsl_flexspi_devtype devtype;
+ int rxfifo;
+ int txfifo;
+ int ahb_buf_size;
+ int driver_data;
+};
+
+static struct fsl_flexspi_devtype_data imx8qm_data = {
+ .devtype = FSL_FLEXSPI_IMX8QM,
+ .rxfifo = 1024,
+ .txfifo = 1024,
+ .ahb_buf_size = 2048,
+ .driver_data = 0,
+};
+
+static struct fsl_flexspi_devtype_data imx8qxp_data = {
+ .devtype = FSL_FLEXSPI_IMX8QXP,
+ .rxfifo = 1024,
+ .txfifo = 1024,
+ .ahb_buf_size = 2048,
+ .driver_data = 0,
+};
+
+static struct fsl_flexspi_devtype_data imx8mm_data = {
+ .devtype = FSL_FLEXSPI_IMX8MM,
+ .rxfifo = 1024,
+ .txfifo = 1024,
+ .ahb_buf_size = 2048,
+ .driver_data = FLEXSPI_QUIRK_QUAD_ONLY,
+};
+
+#define FSL_FLEXSPI_MAX_CHIP 4
+struct fsl_flexspi {
+ struct mtd_info mtd[FSL_FLEXSPI_MAX_CHIP];
+ struct spi_nor nor[FSL_FLEXSPI_MAX_CHIP];
+ void __iomem *iobase;
+ void __iomem *ahb_addr;
+ u32 memmap_phy;
+ u32 memmap_offs;
+ u32 memmap_len;
+ struct clk *clk;
+ struct device *dev;
+ struct completion c;
+ struct fsl_flexspi_devtype_data *devtype_data;
+ u32 nor_size;
+ u32 nor_num;
+ u32 clk_rate;
+ unsigned int chip_base_addr; /* We may support two chips. */
+ bool has_second_chip;
+ u32 ddr_smp;
+ struct mutex lock;
+ struct pm_qos_request pm_qos_req;
+
+#define FLEXSPI_INITILIZED (1 << 0)
+ int flags;
+};
+
+static inline int fsl_flexspi_quad_only(struct fsl_flexspi *flex)
+{
+ return flex->devtype_data->driver_data & FLEXSPI_QUIRK_QUAD_ONLY;
+}
+
+static inline void fsl_flexspi_unlock_lut(struct fsl_flexspi *flex)
+{
+ writel(FLEXSPI_LUTKEY_VALUE, flex->iobase + FLEXSPI_LUTKEY);
+ writel(FLEXSPI_LCKER_UNLOCK, flex->iobase + FLEXSPI_LCKCR);
+}
+
+static inline void fsl_flexspi_lock_lut(struct fsl_flexspi *flex)
+{
+ writel(FLEXSPI_LUTKEY_VALUE, flex->iobase + FLEXSPI_LUTKEY);
+ writel(FLEXSPI_LCKER_LOCK, flex->iobase + FLEXSPI_LCKCR);
+}
+
+static irqreturn_t fsl_flexspi_irq_handler(int irq, void *dev_id)
+{
+ struct fsl_flexspi *flex = dev_id;
+ u32 reg;
+
+ reg = readl(flex->iobase + FLEXSPI_INTR);
+ writel(FLEXSPI_INTR_IPCMDDONE_MASK, flex->iobase + FLEXSPI_INTR);
+ if (reg & FLEXSPI_INTR_IPCMDDONE_MASK)
+ complete(&flex->c);
+
+ return IRQ_HANDLED;
+}
+
+static void fsl_flexspi_init_lut(struct fsl_flexspi *flex)
+{
+ void __iomem *base = flex->iobase;
+ struct spi_nor *nor = &flex->nor[0];
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
+ u32 lut_base;
+ u8 op, dm;
+ int i;
+
+ fsl_flexspi_unlock_lut(flex);
+
+ /* Clear all the LUT table */
+ for (i = 0; i < FLEXSPI_LUT_NUM; i++)
+ writel(0, base + FLEXSPI_LUT_BASE + i * 4);
+
+ /* Quad Read and DDR Quad Read*/
+ lut_base = SEQID_QUAD_READ * 4;
+ op = nor->read_opcode;
+ dm = nor->read_dummy;
+
+ /* Normal read */
+ if (op == SPINOR_OP_READ) {
+ writel(LUT0(CMD, PAD1, op) |
+ LUT1(ADDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ writel(LUT0(FSL_READ, PAD1, 0),
+ base + FLEXSPI_LUT(lut_base + 1));
+ }
+
+ /* Octal DDR read */
+ if (op == SPINOR_OP_READ_1_1_8_D) {
+ writel(LUT0(CMD, PAD1, op) |
+ LUT1(ADDR_DDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ writel(LUT0(DUMMY_DDR, PAD8, dm * 2)
+ | LUT1(READ_DDR, PAD8, 0),
+ base + FLEXSPI_LUT(lut_base + 1));
+
+ }
+
+ if (nor->flash_read == SPI_NOR_QUAD) {
+ if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) {
+ /* read mode : 1-1-4 */
+ writel(LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ writel(LUT0(DUMMY, PAD1, dm) |
+ LUT1(FSL_READ, PAD4, 0),
+ base + FLEXSPI_LUT(lut_base + 1));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ } else if (nor->flash_read == SPI_NOR_DDR_QUAD) {
+ if (op == SPINOR_OP_READ_1_4_4_D ||
+ op == SPINOR_OP_READ4_1_4_4_D) {
+ /* read mode : 1-4-4, such as Spansion s25fl128s. */
+ writel(LUT0(CMD_DDR, PAD1, op)
+ | LUT1(ADDR_DDR, PAD4, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ writel(LUT0(MODE_DDR, PAD4, 0xff)
+ | LUT1(DUMMY, PAD1, dm),
+ base + FLEXSPI_LUT(lut_base + 1));
+
+ writel(LUT0(READ_DDR, PAD4, 0)
+ | LUT1(JMP_ON_CS, PAD1, 0),
+ base + FLEXSPI_LUT(lut_base + 2));
+ } else if (op == SPINOR_OP_READ_1_1_4_D) {
+ /* read mode : 1-1-4, such as Micron N25Q256A. */
+ writel(LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ writel(LUT0(DUMMY_DDR, PAD4, 2 * dm)
+ | LUT1(READ_DDR, PAD4, 0),
+ base + FLEXSPI_LUT(lut_base + 1));
+
+ writel(LUT0(JMP_ON_CS, PAD1, 0),
+ base + FLEXSPI_LUT(lut_base + 2));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ }
+
+ /* Write enable */
+ lut_base = SEQID_WREN * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + FLEXSPI_LUT(lut_base));
+
+ /* Page Program */
+ lut_base = SEQID_PP * 4;
+ writel(LUT0(CMD, PAD1, nor->program_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+ writel(LUT0(FSL_WRITE, PAD1, 0), base + FLEXSPI_LUT(lut_base + 1));
+
+ /* Read Status */
+ lut_base = SEQID_RDSR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RDSR) | LUT1(FSL_READ, PAD1, 0x1),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Erase a sector */
+ lut_base = SEQID_SE * 4;
+ writel(LUT0(CMD, PAD1, nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Erase the whole chip */
+ lut_base = SEQID_CHIP_ERASE * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* READ ID */
+ lut_base = SEQID_RDID * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RDID) | LUT1(FSL_READ, PAD1, 0x8),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Write Register */
+ lut_base = SEQID_WRSR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WRSR) | LUT1(FSL_WRITE, PAD1, 0x2),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Read Configuration Register */
+ lut_base = SEQID_RDCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RDCR) | LUT1(FSL_READ, PAD1, 0x1),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Write disable */
+ lut_base = SEQID_WRDI * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WRDI), base + FLEXSPI_LUT(lut_base));
+
+ /* Enter 4 Byte Mode (Micron) */
+ lut_base = SEQID_EN4B * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_EN4B), base + FLEXSPI_LUT(lut_base));
+
+ /* Enter 4 Byte Mode (Spansion) */
+ lut_base = SEQID_BRWR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_BRWR), base + FLEXSPI_LUT(lut_base));
+
+ /* Read EVCR register */
+ lut_base = SEQID_RD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR),
+ base + FLEXSPI_LUT(lut_base));
+
+ /* Write EVCR register */
+ lut_base = SEQID_WD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR),
+ base + FLEXSPI_LUT(lut_base));
+ fsl_flexspi_lock_lut(flex);
+}
+
+/* Get the SEQID for the command */
+static int fsl_flexspi_get_seqid(struct fsl_flexspi *flex, u8 cmd)
+{
+
+ switch (cmd) {
+ case SPINOR_OP_READ_1_1_4_D:
+ case SPINOR_OP_READ_1_1_8_D:
+ case SPINOR_OP_READ_1_4_4_D:
+ case SPINOR_OP_READ4_1_4_4_D:
+ case SPINOR_OP_READ4_1_1_4:
+ case SPINOR_OP_READ_1_1_4:
+ case SPINOR_OP_READ4:
+ case SPINOR_OP_READ:
+ return SEQID_QUAD_READ;
+ case SPINOR_OP_WREN:
+ return SEQID_WREN;
+ case SPINOR_OP_WRDI:
+ return SEQID_WRDI;
+ case SPINOR_OP_RDSR:
+ return SEQID_RDSR;
+ case SPINOR_OP_BE_4K:
+ case SPINOR_OP_SE:
+ return SEQID_SE;
+ case SPINOR_OP_CHIP_ERASE:
+ return SEQID_CHIP_ERASE;
+ case SPINOR_OP_PP:
+ return SEQID_PP;
+ case SPINOR_OP_RDID:
+ return SEQID_RDID;
+ case SPINOR_OP_WRSR:
+ return SEQID_WRSR;
+ case SPINOR_OP_RDCR:
+ return SEQID_RDCR;
+ case SPINOR_OP_EN4B:
+ return SEQID_EN4B;
+ case SPINOR_OP_BRWR:
+ return SEQID_BRWR;
+ case SPINOR_OP_RD_EVCR:
+ return SEQID_RD_EVCR;
+ case SPINOR_OP_WD_EVCR:
+ return SEQID_WD_EVCR;
+ default:
+ dev_err(flex->dev, "Unsupported cmd 0x%.2x\n", cmd);
+ break;
+ }
+ return -EINVAL;
+}
+
+static int
+fsl_flexspi_runcmd(struct fsl_flexspi *flex, u8 cmd, unsigned int addr, int len)
+{
+ void __iomem *base = flex->iobase;
+ int seqid;
+ int seqnum = 0;
+ u32 reg;
+ int err;
+
+ init_completion(&flex->c);
+ dev_dbg(flex->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n",
+ flex->chip_base_addr, addr, len, cmd);
+
+ /* write address */
+ writel(flex->chip_base_addr + addr, base + FLEXSPI_IPCR0);
+
+ seqid = fsl_flexspi_get_seqid(flex, cmd);
+
+ writel((seqnum << FLEXSPI_IPCR1_SEQNUM_SHIFT) |
+ (seqid << FLEXSPI_IPCR1_SEQID_SHIFT) | len,
+ base + FLEXSPI_IPCR1);
+
+ /* wait till controller is idle */
+ do {
+ reg = readl(base + FLEXSPI_STS0);
+ if ((reg & FLEXSPI_STS0_ARB_IDLE_MASK) &&
+ (reg & FLEXSPI_STS0_SEQ_IDLE_MASK))
+ break;
+ udelay(1);
+ } while (1);
+
+ /* trigger the LUT now */
+ writel(1, base + FLEXSPI_IPCMD);
+
+ /* Wait for the interrupt. */
+ if (!wait_for_completion_timeout(&flex->c, msecs_to_jiffies(1000))) {
+ dev_dbg(flex->dev,
+ "cmd 0x%.2x timeout, addr@%.8x, Status0:0x%.8x, Status1:0x%.8x\n",
+ cmd, addr, readl(base + FLEXSPI_STS0),
+ readl(base + FLEXSPI_STS1));
+ err = -ETIMEDOUT;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Read out the data from the FLEXSPI_RBDR buffer registers. */
+static void fsl_flexspi_read_data(struct fsl_flexspi *flex, int len, u8 *rxbuf)
+{
+ /* u64 tmp; */
+ int i = 0;
+ int size;
+
+ /* invalid RXFIFO first */
+ writel(FLEXSPI_IPRXFCR_CLR_MASK, flex->iobase + FLEXSPI_IPRXFCR);
+ while (len > 0) {
+
+ size = len / 8;
+
+ for (i = 0; i < size; ++i) {
+ /* Wait for RXFIFO available*/
+ while (!(readl(flex->iobase + FLEXSPI_INTR)
+ & FLEXSPI_INTR_IPRXWA_MASK))
+ ;
+
+ /* read 64 bit data once */
+ memcpy(rxbuf, flex->iobase + FLEXSPI_RFDR, 8);
+ rxbuf += 8;
+
+ /* move the FIFO pointer */
+ writel(FLEXSPI_INTR_IPRXWA_MASK,
+ flex->iobase + FLEXSPI_INTR);
+ len -= 8;
+ }
+
+ size = len % 8;
+
+ if (size) {
+ /* Wait for RXFIFO available*/
+ while (!(readl(flex->iobase + FLEXSPI_INTR)
+ & FLEXSPI_INTR_IPRXWA_MASK))
+ ;
+
+ memcpy(rxbuf, flex->iobase + FLEXSPI_RFDR, size);
+ len -= size;
+ }
+
+ writel(FLEXSPI_INTR_IPRXWA_MASK,
+ flex->iobase + FLEXSPI_INTR);
+
+ /* invalid the RXFIFO */
+ writel(FLEXSPI_IPRXFCR_CLR_MASK,
+ flex->iobase + FLEXSPI_IPRXFCR);
+ }
+}
+
+/*
+ * If we have changed the content of the flash by writing or erasing,
+ * we need to invalidate the AHB buffer. If we do not do so, we may read out
+ * the wrong data. The spec tells us reset the AHB domain and Serial Flash
+ * domain at the same time.
+ */
+static inline void fsl_flexspi_invalid(struct fsl_flexspi *flex)
+{
+ u32 reg;
+
+ reg = readl(flex->iobase + FLEXSPI_MCR0);
+ writel(reg | FLEXSPI_MCR0_SWRST_MASK, flex->iobase + FLEXSPI_MCR0);
+
+ /*
+ * The minimum delay : 1 AHB + 2 SFCK clocks.
+ * Delay 1 us is enough.
+ */
+ while (readl(flex->iobase + FLEXSPI_MCR0) & FLEXSPI_MCR0_SWRST_MASK)
+ ;
+
+}
+
+static ssize_t fsl_flexspi_nor_write(struct fsl_flexspi *flex,
+ struct spi_nor *nor, u8 opcode,
+ unsigned int to, u32 *txbuf,
+ unsigned int count)
+{
+ int ret, i;
+ int size;
+
+ dev_dbg(flex->dev, "nor write to 0x%.8x:0x%.8x, len : %d\n",
+ flex->chip_base_addr, to, count);
+
+ /* clear the TX FIFO. */
+ writel(FLEXSPI_IPTXFCR_CLR_MASK, flex->iobase + FLEXSPI_IPTXFCR);
+
+ size = count / 8;
+ for (i = 0; i < size; i++) {
+ /* Wait for TXFIFO empty*/
+ while (!(readl(flex->iobase + FLEXSPI_INTR)
+ & FLEXSPI_INTR_IPTXWE_MASK))
+ ;
+
+ memcpy(flex->iobase + FLEXSPI_TFDR, txbuf, 8);
+ txbuf += 2;
+ writel(FLEXSPI_INTR_IPTXWE_MASK, flex->iobase + FLEXSPI_INTR);
+ }
+
+ size = count % 8;
+ if (size) {
+ /* Wait for TXFIFO empty*/
+ while (!(readl(flex->iobase + FLEXSPI_INTR)
+ & FLEXSPI_INTR_IPTXWE_MASK))
+ ;
+
+ memcpy(flex->iobase + FLEXSPI_TFDR, txbuf, size);
+ writel(FLEXSPI_INTR_IPTXWE_MASK, flex->iobase + FLEXSPI_INTR);
+ }
+
+ /* Trigger it */
+ ret = fsl_flexspi_runcmd(flex, opcode, to, count);
+
+ if (ret == 0)
+ return count;
+
+ return ret;
+}
+
+static void fsl_flexspi_set_map_addr(struct fsl_flexspi *flex)
+{
+ int nor_size = flex->nor_size >> 10;
+ void __iomem *base = flex->iobase;
+
+ writel(nor_size, base + FLEXSPI_FLSHA1CR0);
+ writel(nor_size * 2, base + FLEXSPI_FLSHA2CR0);
+ writel(nor_size * 3, base + FLEXSPI_FLSHB1CR0);
+ writel(nor_size * 4, base + FLEXSPI_FLSHB2CR0);
+}
+
+/*
+ * There are two different ways to read out the data from the flash:
+ * the "IP Command Read" and the "AHB Command Read".
+ *
+ * The IC guy suggests we use the "AHB Command Read" which is faster
+ * then the "IP Command Read". (What's more is that there is a bug in
+ * the "IP Command Read" in the Vybrid.)
+ *
+ * After we set up the registers for the "AHB Command Read", we can use
+ * the memcpy to read the data directly. A "missed" access to the buffer
+ * causes the controller to clear the buffer, and use the sequence pointed
+ * by the FLEXSPI_BFGENCR[SEQID] to initiate a read from the flash.
+ */
+static void fsl_flexspi_init_ahb_read(struct fsl_flexspi *flex)
+{
+ void __iomem *base = flex->iobase;
+ struct spi_nor *nor = &flex->nor[0];
+ /* u32 reg, reg2; */
+ int seqid;
+ int i;
+
+ /* AHB configuration for access buffer 0/1/2 .*/
+ for (i = 0; i < 7; i++)
+ writel(0, base + FLEXSPI_AHBRX_BUF0CR0 + 4 * i);
+ /*
+ * Set ADATSZ with the maximum AHB buffer size to improve the
+ * read performance.
+ */
+ writel((flex->devtype_data->ahb_buf_size / 8 |
+ FLEXSPI_AHBRXBUF0CR7_PREF_MASK),
+ base + FLEXSPI_AHBRX_BUF7CR0);
+
+ /* prefetch and no start address alignment limitation */
+ writel(FLEXSPI_AHBCR_PREF_EN_MASK | FLEXSPI_AHBCR_RDADDROPT_MASK,
+ base + FLEXSPI_AHBCR);
+
+ /* Set the default lut sequence for AHB Read. */
+ seqid = fsl_flexspi_get_seqid(flex, nor->read_opcode);
+ writel(seqid, flex->iobase + FLEXSPI_FLSHA1CR2);
+}
+
+/* This function was used to prepare and enable QSPI clock */
+static int fsl_flexspi_clk_prep_enable(struct fsl_flexspi *flex)
+{
+ int ret;
+
+ ret = clk_prepare_enable(flex->clk);
+ if (ret) {
+ dev_err(flex->dev, "failed to enable the clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* This function was used to disable and unprepare QSPI clock */
+static void fsl_flexspi_clk_disable_unprep(struct fsl_flexspi *flex)
+{
+ clk_disable_unprepare(flex->clk);
+}
+
+static int fsl_flexspi_init_rpm(struct fsl_flexspi *flex)
+{
+ struct device *dev = flex->dev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, FSL_FLEXSPI_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+
+ return 0;
+}
+
+/* We use this function to do some basic init for spi_nor_scan(). */
+static int fsl_flexspi_nor_setup(struct fsl_flexspi *flex)
+{
+ void __iomem *base = flex->iobase;
+ u32 reg;
+ int ret;
+
+ ret = pm_runtime_get_sync(flex->dev);
+ if (ret < 0) {
+ dev_err(flex->dev, "Failed to enable clock %d\n", __LINE__);
+ return ret;
+ }
+
+ /* Reset the module */
+ writel(FLEXSPI_MCR0_SWRST_MASK, base + FLEXSPI_MCR0);
+ do {
+ udelay(1);
+ } while (0x1 & readl(base + FLEXSPI_MCR0));
+
+ /* Disable the module */
+ writel(FLEXSPI_MCR0_MDIS_MASK, base + FLEXSPI_MCR0);
+
+ /* enable module */
+ writel(FLEXSPI_MCR0_AHB_TIMEOUT_MASK | FLEXSPI_MCR0_IP_TIMEOUT_MASK |
+ FLEXSPI_MCR0_OCTCOMB_EN_MASK, base + FLEXSPI_MCR0);
+
+ /* Read the register value */
+ reg = readl(base + FLEXSPI_MCR0);
+
+ /* Init the LUT table. */
+ fsl_flexspi_init_lut(flex);
+
+ /* enable the interrupt */
+ writel(FLEXSPI_INTEN_IPCMDDONE_MASK, flex->iobase + FLEXSPI_INTEN);
+
+ pm_runtime_mark_last_busy(flex->dev);
+ pm_runtime_put_autosuspend(flex->dev);
+
+ return 0;
+}
+
+static int fsl_flexspi_nor_setup_last(struct fsl_flexspi *flex)
+{
+ unsigned long rate = flex->clk_rate;
+ int ret;
+
+ ret = pm_runtime_get_sync(flex->dev);
+ if (ret < 0) {
+ dev_err(flex->dev, "Failed to enable clock %d\n", __LINE__);
+ return ret;
+ }
+
+ /* disable and unprepare clock to avoid glitch pass to controller */
+ fsl_flexspi_clk_disable_unprep(flex);
+
+ ret = clk_set_rate(flex->clk, rate);
+ if (ret)
+ return ret;
+
+ ret = fsl_flexspi_clk_prep_enable(flex);
+ if (ret)
+ return ret;
+
+ /* Init the LUT table again. */
+ fsl_flexspi_init_lut(flex);
+
+ /* Init for AHB read */
+ fsl_flexspi_init_ahb_read(flex);
+
+ pm_runtime_mark_last_busy(flex->dev);
+ pm_runtime_put_autosuspend(flex->dev);
+
+ return 0;
+}
+
+static void fsl_flexspi_set_base_addr(struct fsl_flexspi *flex,
+ struct spi_nor *nor)
+{
+ flex->chip_base_addr = flex->nor_size * (nor - flex->nor);
+}
+
+static int fsl_flexspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+ int len)
+{
+ int ret;
+ struct fsl_flexspi *flex = nor->priv;
+
+ ret = fsl_flexspi_runcmd(flex, opcode, 0, len);
+ if (ret)
+ return ret;
+
+ fsl_flexspi_read_data(flex, len, buf);
+ return 0;
+}
+
+static int fsl_flexspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
+ int len)
+{
+ struct fsl_flexspi *flex = nor->priv;
+ int ret;
+
+ if (!buf) {
+ ret = fsl_flexspi_runcmd(flex, opcode, 0, 1);
+ if (ret)
+ return ret;
+
+ if (opcode == SPINOR_OP_CHIP_ERASE)
+ fsl_flexspi_invalid(flex);
+
+ } else if (len > 0) {
+ ret = fsl_flexspi_nor_write(flex, nor, opcode, 0,
+ (u32 *)buf, len);
+ } else {
+ dev_err(flex->dev, "invalid cmd %d\n", opcode);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static ssize_t fsl_flexspi_write(struct spi_nor *nor, loff_t to,
+ size_t len, const u_char *buf)
+{
+ struct fsl_flexspi *flex = nor->priv;
+
+ ssize_t ret = fsl_flexspi_nor_write(flex, nor, nor->program_opcode, to,
+ (u32 *)buf, len);
+
+ /* invalid the data in the AHB buffer. */
+ fsl_flexspi_invalid(flex);
+ return ret;
+}
+
+static ssize_t fsl_flexspi_read(struct spi_nor *nor, loff_t from,
+ size_t len, u_char *buf)
+{
+ struct fsl_flexspi *flex = nor->priv;
+ int i, j;
+
+ /* if necessary,ioremap buffer before AHB read, */
+ if (!flex->ahb_addr) {
+ flex->memmap_offs = flex->chip_base_addr + from;
+ flex->memmap_len = len > FLEXSPI_MIN_IOMAP ?
+ len : FLEXSPI_MIN_IOMAP;
+
+ flex->ahb_addr = ioremap_nocache(
+ flex->memmap_phy + flex->memmap_offs,
+ flex->memmap_len);
+ if (!flex->ahb_addr) {
+ dev_err(flex->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+ /* ioremap if the data requested is out of range */
+ } else if (flex->chip_base_addr + from < flex->memmap_offs
+ || flex->chip_base_addr + from + len >
+ flex->memmap_offs + flex->memmap_len) {
+ iounmap(flex->ahb_addr);
+
+ flex->memmap_offs = flex->chip_base_addr + from;
+ flex->memmap_len = len > FLEXSPI_MIN_IOMAP ?
+ len : FLEXSPI_MIN_IOMAP;
+ flex->ahb_addr = ioremap_nocache(
+ flex->memmap_phy + flex->memmap_offs,
+ flex->memmap_len);
+ if (!flex->ahb_addr) {
+ dev_err(flex->dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* For non-8-byte alignment cases */
+ if (from % 8) {
+ j = 8 - (from & 0x7);
+ for (i = 0; i < j; ++i) {
+ memcpy(buf + i, flex->ahb_addr + flex->chip_base_addr
+ + from - flex->memmap_offs + i, 1);
+ }
+ memcpy(buf + j, flex->ahb_addr + flex->chip_base_addr + from
+ - flex->memmap_offs + j, len - j);
+ } else {
+ /* Read out the data directly from the AHB buffer.*/
+ memcpy(buf, flex->ahb_addr + flex->chip_base_addr + from
+ - flex->memmap_offs, len);
+ }
+
+ return len;
+}
+
+static int fsl_flexspi_erase(struct spi_nor *nor, loff_t offs)
+{
+ struct fsl_flexspi *flex = nor->priv;
+ int ret;
+
+ dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
+ nor->mtd.erasesize / 1024, flex->chip_base_addr, (u32)offs);
+
+ ret = fsl_flexspi_runcmd(flex, nor->erase_opcode, offs, 0);
+ if (ret)
+ return ret;
+
+ fsl_flexspi_invalid(flex);
+ return 0;
+}
+
+static int fsl_flexspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ struct fsl_flexspi *flex = nor->priv;
+ int ret;
+
+ mutex_lock(&flex->lock);
+
+ ret = pm_runtime_get_sync(flex->dev);
+ if (ret < 0) {
+ dev_err(flex->dev, "Failed to enable clock %d\n", __LINE__);
+ goto err_mutex;
+ }
+
+ fsl_flexspi_set_base_addr(flex, nor);
+ return 0;
+
+err_mutex:
+ mutex_unlock(&flex->lock);
+ return ret;
+}
+
+static void fsl_flexspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
+{
+ struct fsl_flexspi *flex = nor->priv;
+
+ pm_runtime_mark_last_busy(flex->dev);
+ pm_runtime_put_autosuspend(flex->dev);
+ mutex_unlock(&flex->lock);
+}
+
+static const struct of_device_id fsl_flexspi_dt_ids[] = {
+ { .compatible = "fsl,imx8qm-flexspi", .data = (void *)&imx8qm_data, },
+ { .compatible = "fsl,imx8qxp-flexspi", .data = (void *)&imx8qxp_data, },
+ { .compatible = "fsl,imx8mm-flexspi", .data = (void *)&imx8mm_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_flexspi_dt_ids);
+
+static int fsl_flexspi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct fsl_flexspi *flex;
+ struct resource *res;
+ struct spi_nor *nor;
+ struct mtd_info *mtd;
+ int ret, i = 0;
+
+ const struct of_device_id *of_id =
+ of_match_device(fsl_flexspi_dt_ids, &pdev->dev);
+
+ flex = devm_kzalloc(dev, sizeof(*flex), GFP_KERNEL);
+ if (!flex)
+ return -ENOMEM;
+
+ flex->nor_num = of_get_child_count(dev->of_node);
+ if (!flex->nor_num || flex->nor_num > 4)
+ return -ENODEV;
+
+ flex->dev = dev;
+ flex->devtype_data = (struct fsl_flexspi_devtype_data *)of_id->data;
+ platform_set_drvdata(pdev, flex);
+ dev_set_drvdata(dev, flex);
+
+ /* find the resources */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "FlexSPI");
+ if (!res) {
+ dev_err(dev, "FlexSPI get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
+
+ flex->iobase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(flex->iobase))
+ return PTR_ERR(flex->iobase);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "FlexSPI-memory");
+ if (!res) {
+ dev_err(dev,
+ "FlexSPI-memory get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region(dev, res->start, resource_size(res),
+ res->name)) {
+ dev_err(dev, "can't request region for resource %pR\n", res);
+ return -EBUSY;
+ }
+
+ flex->memmap_phy = res->start;
+
+ /* find the clocks */
+ flex->clk = devm_clk_get(dev, "fspi");
+ if (IS_ERR(flex->clk))
+ return PTR_ERR(flex->clk);
+
+ /* find ddrsmp value */
+ ret = of_property_read_u32(dev->of_node, "ddrsmp",
+ &flex->ddr_smp);
+ if (ret)
+ flex->ddr_smp = 0;
+
+ /* enable the clock */
+ ret = fsl_flexspi_init_rpm(flex);
+ if (ret) {
+ dev_err(dev, "can not enable the clock\n");
+ goto clk_failed;
+ }
+
+ /* find the irq */
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to get the irq: %d\n", ret);
+ goto clk_failed;
+ }
+
+ ret = devm_request_irq(dev, ret,
+ fsl_flexspi_irq_handler, 0, pdev->name, flex);
+ if (ret) {
+ dev_err(dev, "failed to request irq: %d\n", ret);
+ goto clk_failed;
+ }
+
+ ret = fsl_flexspi_nor_setup(flex);
+ if (ret)
+ goto clk_failed;
+
+ if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
+ flex->has_second_chip = true;
+
+ mutex_init(&flex->lock);
+
+ /* iterate the subnodes. */
+ for_each_available_child_of_node(dev->of_node, np) {
+ enum read_mode mode = SPI_NOR_DDR_QUAD;
+ u32 dummy = 0;
+
+ /* skip the holes */
+ if (!flex->has_second_chip)
+ i *= 2;
+
+ nor = &flex->nor[i];
+ mtd = &nor->mtd;
+
+ nor->dev = dev;
+ spi_nor_set_flash_node(nor, np);
+ nor->priv = flex;
+
+ /* fill the hooks */
+ nor->read_reg = fsl_flexspi_read_reg;
+ nor->write_reg = fsl_flexspi_write_reg;
+ nor->read = fsl_flexspi_read;
+ nor->write = fsl_flexspi_write;
+ nor->erase = fsl_flexspi_erase;
+
+ nor->prepare = fsl_flexspi_prep;
+ nor->unprepare = fsl_flexspi_unprep;
+
+ ret = of_property_read_u32(np, "spi-max-frequency",
+ &flex->clk_rate);
+ if (ret < 0)
+ goto mutex_failed;
+
+ /* Can we enable the DDR Quad Read? */
+ ret = of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy",
+ &dummy);
+ if (!ret && dummy > 0)
+ mode = fsl_flexspi_quad_only(flex) ?
+ SPI_NOR_DDR_QUAD : SPI_NOR_DDR_OCTAL;
+
+ /* set the chip address for READID */
+ fsl_flexspi_set_base_addr(flex, nor);
+
+
+ ret = spi_nor_scan(nor, NULL, mode);
+ if (ret)
+ goto mutex_failed;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto mutex_failed;
+
+ /* Set the correct NOR size now. */
+ if (flex->nor_size == 0) {
+ flex->nor_size = mtd->size;
+
+ /* Map the SPI NOR to accessiable address */
+ fsl_flexspi_set_map_addr(flex);
+ }
+
+ /*
+ * The TX FIFO is 64 bytes in the Vybrid, but the Page Program
+ * may writes 265 bytes per time. The write is working in the
+ * unit of the TX FIFO, not in the unit of the SPI NOR's page
+ * size.
+ *
+ * So shrink the spi_nor->page_size if it is larger then the
+ * TX FIFO.
+ */
+ if (nor->page_size > flex->devtype_data->txfifo)
+ nor->page_size = flex->devtype_data->txfifo;
+
+ i++;
+ }
+
+ /* finish the rest init. */
+ ret = fsl_flexspi_nor_setup_last(flex);
+ if (ret)
+ goto last_init_failed;
+
+ /* indicate the controller has been initialized */
+ flex->flags |= FLEXSPI_INITILIZED;
+
+ return 0;
+
+last_init_failed:
+ for (i = 0; i < flex->nor_num; i++) {
+ /* skip the holes */
+ if (!flex->has_second_chip)
+ i *= 2;
+ mtd_device_unregister(&flex->mtd[i]);
+ }
+mutex_failed:
+ mutex_destroy(&flex->lock);
+clk_failed:
+ dev_err(dev, "Freescale FlexSPI probe failed\n");
+ return ret;
+}
+
+static int fsl_flexspi_remove(struct platform_device *pdev)
+{
+ struct fsl_flexspi *flex = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < flex->nor_num; i++) {
+ /* skip the holes */
+ if (!flex->has_second_chip)
+ i *= 2;
+ mtd_device_unregister(&flex->nor[i].mtd);
+ }
+
+ /* disable the hardware */
+ writel(FLEXSPI_MCR0_MDIS_MASK, flex->iobase + FLEXSPI_MCR0);
+
+ pm_runtime_disable(flex->dev);
+
+ mutex_destroy(&flex->lock);
+
+ if (flex->ahb_addr)
+ iounmap(flex->ahb_addr);
+
+ return 0;
+}
+
+static int fsl_flexspi_initialized(struct fsl_flexspi *flex)
+{
+ return flex->flags & FLEXSPI_INITILIZED;
+}
+
+static int fsl_flexspi_need_reinit(struct fsl_flexspi *flex)
+{
+ /* we always use the controller in combination mode, so we check this */
+ /* register bit to determine if the controller once lost power, such as */
+ /* suspend/resume, and need to be re-init */
+
+ return !(readl(flex->iobase + FLEXSPI_MCR0) & FLEXSPI_MCR0_OCTCOMB_EN_MASK);
+}
+
+int fsl_flexspi_runtime_suspend(struct device *dev)
+{
+ struct fsl_flexspi *flex = dev_get_drvdata(dev);
+
+ fsl_flexspi_clk_disable_unprep(flex);
+
+ return 0;
+}
+
+int fsl_flexspi_runtime_resume(struct device *dev)
+{
+ struct fsl_flexspi *flex = dev_get_drvdata(dev);
+
+ fsl_flexspi_clk_prep_enable(flex);
+
+ if (fsl_flexspi_initialized(flex) &&
+ fsl_flexspi_need_reinit(flex)) {
+ fsl_flexspi_nor_setup(flex);
+ fsl_flexspi_set_map_addr(flex);
+ fsl_flexspi_nor_setup_last(flex);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops fsl_flexspi_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_flexspi_runtime_suspend, fsl_flexspi_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_flexspi_driver = {
+ .driver = {
+ .name = "fsl-flexspi",
+ .bus = &platform_bus_type,
+ .pm = &fsl_flexspi_pm_ops,
+ .of_match_table = fsl_flexspi_dt_ids,
+ },
+ .probe = fsl_flexspi_probe,
+ .remove = fsl_flexspi_remove,
+};
+module_platform_driver(fsl_flexspi_driver);
+
+
+MODULE_DESCRIPTION("Freescale FlexSPI Controller Driver");
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 5c82e4ef1904..640eb1010dfc 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -1,7 +1,8 @@
/*
* Freescale QuadSPI driver.
*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,9 +42,14 @@
#define QUADSPI_QUIRK_TKT253890 (1 << 2)
/* Controller cannot wake up from wait mode, TKT245618 */
#define QUADSPI_QUIRK_TKT245618 (1 << 3)
+/* Need low level code to control the clock */
+#define QUADSPI_QUIRK_LL_CLK (1 << 4)
/* The registers */
#define QUADSPI_MCR 0x00
+#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT 29
+#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK \
+ (1 << MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT)
#define QUADSPI_MCR_RESERVED_SHIFT 16
#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT)
#define QUADSPI_MCR_MDIS_SHIFT 14
@@ -61,6 +67,11 @@
#define QUADSPI_MCR_SWRSTSD_SHIFT 0
#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT)
+#define QUADSPI_FLSHCR 0x0c
+#define QUADSPI_FLSHCR_TDH_SHIFT 16
+#define QUADSPI_FLSHCR_TDH_MASK (3 << QUADSPI_FLSHCR_TDH_SHIFT)
+#define QUADSPI_FLSHCR_TDH_DDR_EN (1 << QUADSPI_FLSHCR_TDH_SHIFT)
+
#define QUADSPI_IPCR 0x08
#define QUADSPI_IPCR_SEQID_SHIFT 24
#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT)
@@ -205,15 +216,49 @@
#define SEQID_RDCR 9
#define SEQID_EN4B 10
#define SEQID_BRWR 11
+#define SEQID_RD_EVCR 12
+#define SEQID_WD_EVCR 13
+
+/* last two lut slots for dynamic luts*/
+#define SEQID_DYNAMIC_CMD0 14
+#define SEQID_DYNAMIC_CMD1 15
+
+#define QUADSPI_MIN_IOMAP SZ_4M
-#define QUADSPI_MIN_IOMAP SZ_4M
+/* dynamic lut configs */
+#define MAX_LUT_REGS 4
+struct lut_desc {
+ u8 cmd;
+ u32 lut[MAX_LUT_REGS];
+};
+/*
+ * define two lut_des in the struct because many commands use in pairs.
+ * To add a single command, just leave the second desc as blank.
+ */
+struct lut_desc_pair {
+ struct lut_desc lut_desc0;
+ struct lut_desc lut_desc1;
+};
+
+struct lut_desc_pair current_lut_pair;
+
+static const struct lut_desc_pair dynamic_lut_table[] = {
+ /* VCR RD/WR pair */
+ { {SPINOR_OP_RD_VCR, {LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR) |
+ LUT1(FSL_READ, PAD1, 0x1)} },
+ {SPINOR_OP_WR_VCR, {LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR) |
+ LUT1(FSL_WRITE, PAD1, 0x1)} },
+ },
+ {/* sentinel */},
+};
enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID,
FSL_QUADSPI_IMX6SX,
FSL_QUADSPI_IMX7D,
FSL_QUADSPI_IMX6UL,
FSL_QUADSPI_LS1021A,
+ FSL_QUADSPI_IMX7ULP,
};
struct fsl_qspi_devtype_data {
@@ -267,6 +312,15 @@ static struct fsl_qspi_devtype_data ls1021a_data = {
.driver_data = 0,
};
+static struct fsl_qspi_devtype_data imx7ulp_data = {
+ .devtype = FSL_QUADSPI_IMX7ULP,
+ .rxfifo = 64,
+ .txfifo = 64,
+ .ahb_buf_size = 128,
+ .driver_data = QUADSPI_QUIRK_LL_CLK
+ | QUADSPI_QUIRK_TKT253890
+};
+
#define FSL_QSPI_MAX_CHIP 4
struct fsl_qspi {
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
@@ -285,6 +339,7 @@ struct fsl_qspi {
unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip;
bool big_endian;
+ u32 ddr_smp;
struct mutex lock;
struct pm_qos_request pm_qos_req;
};
@@ -309,6 +364,11 @@ static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
}
+static inline int needs_ll_handle_clock(struct fsl_qspi *q)
+{
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_LL_CLK;
+}
+
/*
* R/W functions for big- or little-endian registers:
* The qSPI controller's endian is independent of the CPU core's endian.
@@ -372,8 +432,10 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo;
+ struct spi_nor *nor = &q->nor[0];
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
u32 lut_base;
- u8 cmd, addrlen, dummy;
+ u8 op, dm;
int i;
fsl_qspi_unlock_lut(q);
@@ -382,24 +444,57 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
for (i = 0; i < QUADSPI_LUT_NUM; i++)
qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
- /* Quad Read */
+ /* Quad Read and DDR Quad Read*/
lut_base = SEQID_QUAD_READ * 4;
-
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR24BIT;
- dummy = 8;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR32BIT;
- dummy = 8;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ op = nor->read_opcode;
+ dm = nor->read_dummy;
+ if (nor->flash_read == SPI_NOR_QUAD) {
+ if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) {
+ /* read mode : 1-1-4 */
+ qspi_writel(q, LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(DUMMY, PAD1, dm) | LUT1(FSL_READ, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ } else if (nor->flash_read == SPI_NOR_NORMAL) {
+ writel(LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
+ writel(LUT0(FSL_READ, PAD1, rxfifo) | LUT1(JMP_ON_CS, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
+ } else if (nor->flash_read == SPI_NOR_DDR_QUAD) {
+ if (op == SPINOR_OP_READ_1_4_4_D ||
+ op == SPINOR_OP_READ4_1_4_4_D) {
+ /* read mode : 1-4-4, such as Spansion s25fl128s. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD4, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff)
+ | LUT1(DUMMY, PAD1, dm),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ qspi_writel(q, LUT0(FSL_READ_DDR, PAD4, rxfifo)
+ | LUT1(JMP_ON_CS, PAD1, 0),
+ base + QUADSPI_LUT(lut_base + 2));
+ } else if (op == SPINOR_OP_READ_1_1_4_D) {
+ /* read mode : 1-1-4, such as Micron N25Q256A. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(DUMMY, PAD1, dm)
+ | LUT1(FSL_READ_DDR, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ qspi_writel(q, LUT0(JMP_ON_CS, PAD1, 0),
+ base + QUADSPI_LUT(lut_base + 2));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ }
/* Write enable */
lut_base = SEQID_WREN * 4;
@@ -408,18 +503,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Page Program */
lut_base = SEQID_PP * 4;
-
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_PP;
- addrlen = ADDR24BIT;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_PP;
- addrlen = ADDR32BIT;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
- base + QUADSPI_LUT(lut_base));
+ qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
@@ -431,12 +516,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Erase a sector */
lut_base = SEQID_SE * 4;
-
- cmd = q->nor[0].erase_opcode;
- addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
- base + QUADSPI_LUT(lut_base));
+ qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */
lut_base = SEQID_CHIP_ERASE * 4;
@@ -476,14 +557,85 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
base + QUADSPI_LUT(lut_base));
+ /* Read EVCR register */
+ lut_base = SEQID_RD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR), base + QUADSPI_LUT(lut_base));
+
+ /* Write EVCR register */
+ lut_base = SEQID_WD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR), base + QUADSPI_LUT(lut_base));
fsl_qspi_lock_lut(q);
}
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q);
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q);
+
+static int fsl_qspi_update_dynamic_lut(struct fsl_qspi *q, int index)
+{
+ void __iomem *base = q->iobase;
+ u32 lut_base;
+ int i;
+ int size;
+
+ fsl_qspi_unlock_lut(q);
+
+ lut_base = SEQID_DYNAMIC_CMD0 * 4;
+ size = ARRAY_SIZE(dynamic_lut_table[index].lut_desc0.lut);
+ for (i = 0; i < size; i++) {
+ writel(dynamic_lut_table[index].lut_desc0.lut[i],
+ base + QUADSPI_LUT(lut_base + i));
+ }
+
+ lut_base = SEQID_DYNAMIC_CMD1 * 4;
+ size = ARRAY_SIZE(dynamic_lut_table[index].lut_desc1.lut);
+ for (i = 0; i < size; i++) {
+ writel(dynamic_lut_table[index].lut_desc1.lut[i],
+ base + QUADSPI_LUT(lut_base + i));
+ }
+
+ fsl_qspi_lock_lut(q);
+
+ return 0;
+}
+
+static int fsl_qspi_search_dynamic_lut(struct fsl_qspi *q, u8 cmd)
+{
+ int i;
+ int ret = 0;
+
+ if (cmd == current_lut_pair.lut_desc0.cmd)
+ return SEQID_DYNAMIC_CMD0;
+ if (cmd == current_lut_pair.lut_desc1.cmd)
+ return SEQID_DYNAMIC_CMD1;
+ for (i = 0; i < ARRAY_SIZE(dynamic_lut_table); i++) {
+ if (cmd == dynamic_lut_table[i].lut_desc0.cmd)
+ ret = SEQID_DYNAMIC_CMD0;
+ if (cmd == dynamic_lut_table[i].lut_desc1.cmd)
+ ret = SEQID_DYNAMIC_CMD1;
+ if (ret) {
+ if (fsl_qspi_update_dynamic_lut(q, i)) {
+ pr_err(" failed to update dynamic lut\n");
+ return 0;
+ }
+ current_lut_pair = dynamic_lut_table[i];
+ return ret;
+ }
+ }
+ return ret;
+}
+
/* Get the SEQID for the command */
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{
+ int ret;
+
switch (cmd) {
+ case SPINOR_OP_READ_1_1_4_D:
+ case SPINOR_OP_READ_1_4_4_D:
+ case SPINOR_OP_READ4_1_4_4_D:
+ case SPINOR_OP_READ4_1_1_4:
case SPINOR_OP_READ_1_1_4:
+ case SPINOR_OP_READ:
return SEQID_QUAD_READ;
case SPINOR_OP_WREN:
return SEQID_WREN;
@@ -491,6 +643,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
return SEQID_WRDI;
case SPINOR_OP_RDSR:
return SEQID_RDSR;
+ case SPINOR_OP_BE_4K:
case SPINOR_OP_SE:
return SEQID_SE;
case SPINOR_OP_CHIP_ERASE:
@@ -507,9 +660,16 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
return SEQID_EN4B;
case SPINOR_OP_BRWR:
return SEQID_BRWR;
+ case SPINOR_OP_RD_EVCR:
+ return SEQID_RD_EVCR;
+ case SPINOR_OP_WD_EVCR:
+ return SEQID_WD_EVCR;
default:
if (cmd == q->nor[0].erase_opcode)
return SEQID_SE;
+ ret = fsl_qspi_search_dynamic_lut(q, cmd);
+ if (ret)
+ return ret;
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break;
}
@@ -680,6 +840,8 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
+ struct spi_nor *nor = &q->nor[0];
+ u32 reg, reg2;
int seqid;
/* AHB configuration for access buffer 0/1/2 .*/
@@ -701,9 +863,40 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
qspi_writel(q, 0, base + QUADSPI_BUF2IND);
/* Set the default lut sequence for AHB Read. */
- seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
+ seqid = fsl_qspi_get_seqid(q, nor->read_opcode);
qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
q->iobase + QUADSPI_BFGENCR);
+
+ /* enable the DDR quad read */
+ if (nor->flash_read == SPI_NOR_DDR_QUAD) {
+ reg = readl(q->iobase + QUADSPI_MCR);
+
+ /* Firstly, disable the module */
+ qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK,
+ q->iobase + QUADSPI_MCR);
+
+ /* Set the Sampling Register for DDR */
+ reg2 = readl(q->iobase + QUADSPI_SMPR);
+ reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK;
+ reg2 |= ((q->ddr_smp << QUADSPI_SMPR_DDRSMP_SHIFT) &
+ QUADSPI_SMPR_DDRSMP_MASK);
+ qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR);
+
+ /* Enable the module again (enable the DDR too) */
+ reg |= QUADSPI_MCR_DDR_EN_MASK;
+ if (q->devtype_data->devtype == FSL_QUADSPI_IMX6SX)
+ reg |= MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK;
+
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+
+ if ((q->devtype_data->devtype == FSL_QUADSPI_IMX6UL) ||
+ (q->devtype_data->devtype == FSL_QUADSPI_IMX7D)) {
+ reg = readl(q->iobase + QUADSPI_FLSHCR);
+ reg &= ~QUADSPI_FLSHCR_TDH_MASK;
+ reg |= QUADSPI_FLSHCR_TDH_DDR_EN;
+ qspi_writel(q, reg, q->iobase + QUADSPI_FLSHCR);
+ }
+ }
}
/* This function was used to prepare and enable QSPI clock */
@@ -750,13 +943,21 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
/* the default frequency, we will change it in the future. */
ret = clk_set_rate(q->clk, 66000000);
- if (ret)
+ if (ret && !needs_ll_handle_clock(q))
return ret;
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;
+ if ((q->devtype_data->devtype == FSL_QUADSPI_IMX6UL) ||
+ (q->devtype_data->devtype == FSL_QUADSPI_IMX7D)) {
+ /* clear the DDR_EN bit for 6UL and 7D */
+ reg = readl(base + QUADSPI_MCR);
+ writel(~(QUADSPI_MCR_DDR_EN_MASK) & reg, base + QUADSPI_MCR);
+ udelay(1);
+ }
+
/* Reset the module */
qspi_writel(q, QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
base + QUADSPI_MCR);
@@ -800,7 +1001,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
fsl_qspi_clk_disable_unprep(q);
ret = clk_set_rate(q->clk, rate);
- if (ret)
+ if (ret && !needs_ll_handle_clock(q))
return ret;
ret = fsl_qspi_clk_prep_enable(q);
@@ -809,6 +1010,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
/* Init the LUT table again. */
fsl_qspi_init_lut(q);
+ fsl_qspi_update_dynamic_lut(q, 0);
/* Init for AHB read */
fsl_qspi_init_abh_read(q);
@@ -822,6 +1024,8 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
{ .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
+ { .compatible = "fsl,imx6ull-qspi", .data = (void *)&imx6ul_data, },
+ { .compatible = "fsl,imx7ulp-qspi", .data = (void *)&imx7ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
@@ -887,6 +1091,7 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
+ int i, j;
/* if necessary,ioremap buffer before AHB read, */
if (!q->ahb_addr) {
@@ -921,9 +1126,20 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
len);
- /* Read out the data directly from the AHB buffer.*/
- memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
- len);
+ /* For non-8-byte alignment cases */
+ if (from % 8) {
+ j = 8 - (from & 0x7);
+ for (i = 0; i < j; ++i) {
+ memcpy(buf + i, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs + i, 1);
+ }
+ memcpy(buf + j, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs + j, len - j);
+ } else {
+ /* Read out the data directly from the AHB buffer.*/
+ memcpy(buf, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs, len);
+ }
return len;
}
@@ -997,6 +1213,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
+ if (!res) {
+ dev_err(dev, "QuadSPI get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
q->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(q->iobase))
return PTR_ERR(q->iobase);
@@ -1004,6 +1224,11 @@ static int fsl_qspi_probe(struct platform_device *pdev)
q->big_endian = of_property_read_bool(np, "big-endian");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory");
+ if (!res) {
+ dev_err(dev,
+ "QuadSPI-memory get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
if (!devm_request_mem_region(dev, res->start, resource_size(res),
res->name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
@@ -1021,6 +1246,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (IS_ERR(q->clk))
return PTR_ERR(q->clk);
+ /* find ddrsmp value */
+ ret = of_property_read_u32(dev->of_node, "ddrsmp",
+ &q->ddr_smp);
+ if (ret)
+ q->ddr_smp = 0;
+
ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
dev_err(dev, "can not enable the clock\n");
@@ -1052,6 +1283,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) {
+ enum read_mode mode = SPI_NOR_QUAD;
+ u32 dummy = 0;
+
/* skip the holes */
if (!q->has_second_chip)
i *= 2;
@@ -1078,10 +1312,16 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret < 0)
goto mutex_failed;
+ /* Can we enable the DDR Quad Read? */
+ ret = of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy",
+ &dummy);
+ if (!ret && dummy > 0)
+ mode = SPI_NOR_DDR_QUAD;
+
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, mode);
if (ret)
goto mutex_failed;
@@ -1162,6 +1402,7 @@ static int fsl_qspi_remove(struct platform_device *pdev)
static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
{
+ pinctrl_pm_select_sleep_state(&pdev->dev);
return 0;
}
@@ -1170,6 +1411,8 @@ static int fsl_qspi_resume(struct platform_device *pdev)
int ret;
struct fsl_qspi *q = platform_get_drvdata(pdev);
+ pinctrl_pm_select_default_state(&pdev->dev);
+
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c42523b7d5ed..88670b8ccb05 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -3,7 +3,8 @@
* influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c
*
* Copyright (C) 2005, Intec Automation Inc.
- * Copyright (C) 2014, Freescale Semiconductor, Inc.
+ * Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -75,6 +76,8 @@ struct flash_info {
* bit. Must be used with
* SPI_NOR_HAS_LOCK.
*/
+#define SPI_NOR_DDR_QUAD_READ BIT(10) /* Flash supports DDR Quad Read */
+#define SPI_NOR_DDR_OCTAL_READ BIT(11) /* Flash supports DDR Octal Read */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -146,9 +149,26 @@ static int read_cr(struct spi_nor *nor)
static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
{
switch (nor->flash_read) {
+ case SPI_NOR_DDR_QUAD:
+ case SPI_NOR_DDR_OCTAL:
+ {
+ struct device_node *np = spi_nor_get_flash_node(nor);
+ u32 dummy;
+
+ /*
+ * The m25p80.c can not support the DDR quad read.
+ * We set the dummy cycles to 8 by default. The SPI NOR
+ * controller driver can set it in its child DT node.
+ * We parse it out here.
+ */
+ if (!of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy",
+ &dummy))
+ return dummy;
+ }
case SPI_NOR_FAST:
case SPI_NOR_DUAL:
case SPI_NOR_QUAD:
+ case SPI_NOR_OCTAL:
return 8;
case SPI_NOR_NORMAL:
return 0;
@@ -198,6 +218,7 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
switch (JEDEC_MFR(info)) {
case SNOR_MFR_MICRON:
+ case SNOR_MFR_MICRONO:
/* Some Micron need WREN command; all will accept it */
need_wren = true;
case SNOR_MFR_MACRONIX:
@@ -875,10 +896,12 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) },
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) },
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
+ { "mx25r6435f", INFO(0xc22817, 0, 64 * 1024, 128, 0) },
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+ { "mx25l51245g", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_DDR_QUAD_READ) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
@@ -888,12 +911,14 @@ static const struct flash_info spi_nor_ids[] = {
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "mt25qu256", INFO(0x20bb19, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_DDR_QUAD_READ) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_DDR_QUAD_READ) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ {"mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, SECT_4K | SPI_NOR_DDR_OCTAL_READ) },
/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
@@ -912,6 +937,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "s25fl128s", INFO(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ | SPI_NOR_DDR_QUAD_READ) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
@@ -1178,9 +1204,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
ssize_t written;
page_offset = (to + i) & (nor->page_size - 1);
- WARN_ONCE(page_offset,
- "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
- page_offset);
/* the size of data remaining on the first page */
page_remain = min_t(size_t,
nor->page_size - page_offset, len - i);
@@ -1278,6 +1301,36 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
+static int set_ddr_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+ int status;
+
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_AMD: /* Spansion, actually */
+ status = spansion_quad_enable(nor);
+ if (status) {
+ dev_err(nor->dev,
+ "Spansion DDR quad-read not enabled\n");
+ return status;
+ }
+ return status;
+ case CFI_MFR_MACRONIX:
+ status = macronix_quad_enable(nor);
+ if (status) {
+ dev_err(nor->dev,
+ "Macronix DDR quad-read not enabled\n");
+ return status;
+ }
+ return status;
+ case CFI_MFR_ST: /* Micron, actually */
+ case CFI_MFR_MICRON: /* Original Micron */
+ /* DTR quad read works with the Extended SPI protocol. */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{
int status;
@@ -1291,6 +1344,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
}
return status;
case SNOR_MFR_MICRON:
+ case SNOR_MFR_MICRONO:
return 0;
default:
status = spansion_quad_enable(nor);
@@ -1385,7 +1439,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mtd->_read = spi_nor_read;
/* NOR protection support for STmicro/Micron chips and similar */
- if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
+ if (JEDEC_MFR(info) == SNOR_MFR_MICRON || SNOR_MFR_MICRONO ||
info->flags & SPI_NOR_HAS_LOCK) {
nor->flash_lock = stm_lock;
nor->flash_unlock = stm_unlock;
@@ -1446,8 +1500,18 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (info->flags & SPI_NOR_NO_FR)
nor->flash_read = SPI_NOR_NORMAL;
- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
+ /* DDR Octal/Quad/Dual-read mode takes precedence over fast/normal */
+ if (mode == SPI_NOR_DDR_OCTAL && info->flags & SPI_NOR_DDR_OCTAL_READ) {
+ nor->flash_read = SPI_NOR_DDR_OCTAL;
+ } else if (mode == SPI_NOR_DDR_QUAD &&
+ info->flags & SPI_NOR_DDR_QUAD_READ) {
+ ret = set_ddr_quad_mode(nor, info);
+ if (ret) {
+ dev_err(dev, "DDR quad mode not supported\n");
+ return ret;
+ }
+ nor->flash_read = SPI_NOR_DDR_QUAD;
+ } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
ret = set_quad_mode(nor, info);
if (ret) {
dev_err(dev, "quad mode not supported\n");
@@ -1460,6 +1524,23 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
/* Default commands */
switch (nor->flash_read) {
+ case SPI_NOR_DDR_OCTAL:
+ nor->read_opcode = SPINOR_OP_READ_1_1_8_D;
+ break;
+ case SPI_NOR_DDR_QUAD:
+ if (JEDEC_MFR(info) == CFI_MFR_AMD) { /* Spansion */
+ nor->read_opcode = SPINOR_OP_READ_1_4_4_D;
+ } else if (JEDEC_MFR(info) == CFI_MFR_ST) {
+ nor->read_opcode = SPINOR_OP_READ_1_1_4_D;
+ } else if (JEDEC_MFR(info) == CFI_MFR_MICRON) {
+ nor->read_opcode = SPINOR_OP_READ_1_1_4_D;
+ } else if (JEDEC_MFR(info) == CFI_MFR_MACRONIX) {
+ nor->read_opcode = SPINOR_OP_READ_1_4_4_D;
+ } else {
+ dev_err(dev, "DDR Quad Read is not supported.\n");
+ return -EINVAL;
+ }
+ break;
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ_1_1_4;
break;
@@ -1487,6 +1568,13 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
/* Dedicated 4-byte command set */
switch (nor->flash_read) {
+ case SPI_NOR_DDR_OCTAL:
+ case SPI_NOR_OCTAL:
+ nor->read_opcode = SPINOR_OP_READ_1_1_8_D;
+ break;
+ case SPI_NOR_DDR_QUAD:
+ nor->read_opcode = SPINOR_OP_READ4_1_4_4_D;
+ break;
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ4_1_1_4;
break;
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index ad2b57c6b13f..35c76e4053f8 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -628,7 +628,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