[PATCH 5/6] mtd: atmel_nand: enable Nand Flash Controller (NFC) write via sram
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Wed Jun 12 07:58:20 EDT 2013
On 18:26 Mon 10 Jun , Josh Wu wrote:
> 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: Josh Wu <josh.wu at atmel.com>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj at jcrosoft.com>
Best Regards,
J.
> ---
> v2 --> v3:
> - use a property of NFC node to enable/disable NFC write via sram.
>
> .../devicetree/bindings/mtd/atmel-nand.txt | 2 +
> drivers/mtd/nand/atmel_nand.c | 107 ++++++++++++++++++--
> 2 files changed, 103 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> index 6a267cb..da78e22 100644
> --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> @@ -35,6 +35,8 @@ Optional properties:
> - 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
> if don't want to use it.
> + - Optional properties:
> + - atmel,write-by-sram: boolean to enable NFC write by sram.
>
> Examples:
> nand0: nand at 40000000,0 {
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index c2ec740..e3e348b 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -97,12 +97,14 @@ struct atmel_nfc {
> void __iomem *sram_bank0;
> dma_addr_t sram_bank0_phys;
> bool use_nfc_sram;
> + bool write_by_sram;
>
> bool is_initialized;
> struct completion comp_nfc;
>
> /* Point to the sram bank which include readed data via NFC */
> void __iomem *data_in_sram;
> + bool will_write_sram;
> };
> static struct atmel_nfc nand_nfc;
>
> @@ -269,6 +271,16 @@ static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
> *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.
> */
> @@ -390,7 +402,11 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> dma_dst_addr = phys_addr;
> } else {
> 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,
> @@ -961,9 +977,10 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
> int i, j;
> unsigned long end_time;
>
> - pmecc_enable(host, NAND_ECC_WRITE);
> -
> - chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
> + if (!host->nfc || !host->nfc->write_by_sram) {
> + pmecc_enable(host, NAND_ECC_WRITE);
> + chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
> + }
>
> end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
> while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
> @@ -1802,6 +1819,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> case NAND_CMD_SEQIN:
> case NAND_CMD_RNDIN:
> nfcwr = NFCADDR_CMD_NFCWR;
> + if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN)
> + dataen = NFCADDR_CMD_DATAEN;
> break;
> default:
> break;
> @@ -1846,6 +1865,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)
> {
> struct nand_chip *chip = mtd->priv;
> @@ -1887,10 +1968,20 @@ static int nfc_sram_init(struct mtd_info *mtd)
>
> nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc);
>
> + host->nfc->will_write_sram = false;
> 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;
> }
>
> @@ -2164,11 +2255,15 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
> } else {
> nfc->use_nfc_sram = true;
> 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");
> }
> }
>
> nfc->is_initialized = true;
> -
> dev_info(&pdev->dev, "NFC is probed.\n");
> return 0;
> }
> --
> 1.7.9.5
>
More information about the linux-mtd
mailing list