Commit 6054d4d5 authored by Josh Wu's avatar Josh Wu Committed by David Woodhouse

mtd: atmel_nand: enable Nand Flash Controller (NFC) write via sram

This patch enable writing nand flash via NFC SRAM. It will minimize the CPU
overhead. The SRAM write only support ECC_NONE and ECC_HW with PMECC.

To enable this NFC write by SRAM feature, you can add a string in dts under
NFC driver node.

This driver has been tested on SAMA5D3X-EK with JFFS2, YAFFS2, UBIFS and
mtd-utils.

Here is part of mtd_speedtest (writing test) result, compare with non-NFC
writing, it reduces %65 cpu load with loss %12 speed.

- commands use to test:
  # insmod /mnt/mtd_speedtest.ko dev=2 &
  # top -n 30 -d 1 | grep speedtest

- test result:
=================================================
mtd_speedtest: MTD device: 2
mtd_speedtest: MTD device size 41943040, eraseblock size 131072, page size 2048, count of eraseblocks 320, pages per eraseblock 64, OOB size 64
mtd_speedtest: testing eraseblock write speed
  509   495 root     D     1164   0%   7% insmod /mnt/mtd_speedtest.ko dev=2
  509   495 root     D     1164   0%   8% insmod /mnt/mtd_speedtest.ko dev=2
  509   495 root     R     1164   0%   5% insmod /mnt/mtd_speedtest.ko dev=2
mtd_speedtest: eraseblock write speed is 5194 KiB/s
mtd_speedtest: testing page write speed
  509   495 root     D     1164   0%  32% insmod /mnt/mtd_speedtest.ko dev=2
  509   495 root     D     1164   0%  27% insmod /mnt/mtd_speedtest.ko dev=2
  509   495 root     D     1164   0%  25% insmod /mnt/mtd_speedtest.ko dev=2
  509   495 root     D     1164   0%  30% insmod /mnt/mtd_speedtest.ko dev=2
