summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw/mtk_nand.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-13 15:42:44 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-13 15:42:44 -0700
commit3f06962273e73c5f7a651726b191d531cadef788 (patch)
tree370219bd166ac6dda105901dd316a54c2f9ac3d0 /drivers/mtd/nand/raw/mtk_nand.c
parent2260840592fbed5be98ca03c97eb8172941f27ac (diff)
parent46ce10df799fb0647a9c0e3f793e66463a8d6773 (diff)
downloadlinux-3f06962273e73c5f7a651726b191d531cadef788.tar.gz
linux-3f06962273e73c5f7a651726b191d531cadef788.tar.bz2
linux-3f06962273e73c5f7a651726b191d531cadef788.zip
Merge tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux
Pull MTD updates from Miquel Raynal: "This contains the following changes for MTD: MTD core changes: - New Hyperbus framework - New _is_locked (concat) implementation - Various cleanups NAND core changes: - use longest matching pattern in ->exec_op() default parser - export NAND operation tracer - add flag to indicate panic_write in MTD - use kzalloc() instead of kmalloc() and memset() Raw NAND controller drivers changes: - brcmnand: - fix BCH ECC layout for large page NAND parts - fallback to detected ecc-strength, ecc-step-size - when oops in progress use pio and interrupt polling - code refactor code to introduce helper functions - add support for v7.3 controller - FSMC: - use nand_op_trace for operation tracing - GPMI: - move all driver code into single file - various cleanups (including dmaengine changes) - use runtime PM to manage clocks - implement exec_op - MTK: - correct low level time calculation of r/w cycle - improve data sampling timing for read cycle - add validity check for CE# pin setting - fix wrongly assigned OOB buffer pointer issue - re-license MTK NAND driver as Dual MIT/GPL - STM32: - manage the get_irq error case - increase DMA completion timeouts Raw NAND chips drivers changes: - Macronix: add read-retry support Onenand driver changes: - add support for 8Gb datasize chips - avoid fall-through warnings SPI-NAND changes: - define macros for page-read ops with three-byte addresses - add support for two-byte device IDs and then for GigaDevice GD5F1GQ4UFxxG - add initial support for Paragon PN26G0xA - handle the case where the last page read has bitflips SPI-NOR core changes: - add support for the mt25ql02g and w25q16jv flashes - print error in case of jedec read id fails - is25lp256: add post BFPT fix to correct the addr_width SPI NOR controller drivers changes: - intel-spi: Add support for Intel Elkhart Lake SPI serial flash - smt32: remove the driver as the driver was replaced by spi-stm32-qspi.c - cadence-quadspi: add reset control" * tag 'mtd/for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (60 commits) mtd: concat: implement _is_locked mtd operation mtd: concat: refactor concat_lock/concat_unlock mtd: abi: do not use C++ style comments in uapi header mtd: afs: remove unneeded NULL check mtd: rawnand: stm32_fmc2: increase DMA completion timeouts mtd: rawnand: Use kzalloc() instead of kmalloc() and memset() mtd: hyperbus: Add driver for TI's HyperBus memory controller mtd: spinand: read returns badly if the last page has bitflips mtd: spinand: Add initial support for Paragon PN26G0xA mtd: rawnand: mtk: Re-license MTK NAND driver as Dual MIT/GPL mtd: rawnand: gpmi: remove double assignment to block_size dt-bindings: mtd: brcmnand: Add brcmnand, brcmnand-v7.3 support mtd: rawnand: brcmnand: Add support for v7.3 controller mtd: rawnand: brcmnand: Refactored code to introduce helper functions mtd: rawnand: brcmnand: When oops in progress use pio and interrupt polling mtd: Add flag to indicate panic_write mtd: rawnand: Add Macronix NAND read retry support mtd: onenand: Avoid fall-through warnings mtd: spinand: Add support for GigaDevice GD5F1GQ4UFxxG mtd: spinand: Add support for two-byte device IDs ...
Diffstat (limited to 'drivers/mtd/nand/raw/mtk_nand.c')
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c88
1 files changed, 72 insertions, 16 deletions
diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c
index dceff28c9a31..373d47d1ba4c 100644
--- a/drivers/mtd/nand/raw/mtk_nand.c
+++ b/drivers/mtd/nand/raw/mtk_nand.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* MTK NAND Flash controller driver.
* Copyright (C) 2016 MediaTek Inc.
@@ -79,6 +79,10 @@
#define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2)
#define NFI_FDM_MAX_SIZE (8)
#define NFI_FDM_MIN_SIZE (1)
+#define NFI_DEBUG_CON1 (0x220)
+#define STROBE_MASK GENMASK(4, 3)
+#define STROBE_SHIFT (3)
+#define MAX_STROBE_DLY (3)
#define NFI_MASTER_STA (0x224)
#define MASTER_STA_MASK (0x0FFF)
#define NFI_EMPTY_THRESH (0x23C)
@@ -150,6 +154,8 @@ struct mtk_nfc {
struct list_head chips;
u8 *buffer;
+
+ unsigned long assigned_cs;
};
/*
@@ -500,7 +506,8 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
{
struct mtk_nfc *nfc = nand_get_controller_data(chip);
const struct nand_sdr_timings *timings;
- u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt;
+ u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst = 0, trlt = 0;
+ u32 temp, tsel = 0;
timings = nand_get_sdr_timings(conf);
if (IS_ERR(timings))
@@ -536,14 +543,53 @@ static int mtk_nfc_setup_data_interface(struct nand_chip *chip, int csline,
twh = DIV_ROUND_UP(twh * rate, 1000000) - 1;
twh &= 0xf;
- twst = timings->tWP_min / 1000;
+ /* Calculate real WE#/RE# hold time in nanosecond */
+ temp = (twh + 1) * 1000000 / rate;
+ /* nanosecond to picosecond */
+ temp *= 1000;
+
+ /*
+ * WE# low level time should be expaned to meet WE# pulse time
+ * and WE# cycle time at the same time.
+ */
+ if (temp < timings->tWC_min)
+ twst = timings->tWC_min - temp;
+ twst = max(timings->tWP_min, twst) / 1000;
twst = DIV_ROUND_UP(twst * rate, 1000000) - 1;
twst &= 0xf;
- trlt = max(timings->tREA_max, timings->tRP_min) / 1000;
+ /*
+ * RE# low level time should be expaned to meet RE# pulse time
+ * and RE# cycle time at the same time.
+ */
+ if (temp < timings->tRC_min)
+ trlt = timings->tRC_min - temp;
+ trlt = max(trlt, timings->tRP_min) / 1000;
trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1;
trlt &= 0xf;
+ /* Calculate RE# pulse time in nanosecond. */
+ temp = (trlt + 1) * 1000000 / rate;
+ /* nanosecond to picosecond */
+ temp *= 1000;
+ /*
+ * If RE# access time is bigger than RE# pulse time,
+ * delay sampling data timing.
+ */
+ if (temp < timings->tREA_max) {
+ tsel = timings->tREA_max / 1000;
+ tsel = DIV_ROUND_UP(tsel * rate, 1000000);
+ tsel -= (trlt + 1);
+ if (tsel > MAX_STROBE_DLY) {
+ trlt += tsel - MAX_STROBE_DLY;
+ tsel = MAX_STROBE_DLY;
+ }
+ }
+ temp = nfi_readl(nfc, NFI_DEBUG_CON1);
+ temp &= ~STROBE_MASK;
+ temp |= tsel << STROBE_SHIFT;
+ nfi_writel(nfc, temp, NFI_DEBUG_CON1);
+
/*
* ACCON: access timing control register
* -------------------------------------
@@ -835,19 +881,21 @@ static int mtk_nfc_write_oob_std(struct nand_chip *chip, int page)
return mtk_nfc_write_page_raw(chip, NULL, 1, page);
}
-static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors)
+static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 start,
+ u32 sectors)
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct mtk_nfc *nfc = nand_get_controller_data(chip);
struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
struct mtk_ecc_stats stats;
+ u32 reg_size = mtk_nand->fdm.reg_size;
int rc, i;
rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
if (rc) {
memset(buf, 0xff, sectors * chip->ecc.size);
for (i = 0; i < sectors; i++)
- memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size);
+ memset(oob_ptr(chip, start + i), 0xff, reg_size);
return 0;
}
@@ -867,7 +915,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
u32 spare = mtk_nand->spare_per_sector;
u32 column, sectors, start, end, reg;
dma_addr_t addr;
- int bitflips;
+ int bitflips = 0;
size_t len;
u8 *buf;
int rc;
@@ -934,14 +982,11 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
if (rc < 0) {
dev_err(nfc->dev, "subpage done timeout\n");
bitflips = -EIO;
- } else {
- bitflips = 0;
- if (!raw) {
- rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE);
- bitflips = rc < 0 ? -ETIMEDOUT :
- mtk_nfc_update_ecc_stats(mtd, buf, sectors);
- mtk_nfc_read_fdm(chip, start, sectors);
- }
+ } else if (!raw) {
+ rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE);
+ bitflips = rc < 0 ? -ETIMEDOUT :
+ mtk_nfc_update_ecc_stats(mtd, buf, start, sectors);
+ mtk_nfc_read_fdm(chip, start, sectors);
}
dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
@@ -1315,6 +1360,17 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
dev_err(dev, "reg property failure : %d\n", ret);
return ret;
}
+
+ if (tmp >= MTK_NAND_MAX_NSELS) {
+ dev_err(dev, "invalid CS: %u\n", tmp);
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+ dev_err(dev, "CS %u already assigned\n", tmp);
+ return -EINVAL;
+ }
+
chip->sels[i] = tmp;
}
@@ -1589,6 +1645,6 @@ static struct platform_driver mtk_nfc_driver = {
module_platform_driver(mtk_nfc_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual MIT/GPL");
MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");