diff options
Diffstat (limited to 'drivers/mtd/nand/raw/mtk_nand.c')
-rw-r--r-- | drivers/mtd/nand/raw/mtk_nand.c | 96 |
1 files changed, 72 insertions, 24 deletions
diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index b17619f30b1b..373d47d1ba4c 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT /* * MTK NAND Flash controller driver. * Copyright (C) 2016 MediaTek Inc. * Authors: Xiaolei Li <xiaolei.li@mediatek.com> * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/platform_device.h> @@ -87,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) @@ -158,6 +154,8 @@ struct mtk_nfc { struct list_head chips; u8 *buffer; + + unsigned long assigned_cs; }; /* @@ -508,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)) @@ -544,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 * ------------------------------------- @@ -843,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; } @@ -875,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; @@ -942,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); @@ -1323,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; } @@ -1597,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"); |