diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/bch-regs.h | 24 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 111 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 458 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 37 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/fsl-flexspi.c | 1471 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/fsl-quadspi.c | 329 | ||||
-rw-r--r-- | drivers/mtd/spi-nor/spi-nor.c | 104 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 2 |
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 |