summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 18:13:21 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 18:13:21 -0700
commitc309bfa9b481e7dbd3e1ab819271bf3009f44859 (patch)
treefc102360b3e7cb53796a451bc8fe01239c734e07 /drivers/mtd/nand
parent9e9ac896667a55ae9a3df119611ee5322abe2890 (diff)
parent2a500afe1e0e84c7a126df693dbd01353756dcfa (diff)
Merge tag 'for-linus-20140808' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris: "AMD-compatible CFI driver: - Support OTP programming for Micron M29EW family - Increase buffer write timeout, according to detected flash parameter info NAND - Add helpers for retrieving ONFI timing modes - GPMI: provide option to disable bad block marker swapping (required for Ka-On electronics platforms) SPI NOR - EON EN25QH128 support - Support new Flag Status Register (FSR) on a few Micron flash Common - New sysfs entries for bad block and ECC stats And a few miscellaneous refactorings, cleanups, and driver improvements" * tag 'for-linus-20140808' of git://git.infradead.org/linux-mtd: (31 commits) mtd: gpmi: make blockmark swapping optional mtd: gpmi: remove line breaks from error messages and improve wording mtd: gpmi: remove useless (void *) type casts and spaces between type casts and variables mtd: atmel_nand: NFC: support multiple interrupt handling mtd: atmel_nand: implement the nfc_device_ready() by checking the R/B bit mtd: atmel_nand: add NFC status error check mtd: atmel_nand: make ecc parameters same as definition mtd: nand: add ONFI timing mode to nand_timings converter mtd: nand: define struct nand_timings mtd: cfi_cmdset_0002: fix do_write_buffer() timeout error mtd: denali: use 8 bytes for READID command mtd/ftl: fix the double free of the buffers allocated in build_maps() mtd: phram: Fix whitespace issues mtd: spi-nor: add support for EON EN25QH128 mtd: cfi_cmdset_0002: Add support for locking OTP memory mtd: cfi_cmdset_0002: Add support for writing OTP memory mtd: cfi_cmdset_0002: Invalidate cache after entering/exiting OTP memory mtd: cfi_cmdset_0002: Add support for reading OTP mtd: spi-nor: add support for flag status register on Micron chips mtd: Account for BBT blocks when a partition is being allocated ...
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/atmel_nand.c142
-rw-r--r--drivers/mtd/nand/atmel_nand_nfc.h4
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c24
-rw-r--r--drivers/mtd/nand/denali.c6
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.c71
-rw-r--r--drivers/mtd/nand/lpc32xx_mlc.c6
-rw-r--r--drivers/mtd/nand/lpc32xx_slc.c6
-rw-r--r--drivers/mtd/nand/nand_base.c18
-rw-r--r--drivers/mtd/nand/nand_bbt.c14
-rw-r--r--drivers/mtd/nand/nand_timings.c253
-rw-r--r--drivers/mtd/nand/s3c2410.c4
12 files changed, 443 insertions, 107 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 542b5689eb63..a035e7cc6d46 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -50,4 +50,4 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
-nand-objs := nand_base.o nand_bbt.o
+nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 4ce181a35bcd..e321c564ff05 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -97,7 +97,9 @@ struct atmel_nfc {
bool write_by_sram;
bool is_initialized;
- struct completion comp_nfc;
+ struct completion comp_ready;
+ struct completion comp_cmd_done;
+ struct completion comp_xfer_done;
/* Point to the sram bank which include readed data via NFC */
void __iomem *data_in_sram;
@@ -861,12 +863,11 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
{
struct nand_chip *nand_chip = mtd->priv;
struct atmel_nand_host *host = nand_chip->priv;
- int i, err_nbr, eccbytes;
+ int i, err_nbr;
uint8_t *buf_pos;
int total_err = 0;
- eccbytes = nand_chip->ecc.bytes;
- for (i = 0; i < eccbytes; i++)
+ for (i = 0; i < nand_chip->ecc.total; i++)
if (ecc[i] != 0xff)
goto normal_check;
/* Erased page, return OK */
@@ -928,7 +929,7 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
{
struct atmel_nand_host *host = chip->priv;
- int eccsize = chip->ecc.size;
+ int eccsize = chip->ecc.size * chip->ecc.steps;
uint8_t *oob = chip->oob_poi;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint32_t stat;
@@ -1169,8 +1170,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
goto err;
}
- /* ECC is calculated for the whole page (1 step) */
- nand_chip->ecc.size = mtd->writesize;
+ nand_chip->ecc.size = sector_size;
/* set ECC page size and oob layout */
switch (mtd->writesize) {
@@ -1185,18 +1185,20 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
host->pmecc_index_of = host->pmecc_rom_base +
host->pmecc_lookup_table_offset;
- nand_chip->ecc.steps = 1;
+ nand_chip->ecc.steps = host->pmecc_sector_number;
nand_chip->ecc.strength = cap;
- nand_chip->ecc.bytes = host->pmecc_bytes_per_sector *
+ nand_chip->ecc.bytes = host->pmecc_bytes_per_sector;
+ nand_chip->ecc.total = host->pmecc_bytes_per_sector *
host->pmecc_sector_number;
- if (nand_chip->ecc.bytes > mtd->oobsize - 2) {
+ if (nand_chip->ecc.total > mtd->oobsize - 2) {
dev_err(host->dev, "No room for ECC bytes\n");
err_no = -EINVAL;
goto err;
}
pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
mtd->oobsize,
- nand_chip->ecc.bytes);
+ nand_chip->ecc.total);
+
nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
break;
case 512:
@@ -1572,49 +1574,104 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev,
return 0;
}
+static inline u32 nfc_read_status(struct atmel_nand_host *host)
+{
+ u32 err_flags = NFC_SR_DTOE | NFC_SR_UNDEF | NFC_SR_AWB | NFC_SR_ASE;
+ u32 nfc_status = nfc_readl(host->nfc->hsmc_regs, SR);
+
+ if (unlikely(nfc_status & err_flags)) {
+ if (nfc_status & NFC_SR_DTOE)
+ dev_err(host->dev, "NFC: Waiting Nand R/B Timeout Error\n");
+ else if (nfc_status & NFC_SR_UNDEF)
+ dev_err(host->dev, "NFC: Access Undefined Area Error\n");
+ else if (nfc_status & NFC_SR_AWB)
+ dev_err(host->dev, "NFC: Access memory While NFC is busy\n");
+ else if (nfc_status & NFC_SR_ASE)
+ dev_err(host->dev, "NFC: Access memory Size Error\n");
+ }
+
+ return nfc_status;
+}
+
/* SMC interrupt service routine */
static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
{
struct atmel_nand_host *host = dev_id;
u32 status, mask, pending;
- irqreturn_t ret = IRQ_HANDLED;
+ irqreturn_t ret = IRQ_NONE;
- status = nfc_readl(host->nfc->hsmc_regs, SR);
+ status = nfc_read_status(host);
mask = nfc_readl(host->nfc->hsmc_regs, IMR);
pending = status & mask;
if (pending & NFC_SR_XFR_DONE) {
- complete(&host->nfc->comp_nfc);
+ complete(&host->nfc->comp_xfer_done);
nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
- } else if (pending & NFC_SR_RB_EDGE) {
- complete(&host->nfc->comp_nfc);
+ ret = IRQ_HANDLED;
+ }
+ if (pending & NFC_SR_RB_EDGE) {
+ complete(&host->nfc->comp_ready);
nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE);
- } else if (pending & NFC_SR_CMD_DONE) {
- complete(&host->nfc->comp_nfc);
+ ret = IRQ_HANDLED;
+ }
+ if (pending & NFC_SR_CMD_DONE) {
+ complete(&host->nfc->comp_cmd_done);
nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE);
- } else {
- ret = IRQ_NONE;
+ ret = IRQ_HANDLED;
}
return ret;
}
/* NFC(Nand Flash Controller) related functions */
-static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
+static void nfc_prepare_interrupt(struct atmel_nand_host *host, u32 flag)
{
- unsigned long timeout;
- init_completion(&host->nfc->comp_nfc);
+ if (flag & NFC_SR_XFR_DONE)
+ init_completion(&host->nfc->comp_xfer_done);
+
+ if (flag & NFC_SR_RB_EDGE)
+ init_completion(&host->nfc->comp_ready);
+
+ if (flag & NFC_SR_CMD_DONE)
+ init_completion(&host->nfc->comp_cmd_done);
/* Enable interrupt that need to wait for */
nfc_writel(host->nfc->hsmc_regs, IER, flag);
+}
- timeout = wait_for_completion_timeout(&host->nfc->comp_nfc,
- msecs_to_jiffies(NFC_TIME_OUT_MS));
- if (timeout)
- return 0;
+static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
+{
+ int i, index = 0;
+ struct completion *comp[3]; /* Support 3 interrupt completion */
- /* Time out to wait for the interrupt */
+ if (flag & NFC_SR_XFR_DONE)
+ comp[index++] = &host->nfc->comp_xfer_done;
+
+ if (flag & NFC_SR_RB_EDGE)
+ comp[index++] = &host->nfc->comp_ready;
+
+ if (flag & NFC_SR_CMD_DONE)
+ comp[index++] = &host->nfc->comp_cmd_done;
+
+ if (index == 0) {
+ dev_err(host->dev, "Unkown interrupt flag: 0x%08x\n", flag);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < index; i++) {
+ if (wait_for_completion_timeout(comp[i],
+ msecs_to_jiffies(NFC_TIME_OUT_MS)))
+ continue; /* wait for next completion */
+ else
+ goto err_timeout;
+ }
+
+ return 0;
+
+err_timeout:
dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag);
+ /* Disable the interrupt as it is not handled by interrupt handler */
+ nfc_writel(host->nfc->hsmc_regs, IDR, flag);
return -ETIMEDOUT;
}
@@ -1622,6 +1679,9 @@ static int nfc_send_command(struct atmel_nand_host *host,
unsigned int cmd, unsigned int addr, unsigned char cycle0)
{
unsigned long timeout;
+ u32 flag = NFC_SR_CMD_DONE;
+ flag |= cmd & NFCADDR_CMD_DATAEN ? NFC_SR_XFR_DONE : 0;
+
dev_dbg(host->dev,
"nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n",
cmd, addr, cycle0);
@@ -1635,18 +1695,28 @@ static int nfc_send_command(struct atmel_nand_host *host,
return -ETIMEDOUT;
}
}
+
+ nfc_prepare_interrupt(host, flag);
nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0);
nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs);
- return nfc_wait_interrupt(host, NFC_SR_CMD_DONE);
+ return nfc_wait_interrupt(host, flag);
}
static int nfc_device_ready(struct mtd_info *mtd)
{
+ u32 status, mask;
struct nand_chip *nand_chip = mtd->priv;
struct atmel_nand_host *host = nand_chip->priv;
- if (!nfc_wait_interrupt(host, NFC_SR_RB_EDGE))
- return 1;
- return 0;
+
+ status = nfc_read_status(host);
+ mask = nfc_readl(host->nfc->hsmc_regs, IMR);
+
+ /* The mask should be 0. If not we may lost interrupts */
+ if (unlikely(mask & status))
+ dev_err(host->dev, "Lost the interrupt flags: 0x%08x\n",
+ mask & status);
+
+ return status & NFC_SR_RB_EDGE;
}
static void nfc_select_chip(struct mtd_info *mtd, int chip)
@@ -1795,10 +1865,6 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
- if (dataen == NFCADDR_CMD_DATAEN)
- if (nfc_wait_interrupt(host, NFC_SR_XFR_DONE))
- dev_err(host->dev, "something wrong, No XFR_DONE interrupt comes.\n");
-
/*
* Program and erase have their own busy handlers status, sequential
* in, and deplete1 need no delay.
@@ -1823,6 +1889,7 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
}
/* fall through */
default:
+ nfc_prepare_interrupt(host, NFC_SR_RB_EDGE);
nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
}
}
@@ -2209,6 +2276,9 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
}
}
+ nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff);
+ nfc_readl(nfc->hsmc_regs, SR); /* clear the NFC_SR */
+
nfc->is_initialized = true;
dev_info(&pdev->dev, "NFC is probed.\n");
return 0;
diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h
index 4efd117cd3a3..85b8ca6af7d2 100644
--- a/drivers/mtd/nand/atmel_nand_nfc.h
+++ b/drivers/mtd/nand/atmel_nand_nfc.h
@@ -37,6 +37,10 @@
#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */
#define NFC_SR_XFR_DONE (1 << 16)
#define NFC_SR_CMD_DONE (1 << 17)
+#define NFC_SR_DTOE (1 << 20)
+#define NFC_SR_UNDEF (1 << 21)
+#define NFC_SR_AWB (1 << 22)
+#define NFC_SR_ASE (1 << 23)
#define NFC_SR_RB_EDGE (1 << 24)
#define ATMEL_HSMC_NFC_IER 0x0c
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 722898aea7a6..871c4f712654 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -830,34 +830,10 @@ out_err:
return err;
}
-/* PM Support */
-#ifdef CONFIG_PM
-
-static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
-{
- struct bf5xx_nand_info *info = platform_get_drvdata(dev);
-
- return 0;
-}
-
-static int bf5xx_nand_resume(struct platform_device *dev)
-{
- struct bf5xx_nand_info *info = platform_get_drvdata(dev);
-
- return 0;
-}
-
-#else
-#define bf5xx_nand_suspend NULL
-#define bf5xx_nand_resume NULL
-#endif
-
/* driver device registration */
static struct platform_driver bf5xx_nand_driver = {
.probe = bf5xx_nand_probe,
.remove = bf5xx_nand_remove,
- .suspend = bf5xx_nand_suspend,
- .resume = bf5xx_nand_resume,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 9f2012a3e764..0b071a3136a2 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -473,7 +473,7 @@ static void detect_partition_feature(struct denali_nand_info *denali)
static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
{
uint16_t status = PASS;
- uint32_t id_bytes[5], addr;
+ uint32_t id_bytes[8], addr;
uint8_t i, maf_id, device_id;
dev_dbg(denali->dev,
@@ -488,7 +488,7 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
addr = (uint32_t)MODE_11 | BANK(denali->flash_bank);
index_addr(denali, (uint32_t)addr | 0, 0x90);
index_addr(denali, (uint32_t)addr | 1, 0);
- for (i = 0; i < 5; i++)
+ for (i = 0; i < 8; i++)
index_addr_read_data(denali, addr | 2, &id_bytes[i]);
maf_id = id_bytes[0];
device_id = id_bytes[1];
@@ -1276,7 +1276,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
addr = (uint32_t)MODE_11 | BANK(denali->flash_bank);
index_addr(denali, (uint32_t)addr | 0, 0x90);
index_addr(denali, (uint32_t)addr | 1, 0);
- for (i = 0; i < 5; i++) {
+ for (i = 0; i < 8; i++) {
index_addr_read_data(denali,
(uint32_t)addr | 2,
&id);
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index f638cd8077ca..959cb9b70310 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -285,9 +285,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
geo->ecc_strength = get_ecc_strength(this);
if (!gpmi_check_ecc(this)) {
dev_err(this->dev,
- "We can not support this nand chip."
- " Its required ecc strength(%d) is beyond our"
- " capability(%d).\n", geo->ecc_strength,
+ "required ecc strength of the NAND chip: %d is not supported by the GPMI controller (%d)\n",
+ geo->ecc_strength,
this->devdata->bch_max_ecc_strength);
return -EINVAL;
}
@@ -1082,6 +1081,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
int first, last, marker_pos;
int ecc_parity_size;
int col = 0;
+ int old_swap_block_mark = this->swap_block_mark;
/* The size of ECC parity */
ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
@@ -1090,17 +1090,21 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
first = offs / size;
last = (offs + len - 1) / size;
- /*
- * Find the chunk which contains the Block Marker. If this chunk is
- * in the range of [first, last], we have to read out the whole page.
- * Why? since we had swapped the data at the position of Block Marker
- * to the metadata which is bound with the chunk 0.
- */
- marker_pos = geo->block_mark_byte_offset / size;
- if (last >= marker_pos && first <= marker_pos) {
- dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
+ if (this->swap_block_mark) {
+ /*
+ * Find the chunk which contains the Block Marker.
+ * If this chunk is in the range of [first, last],
+ * we have to read out the whole page.
+ * Why? since we had swapped the data at the position of Block
+ * Marker to the metadata which is bound with the chunk 0.
+ */
+ marker_pos = geo->block_mark_byte_offset / size;
+ if (last >= marker_pos && first <= marker_pos) {
+ dev_dbg(this->dev,
+ "page:%d, first:%d, last:%d, marker at:%d\n",
page, first, last, marker_pos);
- return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+ return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+ }
}
meta = geo->metadata_size;
@@ -1146,7 +1150,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
this->bch_geometry = old_geo;
- this->swap_block_mark = true;
+ this->swap_block_mark = old_swap_block_mark;
return max_bitflips;
}
@@ -1180,7 +1184,7 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
/* Handle block mark swapping. */
block_mark_swapping(this,
- (void *) payload_virt, (void *) auxiliary_virt);
+ (void *)payload_virt, (void *)auxiliary_virt);
} else {
/*
* If control arrives here, we're not doing block mark swapping,
@@ -1310,10 +1314,10 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
/*
* Now, we want to make sure the block mark is correct. In the
- * Swapping/Raw case, we already have it. Otherwise, we need to
- * explicitly read it.
+ * non-transcribing case (!GPMI_IS_MX23()), we already have it.
+ * Otherwise, we need to explicitly read it.
*/
- if (!this->swap_block_mark) {
+ if (GPMI_IS_MX23(this)) {
/* Read the block mark into the first byte of the OOB buffer. */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
chip->oob_poi[0] = chip->read_byte(mtd);
@@ -1354,7 +1358,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
chipnr = (int)(ofs >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
- column = this->swap_block_mark ? mtd->writesize : 0;
+ column = !GPMI_IS_MX23(this) ? mtd->writesize : 0;
/* Write the block mark. */
block_mark = this->data_buffer_dma;
@@ -1597,8 +1601,9 @@ static int mx23_boot_init(struct gpmi_nand_data *this)
dev_dbg(dev, "Transcribing mark in block %u\n", block);
ret = chip->block_markbad(mtd, byte);
if (ret)
- dev_err(dev, "Failed to mark block bad with "
- "ret %d\n", ret);
+ dev_err(dev,
+ "Failed to mark block bad with ret %d\n",
+ ret);
}
}
@@ -1649,9 +1654,6 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
struct bch_geometry *bch_geo = &this->bch_geometry;
int ret;
- /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
- this->swap_block_mark = !GPMI_IS_MX23(this);
-
/* Set up the medium geometry */
ret = gpmi_set_geometry(this);
if (ret)
@@ -1715,9 +1717,20 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
chip->badblock_pattern = &gpmi_bbt_descr;
chip->block_markbad = gpmi_block_markbad;
chip->options |= NAND_NO_SUBPAGE_WRITE;
- if (of_get_nand_on_flash_bbt(this->dev->of_node))
+
+ /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
+ this->swap_block_mark = !GPMI_IS_MX23(this);
+
+ if (of_get_nand_on_flash_bbt(this->dev->of_node)) {
chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+ if (of_property_read_bool(this->dev->of_node,
+ "fsl,no-blockmark-swap"))
+ this->swap_block_mark = false;
+ }
+ dev_dbg(this->dev, "Blockmark swapping %sabled\n",
+ this->swap_block_mark ? "en" : "dis");
+
/*
* Allocate a temporary DMA buffer for reading ID in the
* nand_scan_ident().
@@ -1760,16 +1773,16 @@ err_out:
static const struct of_device_id gpmi_nand_id_table[] = {
{
.compatible = "fsl,imx23-gpmi-nand",
- .data = (void *)&gpmi_devdata_imx23,
+ .data = &gpmi_devdata_imx23,
}, {
.compatible = "fsl,imx28-gpmi-nand",
- .data = (void *)&gpmi_devdata_imx28,
+ .data = &gpmi_devdata_imx28,
}, {
.compatible = "fsl,imx6q-gpmi-nand",
- .data = (void *)&gpmi_devdata_imx6q,
+ .data = &gpmi_devdata_imx6q,
}, {
.compatible = "fsl,imx6sx-gpmi-nand",
- .data = (void *)&gpmi_devdata_imx6sx,
+ .data = &gpmi_devdata_imx6sx,
}, {}
};
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c
index 687478c9f09c..7335346dc126 100644
--- a/drivers/mtd/nand/lpc32xx_mlc.c
+++ b/drivers/mtd/nand/lpc32xx_mlc.c
@@ -721,12 +721,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
nand_chip->bbt_td = &lpc32xx_nand_bbt;
nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror;
- /* bitflip_threshold's default is defined as ecc_strength anyway.
- * Unfortunately, it is set only later at add_mtd_device(). Meanwhile
- * being 0, it causes bad block table scanning errors in
- * nand_scan_tail(), so preparing it here. */
- mtd->bitflip_threshold = nand_chip->ecc.strength;
-
if (use_dma) {
res = lpc32xx_dma_setup(host);
if (res) {
diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c
index 53a6742e3da3..8caef28e0756 100644
--- a/drivers/mtd/nand/lpc32xx_slc.c
+++ b/drivers/mtd/nand/lpc32xx_slc.c
@@ -840,12 +840,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
chip->ecc.strength = 1;
chip->ecc.hwctl = lpc32xx_nand_ecc_enable;
- /* bitflip_threshold's default is defined as ecc_strength anyway.
- * Unfortunately, it is set only later at add_mtd_device(). Meanwhile
- * being 0, it causes bad block table scanning errors in
- * nand_scan_tail(), so preparing it here already. */
- mtd->bitflip_threshold = chip->ecc.strength;
-
/*
* Allocate a large enough buffer for a single huge page plus
* extra space for the spare area and ECC storage area
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4f3e80c68a26..d8cdf06343fb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -488,6 +488,23 @@ static int nand_check_wp(struct mtd_info *mtd)
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure
* @ofs: offset from device start
+ *
+ * Check if the block is mark as reserved.
+ */
+static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (!chip->bbt)
+ return 0;
+ /* Return info from the table */
+ return nand_isreserved_bbt(mtd, ofs);
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area
*
@@ -4113,6 +4130,7 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->_unlock = NULL;
mtd->_suspend = nand_suspend;
mtd->_resume = nand_resume;
+ mtd->_block_isreserved = nand_block_isreserved;
mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad;
mtd->writebufsize = mtd->writesize;
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 7f0c3b4c2a4f..443fa82cde6a 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -1311,6 +1311,20 @@ int nand_default_bbt(struct mtd_info *mtd)
}
/**
+ * nand_isreserved_bbt - [NAND Interface] Check if a block is reserved
+ * @mtd: MTD device structure
+ * @offs: offset in the device
+ */
+int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_chip *this = mtd->priv;
+ int block;
+
+ block = (int)(offs >> this->bbt_erase_shift);
+ return bbt_get_entry(this, block) == BBT_BLOCK_RESERVED;
+}
+
+/**
* nand_isbad_bbt - [NAND Interface] Check if a block is bad
* @mtd: MTD device structure
* @offs: offset in the device
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
new file mode 100644
index 000000000000..8b36253420fa
--- /dev/null
+++ b/drivers/mtd/nand/nand_timings.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/mtd/nand.h>
+
+static const struct nand_sdr_timings onfi_sdr_timings[] = {
+ /* Mode 0 */
+ {
+ .tADL_min = 200000,
+ .tALH_min = 20000,
+ .tALS_min = 50000,
+ .tAR_min = 25000,
+ .tCEA_max = 100000,
+ .tCEH_min = 20000,
+ .tCH_min = 20000,
+ .tCHZ_max = 100000,
+ .tCLH_min = 20000,
+ .tCLR_min = 20000,
+ .tCLS_min = 50000,
+ .tCOH_min = 0,
+ .tCS_min = 70000,
+ .tDH_min = 20000,
+ .tDS_min = 40000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 10000,
+ .tITC_max = 1000000,
+ .tRC_min = 100000,
+ .tREA_max = 40000,
+ .tREH_min = 30000,
+ .tRHOH_min = 0,
+ .tRHW_min = 200000,
+ .tRHZ_max = 200000,
+ .tRLOH_min = 0,
+ .tRP_min = 50000,
+ .tRST_max = 250000000000,
+ .tWB_max = 200000,
+ .tRR_min = 40000,
+ .tWC_min = 100000,
+ .tWH_min = 30000,
+ .tWHR_min = 120000,
+ .tWP_min = 50000,
+ .tWW_min = 100000,
+ },
+ /* Mode 1 */
+ {
+ .tADL_min = 100000,
+ .tALH_min = 10000,
+ .tALS_min = 25000,
+ .tAR_min = 10000,
+ .tCEA_max = 45000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 25000,
+ .tCOH_min = 15000,
+ .tCS_min = 35000,
+ .tDH_min = 10000,
+ .tDS_min = 20000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 50000,
+ .tREA_max = 30000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 25000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 45000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 25000,
+ .tWW_min = 100000,
+ },
+ /* Mode 2 */
+ {
+ .tADL_min = 100000,
+ .tALH_min = 10000,
+ .tALS_min = 15000,
+ .tAR_min = 10000,
+ .tCEA_max = 30000,
+ .tCEH_min = 20000,
+ .tCH_min = 10000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 10000,
+ .tCLR_min = 10000,
+ .tCLS_min = 15000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 15000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 35000,
+ .tREA_max = 25000,
+ .tREH_min = 15000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tRP_min = 17000,
+ .tWC_min = 35000,
+ .tWH_min = 15000,
+ .tWHR_min = 80000,
+ .tWP_min = 17000,
+ .tWW_min = 100000,
+ },
+ /* Mode 3 */
+ {
+ .tADL_min = 100000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 50000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 25000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 30000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 0,
+ .tRP_min = 15000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 30000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 15000,
+ .tWW_min = 100000,
+ },
+ /* Mode 4 */
+ {
+ .tADL_min = 70000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 20000,
+ .tDH_min = 5000,
+ .tDS_min = 10000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 25000,
+ .tREA_max = 20000,
+ .tREH_min = 10000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 12000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 25000,
+ .tWH_min = 10000,
+ .tWHR_min = 80000,
+ .tWP_min = 12000,
+ .tWW_min = 100000,
+ },
+ /* Mode 5 */
+ {
+ .tADL_min = 70000,
+ .tALH_min = 5000,
+ .tALS_min = 10000,
+ .tAR_min = 10000,
+ .tCEA_max = 25000,
+ .tCEH_min = 20000,
+ .tCH_min = 5000,
+ .tCHZ_max = 30000,
+ .tCLH_min = 5000,
+ .tCLR_min = 10000,
+ .tCLS_min = 10000,
+ .tCOH_min = 15000,
+ .tCS_min = 15000,
+ .tDH_min = 5000,
+ .tDS_min = 7000,
+ .tFEAT_max = 1000000,
+ .tIR_min = 0,
+ .tITC_max = 1000000,
+ .tRC_min = 20000,
+ .tREA_max = 16000,
+ .tREH_min = 7000,
+ .tRHOH_min = 15000,
+ .tRHW_min = 100000,
+ .tRHZ_max = 100000,
+ .tRLOH_min = 5000,
+ .tRP_min = 10000,
+ .tRR_min = 20000,
+ .tRST_max = 500000000,
+ .tWB_max = 100000,
+ .tWC_min = 20000,
+ .tWH_min = 7000,
+ .tWHR_min = 80000,
+ .tWP_min = 10000,
+ .tWW_min = 100000,
+ },
+};
+
+/**
+ * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND
+ * timings according to the given ONFI timing mode
+ * @mode: ONFI timing mode
+ */
+const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
+{
+ if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
+ return ERR_PTR(-EINVAL);
+
+ return &onfi_sdr_timings[mode];
+}
+EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 79acbb8691b5..6b97bf17ce5d 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -208,10 +208,10 @@ static void s3c2410_nand_clk_set_state(struct s3c2410_nand_info *info,
if (info->clk_state == CLOCK_ENABLE) {
if (new_state != CLOCK_ENABLE)
- clk_disable(info->clk);
+ clk_disable_unprepare(info->clk);
} else {
if (new_state == CLOCK_ENABLE)
- clk_enable(info->clk);
+ clk_prepare_enable(info->clk);
}
info->clk_state = new_state;