mtd_speedtest: page write speed is 5024 KiB/s
Signed-off-by: default avatarJosh Wu <josh.wu@atmel.com>
Acked-by: default avatarJean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 1ae9c092
...@@ -36,6 +36,8 @@ Optional properties: ...@@ -36,6 +36,8 @@ Optional properties:
- reg : should specify the address and size used for NFC command registers, - reg : should specify the address and size used for NFC command registers,
NFC registers and NFC Sram. NFC Sram address and size can be absent NFC registers and NFC Sram. NFC Sram address and size can be absent
if don't want to use it. if don't want to use it.
- Optional properties:
- atmel,write-by-sram: boolean to enable NFC write by sram.
Examples: Examples:
nand0: nand@40000000,0 { nand0: nand@40000000,0 {
......
...@@ -94,12 +94,14 @@ struct atmel_nfc { ...@@ -94,12 +94,14 @@ struct atmel_nfc {
void __iomem *sram_bank0; void __iomem *sram_bank0;
dma_addr_t sram_bank0_phys; dma_addr_t sram_bank0_phys;
bool use_nfc_sram; bool use_nfc_sram;
bool write_by_sram;
bool is_initialized; bool is_initialized;
struct completion comp_nfc; struct completion comp_nfc;
/* Point to the sram bank which include readed data via NFC */ /* Point to the sram bank which include readed data via NFC */
void __iomem *data_in_sram; void __iomem *data_in_sram;
bool will_write_sram;
}; };
static struct atmel_nfc nand_nfc; static struct atmel_nfc nand_nfc;
...@@ -261,6 +263,16 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) ...@@ -261,6 +263,16 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
*t++ = readl_relaxed(s++); *t++ = readl_relaxed(s++);
} }
static void memcpy32_toio(void __iomem *trg, const void *src, int size)
{
int i;
u32 __iomem *t = trg;
const u32 *s = src;
for (i = 0; i < (size >> 2); i++)
writel_relaxed(*s++, t++);
}
/* /*
* Minimal-overhead PIO for data access. * Minimal-overhead PIO for data access.
*/ */
...@@ -382,7 +394,11 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, ...@@ -382,7 +394,11 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
dma_dst_addr = phys_addr; dma_dst_addr = phys_addr;
} else { } else {
dma_src_addr = phys_addr; dma_src_addr = phys_addr;
dma_dst_addr = host->io_phys;
if (nfc && nfc->write_by_sram)
dma_dst_addr = nfc_sram_phys(host);
else
dma_dst_addr = host->io_phys;
} }
tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr, tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
...@@ -954,9 +970,10 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, ...@@ -954,9 +970,10 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
int i, j; int i, j;
unsigned long end_time; unsigned long end_time;
pmecc_enable(host, NAND_ECC_WRITE); if (!host->nfc || !host->nfc->write_by_sram) {
pmecc_enable(host, NAND_ECC_WRITE);
chip->write_buf(mtd, (u8 *)buf, mtd->writesize); chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
}
end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) { while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
...@@ -1798,6 +1815,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, ...@@ -1798,6 +1815,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
case NAND_CMD_SEQIN: case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN: case NAND_CMD_RNDIN:
nfcwr = NFCADDR_CMD_NFCWR; nfcwr = NFCADDR_CMD_NFCWR;
if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN)
dataen = NFCADDR_CMD_DATAEN;
break; break;
default: default:
break; break;
...@@ -1842,6 +1861,68 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, ...@@ -1842,6 +1861,68 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
} }
} }
static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, int data_len, const uint8_t *buf,
int oob_required, int page, int cached, int raw)
{
int cfg, len;
int status = 0;
struct atmel_nand_host *host = chip->priv;
void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
/* Subpage write is not supported */
if (offset || (data_len < mtd->writesize))
return -EINVAL;
cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
len = mtd->writesize;
if (unlikely(raw)) {
len += mtd->oobsize;
nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
} else
nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE);
/* Copy page data to sram that will write to nand via NFC */
if (use_dma) {
if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0)
/* Fall back to use cpu copy */
memcpy32_toio(sram, buf, len);
} else {
memcpy32_toio(sram, buf, len);
}
if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc)
/*
* When use NFC sram, need set up PMECC before send
* NAND_CMD_SEQIN command. Since when the nand command
* is sent, nfc will do transfer from sram and nand.
*/
pmecc_enable(host, NAND_ECC_WRITE);
host->nfc->will_write_sram = true;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
host->nfc->will_write_sram = false;
if (likely(!raw))
/* Need to write ecc into oob */
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
if (status < 0)
return status;
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status, page);
if (status & NAND_STATUS_FAIL)
return -EIO;
return 0;
}
static int nfc_sram_init(struct mtd_info *mtd) static int nfc_sram_init(struct mtd_info *mtd)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
...@@ -1884,10 +1965,20 @@ static int nfc_sram_init(struct mtd_info *mtd) ...@@ -1884,10 +1965,20 @@ static int nfc_sram_init(struct mtd_info *mtd)
nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc); nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc);
host->nfc->will_write_sram = false;
nfc_set_sram_bank(host, 0); nfc_set_sram_bank(host, 0);
dev_info(host->dev, "Using NFC Sram read\n"); /* Use Write page with NFC SRAM only for PMECC or ECC NONE. */
if (host->nfc->write_by_sram) {
if ((chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) ||
chip->ecc.mode == NAND_ECC_NONE)
chip->write_page = nfc_sram_write_page;
else
host->nfc->write_by_sram = false;
}
dev_info(host->dev, "Using NFC Sram read %s\n",
host->nfc->write_by_sram ? "and write" : "");
return 0; return 0;
} }
...@@ -2147,6 +2238,11 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev) ...@@ -2147,6 +2238,11 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
} else { } else {
nfc->use_nfc_sram = true; nfc->use_nfc_sram = true;
nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start;
if (pdev->dev.of_node)
nfc->write_by_sram = of_property_read_bool(
pdev->dev.of_node,
"atmel,write-by-sram");
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment