summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-06-08 10:39:20 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-06-08 10:39:20 -0700
commitf4e70c2e5f1406e715f6359ae341e76e5004fb98 (patch)
tree79622d866d0cb8bdb494718e11d458770aee9713 /drivers/mtd/nand
parentea125dedbc14b305307889c40d74d564c4419851 (diff)
parentb771327a4530e7ff05ae173d4903cd70357bb803 (diff)
Merge tag 'mtd/for-4.18' of git://git.infradead.org/linux-mtd
Pull MTD updates from Boris Brezillon: "Core changes: - Add a sysfs attribute to expose available OOB size Driver changes: - Remove HAS_DMA dependency on various drivers - Use dev_get_drvdata() instead of platform_get_drvdata() in docg3 - Replace msleep by usleep_range() in the dataflash driver - Avoid VLA usage in nftl layers - Remove useless .owner assignment in pismo - Fix various issues in the CFI driver - Improve TRX partition handling expose a DT compat for this part parser - Clarify OFFSET_CONTINUOUS meaning NAND core changes: - Add Miquel as a NAND maintainer - Add access mode to the nand_page_io_req struct - Fix kernel-doc in rawnand.h - Support bit-wise majority to recover from corrupted ONFI parameter pages - Stop checking FAIL bit after a SET_FEATURES, as documented in the ONFI spec Raw NAND Driver changes: - Fix and cleanup the error path of many NAND controller drivers - GPMI: + Cleanup/simplification of a few aspects in the driver + Take ECC setup specified in the DT into account - sunxi: remove support for GPIO-based R/B polling - MTK: + Use of_device_get_match_data() instead of of_match_device() + Add an entry in MAINTAINERS for this driver + Fix nand-ecc-step-size and nand-ecc-strength description in the DT bindings doc - fsl_ifc: fix ->cmdfunc() to read more than one ONFI parameter page OneNAND driver changes: - samsung: use dev_get_drvdata() instead of platform_get_drvdata() SPI NOR core changes: - Add support for a bunch of SPI NOR chips - Clear EAR reg when switching to 3-byte addressing mode on Winbond chips SPI NOR controller driver changes: - cadence: Add DMA support for direct mode reads - hisi: Prefix a few functions with hisi_ - intel: + Mark the driver as "dangerous" in Kconfig + Fix atomic sequence handling + Pass a 40us delay (instead of 0us) to readl_poll_timeout() - fsl: + fix a typo in a function name + add support for IP variants embedded in the ls2080a and ls1080a SoCs - stm32: request exclusive control of the reset line" * tag 'mtd/for-4.18' of git://git.infradead.org/linux-mtd: (66 commits) mtd: nand: Pass mode information to nand_page_io_req mtd: cfi_cmdset_0002: Change erase one block to enable XIP once mtd: cfi_cmdset_0002: Change erase functions to check chip good only mtd: cfi_cmdset_0002: Change erase functions to retry for error mtd: cfi_cmdset_0002: Change definition naming to retry write operation mtd: cfi_cmdset_0002: Change write buffer to check correct value mtd: cmdlinepart: Update comment for introduction of OFFSET_CONTINUOUS mtd: bcm47xxpart: add of_match_table with a new DT binding dt-bindings: mtd: document Broadcom's BCM47xx partitions mtd: spi-nor: Add support for EN25QH32 mtd: spi-nor: Add support for is25wp series chips mtd: spi-nor: Add Winbond w25q32jv support mtd: spi-nor: fsl-quadspi: add support for ls2080a/ls1080a mtd: spi-nor: stm32-quadspi: explicitly request exclusive reset control mtd: spi-nor: intel: provide a range for poll_timout mtd: spi-nor: fsl-quadspi: fix api naming typo _init_ahb_read mtd: spi-nor: intel-spi: Explicitly mark the driver as dangerous in Kconfig mtd: spi-nor: intel-spi: Fix atomic sequence handling mtd: rawnand: Do not check FAIL bit when executing a SET_FEATURES op mtd: rawnand: use bit-wise majority to recover the ONFI param page ...
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/onenand/samsung.c6
-rw-r--r--drivers/mtd/nand/raw/Kconfig8
-rw-r--r--drivers/mtd/nand/raw/davinci_nand.c25
-rw-r--r--drivers/mtd/nand/raw/diskonchip.c4
-rw-r--r--drivers/mtd/nand/raw/fsl_elbc_nand.c13
-rw-r--r--drivers/mtd/nand/raw/fsl_ifc_nand.c29
-rw-r--r--drivers/mtd/nand/raw/fsmc_nand.c27
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c54
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c188
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h25
-rw-r--r--drivers/mtd/nand/raw/hisi504_nand.c35
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_mlc.c38
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c26
-rw-r--r--drivers/mtd/nand/raw/mtk_ecc.c7
-rw-r--r--drivers/mtd/nand/raw/mtk_nand.c10
-rw-r--r--drivers/mtd/nand/raw/nand_base.c89
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c91
17 files changed, 269 insertions, 406 deletions
diff --git a/drivers/mtd/nand/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c
index 2e9d076e445a..4cce4c0311ca 100644
--- a/drivers/mtd/nand/onenand/samsung.c
+++ b/drivers/mtd/nand/onenand/samsung.c
@@ -958,8 +958,7 @@ static int s3c_onenand_remove(struct platform_device *pdev)
static int s3c_pm_ops_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = dev_get_drvdata(dev);
struct onenand_chip *this = mtd->priv;
this->wait(mtd, FL_PM_SUSPENDED);
@@ -968,8 +967,7 @@ static int s3c_pm_ops_suspend(struct device *dev)
static int s3c_pm_ops_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mtd_info *mtd = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = dev_get_drvdata(dev);
struct onenand_chip *this = mtd->priv;
this->unlock_all(mtd);
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 19a2b283fbbe..6871ff0fd300 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -46,7 +46,7 @@ config MTD_NAND_DENALI
config MTD_NAND_DENALI_PCI
tristate "Support Denali NAND controller on Intel Moorestown"
select MTD_NAND_DENALI
- depends on HAS_DMA && PCI
+ depends on PCI
help
Enable the driver for NAND flash on Intel Moorestown, using the
Denali NAND controller core.
@@ -152,7 +152,6 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_TANGO
tristate "NAND Flash support for Tango chips"
depends on ARCH_TANGO || COMPILE_TEST
- depends on HAS_DMA
help
Enables the NAND Flash controller on Tango chips.
@@ -285,7 +284,7 @@ config MTD_NAND_MARVELL
tristate "NAND controller support on Marvell boards"
depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
COMPILE_TEST
- depends on HAS_IOMEM && HAS_DMA
+ depends on HAS_IOMEM
help
This enables the NAND flash controller driver for Marvell boards,
including:
@@ -447,7 +446,6 @@ config MTD_NAND_SH_FLCTL
tristate "Support for NAND on Renesas SuperH FLCTL"
depends on SUPERH || COMPILE_TEST
depends on HAS_IOMEM
- depends on HAS_DMA
help
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL.
@@ -515,7 +513,6 @@ config MTD_NAND_SUNXI
config MTD_NAND_HISI504
tristate "Support for NAND controller on Hisilicon SoC Hip04"
depends on ARCH_HISI || COMPILE_TEST
- depends on HAS_DMA
help
Enables support for NAND controller on Hisilicon SoC Hip04.
@@ -529,7 +526,6 @@ config MTD_NAND_QCOM
config MTD_NAND_MTK
tristate "Support for NAND controller on MTK SoCs"
depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_DMA
help
Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c
index 0f09518d980f..7255a0d94374 100644
--- a/drivers/mtd/nand/raw/davinci_nand.c
+++ b/drivers/mtd/nand/raw/davinci_nand.c
@@ -27,7 +27,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
-#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
@@ -55,7 +54,6 @@ struct davinci_nand_info {
struct nand_chip chip;
struct device *dev;
- struct clk *clk;
bool is_readmode;
@@ -703,22 +701,6 @@ static int nand_davinci_probe(struct platform_device *pdev)
/* Use board-specific ECC config */
info->chip.ecc.mode = pdata->ecc_mode;
- ret = -EINVAL;
-
- info->clk = devm_clk_get(&pdev->dev, "aemif");
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(info->clk);
- if (ret < 0) {
- dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
- ret);
- goto err_clk_enable;
- }
-
spin_lock_irq(&davinci_nand_lock);
/* put CSxNAND into NAND mode */
@@ -732,7 +714,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL);
if (ret < 0) {
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
- goto err;
+ return ret;
}
switch (info->chip.ecc.mode) {
@@ -838,9 +820,6 @@ err_cleanup_nand:
nand_cleanup(&info->chip);
err:
- clk_disable_unprepare(info->clk);
-
-err_clk_enable:
spin_lock_irq(&davinci_nand_lock);
if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME)
ecc4_busy = false;
@@ -859,8 +838,6 @@ static int nand_davinci_remove(struct platform_device *pdev)
nand_release(nand_to_mtd(&info->chip));
- clk_disable_unprepare(info->clk);
-
return 0;
}
diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c
index 2b7b2b982b77..8d10061abb4b 100644
--- a/drivers/mtd/nand/raw/diskonchip.c
+++ b/drivers/mtd/nand/raw/diskonchip.c
@@ -1480,12 +1480,12 @@ static int __init doc_probe(unsigned long physadr)
WriteDOC(tmp, virtadr, Mplus_DOCControl);
WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
- mdelay(1);
+ usleep_range(1000, 2000);
/* Enable the Millennium Plus ASIC */
tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
WriteDOC(tmp, virtadr, Mplus_DOCControl);
WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
- mdelay(1);
+ usleep_range(1000, 2000);
ChipID = ReadDOC(virtadr, ChipID);
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c
index d28df991c73c..51f0b340bc0d 100644
--- a/drivers/mtd/nand/raw/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c
@@ -813,8 +813,6 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
struct mtd_info *mtd = nand_to_mtd(&priv->chip);
- nand_release(mtd);
-
kfree(mtd->name);
if (priv->vbase)
@@ -926,15 +924,20 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
- mtd_device_parse_register(mtd, part_probe_types, NULL,
- NULL, 0);
+ ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+ if (ret)
+ goto cleanup_nand;
pr_info("eLBC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res.start, priv->bank);
+
return 0;
+cleanup_nand:
+ nand_cleanup(&priv->chip);
err:
fsl_elbc_chip_remove(priv);
+
return ret;
}
@@ -942,7 +945,9 @@ static int fsl_elbc_nand_remove(struct platform_device *pdev)
{
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+ nand_release(mtd);
fsl_elbc_chip_remove(priv);
mutex_lock(&fsl_elbc_nand_mutex);
diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae0224078..382b67e97174 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+ /*
+ * For READID, read 8 bytes that are currently used.
+ * For PARAM, read all 3 copies of 256-bytes pages.
+ */
+ int len = 8;
int timing = IFC_FIR_OP_RB;
- if (command == NAND_CMD_PARAM)
+ if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+ len = 256 * 3;
+ }
ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
&ifc->ifc_nand.nand_fcr0);
ifc_out32(column, &ifc->ifc_nand.row3);
- /*
- * although currently it's 8 bytes for READID, we always read
- * the maximum 256 bytes(for PARAM)
- */
- ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
- ifc_nand_ctrl->read_bytes = 256;
+ ifc_out32(len, &ifc->ifc_nand.nand_fbcr);
+ ifc_nand_ctrl->read_bytes = len;
set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
@@ -924,8 +927,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
{
struct mtd_info *mtd = nand_to_mtd(&priv->chip);
- nand_release(mtd);
-
kfree(mtd->name);
if (priv->vbase)
@@ -1059,21 +1060,29 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
- mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+ ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
+ if (ret)
+ goto cleanup_nand;
dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res.start, priv->bank);
+
return 0;
+cleanup_nand:
+ nand_cleanup(&priv->chip);
err:
fsl_ifc_chip_remove(priv);
+
return ret;
}
static int fsl_ifc_nand_remove(struct platform_device *dev)
{
struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev);
+ struct mtd_info *mtd = nand_to_mtd(&priv->chip);
+ nand_release(mtd);
fsl_ifc_chip_remove(priv);
mutex_lock(&fsl_ifc_nand_mutex);
diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c
index 28c48dcc514e..f4a5a317d4ae 100644
--- a/drivers/mtd/nand/raw/fsmc_nand.c
+++ b/drivers/mtd/nand/raw/fsmc_nand.c
@@ -1022,12 +1022,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
host->read_dma_chan = dma_request_channel(mask, filter, NULL);
if (!host->read_dma_chan) {
dev_err(&pdev->dev, "Unable to get read dma channel\n");
- goto err_req_read_chnl;
+ goto disable_clk;
}
host->write_dma_chan = dma_request_channel(mask, filter, NULL);
if (!host->write_dma_chan) {
dev_err(&pdev->dev, "Unable to get write dma channel\n");
- goto err_req_write_chnl;
+ goto release_dma_read_chan;
}
}
@@ -1050,7 +1050,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
ret = nand_scan_ident(mtd, 1, NULL);
if (ret) {
dev_err(&pdev->dev, "No NAND Device found!\n");
- goto err_scan_ident;
+ goto release_dma_write_chan;
}
if (AMBA_REV_BITS(host->pid) >= 8) {
@@ -1065,7 +1065,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
ret = -EINVAL;
- goto err_probe;
+ goto release_dma_write_chan;
}
mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
@@ -1090,7 +1090,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "Unsupported ECC mode!\n");
- goto err_probe;
+ goto release_dma_write_chan;
}
/*
@@ -1110,7 +1110,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
"No oob scheme defined for oobsize %d\n",
mtd->oobsize);
ret = -EINVAL;
- goto err_probe;
+ goto release_dma_write_chan;
}
}
}
@@ -1118,26 +1118,29 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
/* Second stage of scan to fill MTD data-structures */
ret = nand_scan_tail(mtd);
if (ret)
- goto err_probe;
+ goto release_dma_write_chan;
mtd->name = "nand";
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
- goto err_probe;
+ goto cleanup_nand;
platform_set_drvdata(pdev, host);
dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
+
return 0;
-err_probe:
-err_scan_ident:
+cleanup_nand:
+ nand_cleanup(nand);
+release_dma_write_chan:
if (host->mode == USE_DMA_ACCESS)
dma_release_channel(host->write_dma_chan);
-err_req_write_chnl:
+release_dma_read_chan:
if (host->mode == USE_DMA_ACCESS)
dma_release_channel(host->read_dma_chan);
-err_req_read_chnl:
+disable_clk:
clk_disable_unprepare(host->clk);
+
return ret;
}
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
index e94556705dc7..83697b8df871 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c
@@ -258,8 +258,9 @@ int bch_set_geometry(struct gpmi_nand_data *this)
unsigned int gf_len;
int ret;
- if (common_nfc_set_geometry(this))
- return !0;
+ ret = common_nfc_set_geometry(this);
+ if (ret)
+ return ret;
block_count = bch_geo->ecc_chunk_count - 1;
block_size = bch_geo->ecc_chunk_size;
@@ -544,19 +545,13 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip)
return reg & mask;
}
-static inline void set_dma_type(struct gpmi_nand_data *this,
- enum dma_ops_type type)
-{
- this->last_dma_type = this->dma_type;
- this->dma_type = type;
-}
-
int gpmi_send_command(struct gpmi_nand_data *this)
{
struct dma_chan *channel = get_dma_chan(this);
struct dma_async_tx_descriptor *desc;
struct scatterlist *sgl;
int chip = this->current_chip;
+ int ret;
u32 pio[3];
/* [1] send out the PIO words */
@@ -586,15 +581,19 @@ int gpmi_send_command(struct gpmi_nand_data *this)
return -EINVAL;
/* [3] submit the DMA */
- set_dma_type(this, DMA_FOR_COMMAND);
- return start_dma_without_bch_irq(this, desc);
+ ret = start_dma_without_bch_irq(this, desc);
+
+ dma_unmap_sg(this->dev, sgl, 1, DMA_TO_DEVICE);
+
+ return ret;
}
-int gpmi_send_data(struct gpmi_nand_data *this)
+int gpmi_send_data(struct gpmi_nand_data *this, const void *buf, int len)
{
struct dma_async_tx_descriptor *desc;
struct dma_chan *channel = get_dma_chan(this);
int chip = this->current_chip;
+ int ret;
uint32_t command_mode;
uint32_t address;
u32 pio[2];
@@ -608,7 +607,7 @@ int gpmi_send_data(struct gpmi_nand_data *this)
| BF_GPMI_CTRL0_CS(chip, this)
| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
| BF_GPMI_CTRL0_ADDRESS(address)
- | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ | BF_GPMI_CTRL0_XFER_COUNT(len);
pio[1] = 0;
desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio,
ARRAY_SIZE(pio), DMA_TRANS_NONE, 0);
@@ -616,7 +615,7 @@ int gpmi_send_data(struct gpmi_nand_data *this)
return -EINVAL;
/* [2] send DMA request */
- prepare_data_dma(this, DMA_TO_DEVICE);
+ prepare_data_dma(this, buf, len, DMA_TO_DEVICE);
desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
1, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -624,16 +623,21 @@ int gpmi_send_data(struct gpmi_nand_data *this)
return -EINVAL;
/* [3] submit the DMA */
- set_dma_type(this, DMA_FOR_WRITE_DATA);
- return start_dma_without_bch_irq(this, desc);
+ ret = start_dma_without_bch_irq(this, desc);
+
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
+
+ return ret;
}
-int gpmi_read_data(struct gpmi_nand_data *this)
+int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len)
{
struct dma_async_tx_descriptor *desc;
struct dma_chan *channel = get_dma_chan(this);
int chip = this->current_chip;
+ int ret;
u32 pio[2];
+ bool direct;
/* [1] : send PIO */
pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
@@ -641,7 +645,7 @@ int gpmi_read_data(struct gpmi_nand_data *this)
| BF_GPMI_CTRL0_CS(chip, this)
| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
- | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len);
+ | BF_GPMI_CTRL0_XFER_COUNT(len);
pio[1] = 0;
desc = dmaengine_prep_slave_sg(channel,
(struct scatterlist *)pio,
@@ -650,7 +654,7 @@ int gpmi_read_data(struct gpmi_nand_data *this)
return -EINVAL;
/* [2] : send DMA request */
- prepare_data_dma(this, DMA_FROM_DEVICE);
+ direct = prepare_data_dma(this, buf, len, DMA_FROM_DEVICE);
desc = dmaengine_prep_slave_sg(channel, &this->data_sgl,
1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -658,8 +662,14 @@ int gpmi_read_data(struct gpmi_nand_data *this)
return -EINVAL;
/* [3] : submit the DMA */
- set_dma_type(this, DMA_FOR_READ_DATA);
- return start_dma_without_bch_irq(this, desc);
+
+ ret = start_dma_without_bch_irq(this, desc);
+
+ dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
+ if (!direct)
+ memcpy(buf, this->data_buffer_dma, len);
+
+ return ret;
}
int gpmi_send_page(struct gpmi_nand_data *this,
@@ -703,7 +713,6 @@ int gpmi_send_page(struct gpmi_nand_data *this,
if (!desc)
return -EINVAL;
- set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE);
return start_dma_with_bch_irq(this, desc);
}
@@ -785,7 +794,6 @@ int gpmi_read_page(struct gpmi_nand_data *this,
return -EINVAL;
/* [4] submit the DMA */
- set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
return start_dma_with_bch_irq(this, desc);
}
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index c2597c8107a0..f6aa358a3452 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -198,17 +198,16 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this)
*
* We may have available oob space in this case.
*/
-static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
+static int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
+ unsigned int ecc_strength,
+ unsigned int ecc_step)
{
struct bch_geometry *geo = &this->bch_geometry;
struct nand_chip *chip = &this->nand;
struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int block_mark_bit_offset;
- if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
- return -EINVAL;
-
- switch (chip->ecc_step_ds) {
+ switch (ecc_step) {
case SZ_512:
geo->gf_len = 13;
break;
@@ -221,8 +220,8 @@ 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_strength = round_up(chip->ecc_strength_ds, 2);
+ geo->ecc_chunk_size = ecc_step;
+ geo->ecc_strength = round_up(ecc_strength, 2);
if (!gpmi_check_ecc(this))
return -EINVAL;
@@ -230,7 +229,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this)
if (geo->ecc_chunk_size < mtd->oobsize) {
dev_err(this->dev,
"unsupported nand chip. ecc size: %d, oob size : %d\n",
- chip->ecc_step_ds, mtd->oobsize);
+ ecc_step, mtd->oobsize);
return -EINVAL;
}
@@ -423,9 +422,20 @@ static int legacy_set_geometry(struct gpmi_nand_data *this)
int common_nfc_set_geometry(struct gpmi_nand_data *this)
{
+ struct nand_chip *chip = &this->nand;
+
+ if (chip->ecc.strength > 0 && chip->ecc.size > 0)
+ return set_geometry_by_ecc_info(this, chip->ecc.strength,
+ chip->ecc.size);
+
if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc"))
- || legacy_set_geometry(this))
- return set_geometry_by_ecc_info(this);
+ || legacy_set_geometry(this)) {
+ if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0))
+ return -EINVAL;
+
+ return set_geometry_by_ecc_info(this, chip->ecc_strength_ds,
+ chip->ecc_step_ds);
+ }
return 0;
}
@@ -437,33 +447,32 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
}
/* Can we use the upper's buffer directly for DMA? */
-void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr)
+bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len,
+ enum dma_data_direction dr)
{
struct scatterlist *sgl = &this->data_sgl;
int ret;
/* first try to map the upper buffer directly */
- if (virt_addr_valid(this->upper_buf) &&
- !object_is_on_stack(this->upper_buf)) {
- sg_init_one(sgl, this->upper_buf, this->upper_len);
+ if (virt_addr_valid(buf) && !object_is_on_stack(buf)) {
+ sg_init_one(sgl, buf, len);
ret = dma_map_sg(this->dev, sgl, 1, dr);
if (ret == 0)
goto map_fail;
- this->direct_dma_map_ok = true;
- return;
+ return true;
}
map_fail:
/* We have to use our own DMA buffer. */
- sg_init_one(sgl, this->data_buffer_dma, this->upper_len);
+ sg_init_one(sgl, this->data_buffer_dma, len);
if (dr == DMA_TO_DEVICE)
- memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len);
+ memcpy(this->data_buffer_dma, buf, len);
dma_map_sg(this->dev, sgl, 1, dr);
- this->direct_dma_map_ok = false;
+ return false;
}
/* This will be called after the DMA operation is finished. */
@@ -472,31 +481,6 @@ static void dma_irq_callback(void *param)
struct gpmi_nand_data *this = param;
struct completion *dma_c = &this->dma_done;
- switch (this->dma_type) {
- case DMA_FOR_COMMAND:
- dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE);
- break;
-
- case DMA_FOR_READ_DATA:
- dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE);
- if (this->direct_dma_map_ok == false)
- memcpy(this->upper_buf, this->data_buffer_dma,
- this->upper_len);
- break;
-
- case DMA_FOR_WRITE_DATA:
- dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE);
- break;
-
- case DMA_FOR_READ_ECC_PAGE:
- case DMA_FOR_WRITE_ECC_PAGE:
- /* We have to wait the BCH interrupt to finish. */
- break;
-
- default:
- dev_err(this->dev, "in wrong DMA operation.\n");
- }
-
complete(dma_c);
}
@@ -516,8 +500,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this,
/* Wait for the interrupt from the DMA block. */
timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000));
if (!timeout) {
- dev_err(this->dev, "DMA timeout, last DMA :%d\n",
- this->last_dma_type);
+ dev_err(this->dev, "DMA timeout, last DMA\n");
gpmi_dump_info(this);
return -ETIMEDOUT;
}
@@ -546,8 +529,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this,
/* Wait for the interrupt from the BCH block. */
timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000));
if (!timeout) {
- dev_err(this->dev, "BCH timeout, last DMA :%d\n",
- this->last_dma_type);
+ dev_err(this->dev, "BCH timeout\n");
gpmi_dump_info(this);
return -ETIMEDOUT;
}
@@ -695,56 +677,6 @@ static void release_resources(struct gpmi_nand_data *this)
release_dma_channels(this);
}
-static int read_page_prepare(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void **use_virt, dma_addr_t *use_phys)
-{
- struct device *dev = this->dev;
-
- if (virt_addr_valid(destination)) {
- dma_addr_t dest_phys;
-
- dest_phys = dma_map_single(dev, destination,
- length, DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, dest_phys)) {
- if (alt_size < length) {
- dev_err(dev, "Alternate buffer is too small\n");
- return -ENOMEM;
- }
- goto map_failed;
- }
- *use_virt = destination;
- *use_phys = dest_phys;
- this->direct_dma_map_ok = true;
- return 0;
- }
-
-map_failed:
- *use_virt = alt_virt;
- *use_phys = alt_phys;
- this->direct_dma_map_ok = false;
- return 0;
-}
-
-static inline void read_page_end(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void *used_virt, dma_addr_t used_phys)
-{
- if (this->direct_dma_map_ok)
- dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE);
-}
-
-static inline void read_page_swap_end(struct gpmi_nand_data *this,
- void *destination, unsigned length,
- void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
- void *used_virt, dma_addr_t used_phys)
-{
- if (!this->direct_dma_map_ok)
- memcpy(destination, alt_virt, length);
-}
-
static int send_page_prepare(struct gpmi_nand_data *this,
const void *source, unsigned length,
void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
@@ -946,10 +878,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
struct gpmi_nand_data *this = nand_get_controller_data(chip);
dev_dbg(this->dev, "len is %d\n", len);
- this->upper_buf = buf;
- this->upper_len = len;
- gpmi_read_data(this);
+ gpmi_read_data(this, buf, len);
}
static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
@@ -958,10 +888,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
struct gpmi_nand_data *this = nand_get_controller_data(chip);
dev_dbg(this->dev, "len is %d\n", len);
- this->upper_buf = (uint8_t *)buf;
- this->upper_len = len;
- gpmi_send_data(this);
+ gpmi_send_data(this, buf, len);
}
static uint8_t gpmi_read_byte(struct mtd_info *mtd)
@@ -1031,44 +959,46 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
struct mtd_info *mtd = nand_to_mtd(chip);
void *payload_virt;
dma_addr_t payload_phys;
- void *auxiliary_virt;
- dma_addr_t auxiliary_phys;
unsigned int i;
unsigned char *status;
unsigned int max_bitflips = 0;
int ret;
+ bool direct = false;
dev_dbg(this->dev, "page number is : %d\n", page);
- ret = read_page_prepare(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- &payload_virt, &payload_phys);
- if (ret) {
- dev_err(this->dev, "Inadequate DMA buffer\n");
- ret = -ENOMEM;
- return ret;
+
+ payload_virt = this->payload_virt;
+ payload_phys = this->payload_phys;
+
+ if (virt_addr_valid(buf)) {
+ dma_addr_t dest_phys;
+
+ dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size,
+ DMA_FROM_DEVICE);
+ if (!dma_mapping_error(this->dev, dest_phys)) {
+ payload_virt = buf;
+ payload_phys = dest_phys;
+ direct = true;
+ }
}
- auxiliary_virt = this->auxiliary_virt;
- auxiliary_phys = this->auxiliary_phys;
/* go! */
- ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
- read_page_end(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- payload_virt, payload_phys);
+ ret = gpmi_read_page(this, payload_phys, this->auxiliary_phys);
+
+ if (direct)
+ dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size,
+ DMA_FROM_DEVICE);
+
if (ret) {
dev_err(this->dev, "Error in ECC-based read: %d\n", ret);
return ret;
}
/* Loop over status bytes, accumulating ECC status. */
- status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
+ status = this->auxiliary_virt + nfc_geo->auxiliary_status_offset;
- read_page_swap_end(this, buf, nfc_geo->payload_size,
- this->payload_virt, this->payload_phys,
- nfc_geo->payload_size,
- payload_virt, payload_phys);
+ if (!direct)
+ memcpy(buf, this->payload_virt, nfc_geo->payload_size);
for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
@@ -1123,7 +1053,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
buf + i * nfc_geo->ecc_chunk_size,
nfc_geo->ecc_chunk_size,
eccbuf, eccbytes,
- auxiliary_virt,
+ this->auxiliary_virt,
nfc_geo->metadata_size,
nfc_geo->ecc_strength);
} else {
@@ -1151,7 +1081,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip,
}