summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/gpmi-nand/gpmi-nand.c')
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c458
1 files changed, 414 insertions, 44 deletions
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)
};