[PATCH 4/6] mtd: atmel_nand: enable Nand Flash Controller (NFC) read data via sram
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Wed Jun 12 07:58:03 EDT 2013
On 18:26 Mon 10 Jun , Josh Wu wrote:
> NFC has embedded sram which can use to transfer data. This patch enable reading
> nand flash via NFC SRAM. It will minimize the CPU overhead.
>
> This driver has been tested on SAMA5D3X-EK with JFFS2, YAFFS2, UBIFS and
> mtd-utils.
>
> Here puts the part of mtd_speedtest (read test) result as following:
> Compare with non-NFC mtd_speedtest result, reading will reduce %45 cpu load
> with increase %80 speed.
>
> - commands use to test:
> # insmod /mnt/mtd_speedtest.ko dev=2 &
> # top -n 30 -d 1 | grep speedtest
>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj at jcrosoft.com>
Best Regards,
J.
> - 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 read speed
> 509 495 root D 1164 0% 28% 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% 26% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: eraseblock read speed is 9403 KiB/s
> mtd_speedtest: testing page read speed
> 509 495 root R 1164 0% 31% insmod /mnt/mtd_speedtest.ko dev=2
> 509 495 root D 1164 0% 57% insmod /mnt/mtd_speedtest.ko dev=2
> 509 495 root D 1164 0% 53% insmod /mnt/mtd_speedtest.ko dev=2
> 509 495 root D 1164 0% 71% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: page read speed is 9258 KiB/s
>
> Signed-off-by: Josh Wu <josh.wu at atmel.com>
> ---
> v2 --> v3:
> - if NFC sram address and size is specified then enable NFC sram read.
> Otherwise disable NFC sram read.
>
> drivers/mtd/nand/atmel_nand.c | 163 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 157 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index bf3370b..c2ec740 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -96,9 +96,13 @@ struct atmel_nfc {
> void __iomem *hsmc_regs;
> void __iomem *sram_bank0;
> dma_addr_t sram_bank0_phys;
> + bool use_nfc_sram;
>
> bool is_initialized;
> struct completion comp_nfc;
> +
> + /* Point to the sram bank which include readed data via NFC */
> + void __iomem *data_in_sram;
> };
> static struct atmel_nfc nand_nfc;
>
> @@ -251,24 +255,47 @@ static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
> return res;
> }
> }
> +
> return res;
> }
>
> +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size)
> +{
> + int i;
> + u32 *t = trg;
> + const __iomem u32 *s = src;
> +
> + for (i = 0; i < (size >> 2); i++)
> + *t++ = readl_relaxed(s++);
> +}
> +
> /*
> * Minimal-overhead PIO for data access.
> */
> static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
> + struct atmel_nand_host *host = nand_chip->priv;
>
> - __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
> + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
> + memcpy32_fromio(buf, host->nfc->data_in_sram, len);
> + host->nfc->data_in_sram += len;
> + } else {
> + __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
> + }
> }
>
> static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
> + struct atmel_nand_host *host = nand_chip->priv;
>
> - __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
> + memcpy32_fromio(buf, host->nfc->data_in_sram, len);
> + host->nfc->data_in_sram += len;
> + } else {
> + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> + }
> }
>
> static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> @@ -290,6 +317,40 @@ static void dma_complete_func(void *completion)
> complete(completion);
> }
>
> +static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank)
> +{
> + /* NFC only has two banks. Must be 0 or 1 */
> + if (bank > 1)
> + return -EINVAL;
> +
> + if (bank) {
> + /* Only for a 2k-page or lower flash, NFC can handle 2 banks */
> + if (host->mtd.writesize > 2048)
> + return -EINVAL;
> + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1);
> + } else {
> + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK0);
> + }
> +
> + return 0;
> +}
> +
> +static uint nfc_get_sram_off(struct atmel_nand_host *host)
> +{
> + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1)
> + return NFC_SRAM_BANK1_OFFSET;
> + else
> + return 0;
> +}
> +
> +static dma_addr_t nfc_sram_phys(struct atmel_nand_host *host)
> +{
> + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1)
> + return host->nfc->sram_bank0_phys + NFC_SRAM_BANK1_OFFSET;
> + else
> + return host->nfc->sram_bank0_phys;
> +}
> +
> static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> int is_read)
> {
> @@ -303,6 +364,7 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> void *p = buf;
> int err = -EIO;
> enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> + struct atmel_nfc *nfc = host->nfc;
>
> if (buf >= high_memory)
> goto err_buf;
> @@ -319,7 +381,12 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> }
>
> if (is_read) {
> - dma_src_addr = host->io_phys;
> + if (nfc && nfc->data_in_sram)
> + dma_src_addr = nfc_sram_phys(host) + (nfc->data_in_sram
> + - (nfc->sram_bank0 + nfc_get_sram_off(host)));
> + else
> + dma_src_addr = host->io_phys;
> +
> dma_dst_addr = phys_addr;
> } else {
> dma_src_addr = phys_addr;
> @@ -346,6 +413,10 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> dma_async_issue_pending(host->dma_chan);
> wait_for_completion(&host->comp);
>
> + if (is_read && nfc && nfc->data_in_sram)
> + /* After read data from SRAM, need to increase the position */
> + nfc->data_in_sram += len;
> +
> err = 0;
>
> err_dma:
> @@ -856,7 +927,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
> unsigned long end_time;
> int bitflips = 0;
>
> - pmecc_enable(host, NAND_ECC_READ);
> + if (!host->nfc || !host->nfc->use_nfc_sram)
> + pmecc_enable(host, NAND_ECC_READ);
>
> chip->read_buf(mtd, buf, eccsize);
> chip->read_buf(mtd, oob, mtd->oobsize);
> @@ -1669,6 +1741,7 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> unsigned int addr1234 = 0;
> unsigned int cycle0 = 0;
> bool do_addr = true;
> + host->nfc->data_in_sram = NULL;
>
> dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
> __func__, command, column, page_addr);
> @@ -1710,6 +1783,16 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> command = NAND_CMD_READ0; /* only READ0 is valid */
> cmd1 = command << 2;
> }
> + if (host->nfc->use_nfc_sram) {
> + /* Enable Data transfer to sram */
> + dataen = NFCADDR_CMD_DATAEN;
> +
> + /* Need enable PMECC now, since NFC will transfer
> + * data in bus after sending nfc read command.
> + */
> + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc)
> + pmecc_enable(host, NAND_ECC_READ);
> + }
>
> cmd2 = NAND_CMD_READSTART << 10;
> vcmd2 = NFCADDR_CMD_VCMD2;
> @@ -1731,6 +1814,10 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
> nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
>
> + if (dataen == NFCADDR_CMD_DATAEN)
> + if (nfc_wait_interrupt(host, NFC_SR_XFR_DONE))
> + dev_err(host->dev, "something wrong, No XFR_DONE interrupt comes.\n");
> +
> /*
> * Program and erase have their own busy handlers status, sequential
> * in, and deplete1 need no delay.
> @@ -1748,12 +1835,65 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> return;
>
> case NAND_CMD_READ0:
> + if (dataen == NFCADDR_CMD_DATAEN) {
> + host->nfc->data_in_sram = host->nfc->sram_bank0 +
> + nfc_get_sram_off(host);
> + return;
> + }
> /* fall through */
> default:
> nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
> }
> }
>
> +static int nfc_sram_init(struct mtd_info *mtd)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + int res = 0;
> +
> + /* Initialize the NFC CFG register */
> + unsigned int cfg_nfc = 0;
> +
> + /* set page size and oob layout */
> + switch (mtd->writesize) {
> + case 512:
> + cfg_nfc = NFC_CFG_PAGESIZE_512;
> + break;
> + case 1024:
> + cfg_nfc = NFC_CFG_PAGESIZE_1024;
> + break;
> + case 2048:
> + cfg_nfc = NFC_CFG_PAGESIZE_2048;
> + break;
> + case 4096:
> + cfg_nfc = NFC_CFG_PAGESIZE_4096;
> + break;
> + case 8192:
> + cfg_nfc = NFC_CFG_PAGESIZE_8192;
> + break;
> + default:
> + dev_err(host->dev, "Unsupported page size for NFC.\n");
> + res = -ENXIO;
> + return res;
> + }
> +
> + /* oob bytes size = (NFCSPARESIZE + 1) * 4
> + * Max support spare size is 512 bytes. */
> + cfg_nfc |= (((mtd->oobsize / 4) - 1) << 24 & NFC_CFG_NFC_SPARESIZE);
> + /* default set a max timeout */
> + cfg_nfc |= NFC_CFG_RSPARE |
> + NFC_CFG_NFC_DTOCYC | NFC_CFG_NFC_DTOMUL;
> +
> + nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc);
> +
> + nfc_set_sram_bank(host, 0);
> +
> + dev_info(host->dev, "Using NFC Sram read\n");
> +
> + return 0;
> +}
> +
> static struct platform_driver atmel_nand_nfc_driver;
> /*
> * Probe for the NAND device.
> @@ -1928,6 +2068,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> goto err_hw_ecc;
> }
>
> + /* initialize the nfc configuration register */
> + if (host->nfc && host->nfc->use_nfc_sram) {
> + res = nfc_sram_init(mtd);
> + if (res) {
> + host->nfc->use_nfc_sram = false;
> + dev_err(host->dev, "Disable use nfc sram for data transfer.\n");
> + }
> + }
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -2009,11 +2158,13 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
> nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> if (nfc_sram) {
> nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram);
> - if (IS_ERR(nfc->sram_bank0))
> + if (IS_ERR(nfc->sram_bank0)) {
> dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n",
> PTR_ERR(nfc->sram_bank0));
> - else
> + } else {
> + nfc->use_nfc_sram = true;
> nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start;
> + }
> }
>
> nfc->is_initialized = true;
> --
> 1.7.9.5
>
More information about the linux-arm-kernel
mailing list