[PATCH v2 2/4] mtd: atmel_nand: add Nand Flash Controller (NFC) support

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Fri May 24 16:09:47 EDT 2013


On 17:51 Fri 17 May     , Josh Wu wrote:
> Nand Flash Controller (NFC) can handle automatic transfers, sending the
> commands and address cycles to the NAND Flash.
> 
> To use NFC in this driver, the user needs to set the address and size of
> NFC command registers, NFC registers (embedded in HSMC) and NFC SRAM in dts.
> Also user need to set up the HSMC irq, which use to check whether nfc
> command is finish or not.
> 
> This driver has been tested on SAMA5D3X-EK board with JFFS2, YAFFS,
> UBIFS and mtd-utils.
> 
> I put the part of the mtd_speedtest result here for your information.
> From the mtd_speedtest, we can see the NFC will reduce the %50 of cpu load
> when writing nand flash. No change when reading.
> In the meantime, the speed will be slow about %8.
> 
> - commands use to test:
>     #insmod /mnt/mtd_speedtest.ko dev=2 &
>     #top -n 30 -d 1 | grep speedtest
> 
> - test result:
> 
> Before the patch:
> =================================================
> 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
>   515   495 root     R     1164   0%  93% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  98% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  99% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: eraseblock write speed is 5768 KiB/s
> mtd_speedtest: testing eraseblock read speed
>   515   495 root     R     1164   0%  92% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  91% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  94% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: eraseblock read speed is 5932 KiB/s
> mtd_speedtest: testing page write speed
>   515   495 root     R     1164   0%  94% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  98% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  98% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: page write speed is 5770 KiB/s
> mtd_speedtest: testing page read speed
>   515   495 root     R     1164   0%  91% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  89% insmod /mnt/mtd_speedtest.ko dev=2
>   515   495 root     R     1164   0%  91% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: page read speed is 5910 KiB/s
> 
> After the patch:
> =================================================
> 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%  49% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     D     1164   0%  50% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     D     1164   0%  47% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: eraseblock write speed is 5370 KiB/s
> mtd_speedtest: testing eraseblock read speed
>   509   495 root     R     1164   0%  92% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     R     1164   0%  91% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     R     1164   0%  95% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: eraseblock read speed is 5715 KiB/s
> mtd_speedtest: testing page write speed
>   509   495 root     D     1164   0%  48% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     D     1164   0%  47% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     D     1164   0%  50% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: page write speed is 5224 KiB/s
> mtd_speedtest: testing page read speed
>   509   495 root     R     1164   0%  89% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     R     1164   0%  94% insmod /mnt/mtd_speedtest.ko dev=2
>   509   495 root     R     1164   0%  93% insmod /mnt/mtd_speedtest.ko dev=2
> mtd_speedtest: page read speed is 5641 KiB/s
> 
> Signed-off-by: Josh Wu <josh.wu at atmel.com>
> ---
> v1 --> v2:
>   remove the useless nand commands: NAND_CMD_STATUS_ERRORx, NAND_CMD_DEPLETE1.
> 
>  .../devicetree/bindings/mtd/atmel-nand.txt         |    3 +
>  drivers/mtd/nand/atmel_nand.c                      |  373 ++++++++++++++++++--
>  drivers/mtd/nand/atmel_nand_nfc.h                  |  106 ++++++
>  3 files changed, 454 insertions(+), 28 deletions(-)
>  create mode 100644 drivers/mtd/nand/atmel_nand_nfc.h
> 
> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> index d555421..88e3313 100644
> --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> @@ -6,6 +6,8 @@ Required properties:
>  	and hardware ECC controller if available.
>  	If the hardware ECC is PMECC, it should contain address and size for
>  	PMECC, PMECC Error Location controller and ROM which has lookup tables.
> +	If hardware supports Nand Flash Controller, it should contain address and
> +	size for NFC command registers, NFC registers and NFC Sram.
>  - atmel,nand-addr-offset : offset for the address latch.
>  - atmel,nand-cmd-offset : offset for the command latch.
>  - #address-cells, #size-cells : Must be present if the device has sub-nodes
> @@ -29,6 +31,7 @@ Optional properties:
>    sector size 1024.
>  - nand-bus-width : 8 or 16 bus width if not present 8
>  - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
> +- atmel,has-nfc: boolean to enable Nand Flash Controller(NFC).

yes but you need to update the compatible or check the IP support it too
as has-nfc will mean I have the nfs on the hardware but not enable it

the SoC dtsi will describe you have it but the board will decide to enable it or
not
>  
>  Examples:
>  nand0: nand at 40000000,0 {
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index f747791..48d7ee6 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -18,6 +18,9 @@
>   *  Add Programmable Multibit ECC support for various AT91 SoC
>   *     © Copyright 2012 ATMEL, Hong Xu
>   *
> + *  Add Nand Flash Controller support for SAMA5 SoC
> + *     © Copyright 2013 ATMEL, Josh Wu (josh.wu at atmel.com)
> + *
>   * 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.
> @@ -37,9 +40,11 @@
>  #include <linux/mtd/nand.h>
>  #include <linux/mtd/partitions.h>
>  
> +#include <linux/delay.h>
>  #include <linux/dmaengine.h>
>  #include <linux/gpio.h>
>  #include <linux/io.h>
> +#include <linux/interrupt.h>
>  #include <linux/platform_data/atmel.h>
>  #include <linux/pinctrl/consumer.h>
>  
> @@ -58,6 +63,7 @@ module_param(on_flash_bbt, int, 0);
>  	__raw_writel((value), add + ATMEL_ECC_##reg)
>  
>  #include "atmel_nand_ecc.h"	/* Hardware ECC registers */
> +#include "atmel_nand_nfc.h"	/* Nand Flash Controller definition */
>  
>  /* oob layout for large page size
>   * bad block info is on bytes 0 and 1
> @@ -85,6 +91,13 @@ static struct nand_ecclayout atmel_oobinfo_small = {
>  	},
>  };
>  
> +struct atmel_nfc {
> +	void __iomem		*base_cmd_regs;
> +	void __iomem		*hsmc_regs;
> +	void __iomem		*sram_bank0;
> +	dma_addr_t		sram_bank0_phys;
> +};
> +
>  struct atmel_nand_host {
>  	struct nand_chip	nand_chip;
>  	struct mtd_info		mtd;
> @@ -93,10 +106,15 @@ struct atmel_nand_host {
>  	struct atmel_nand_data	board;
>  	struct device		*dev;
>  	void __iomem		*ecc;
> +	int			irq;
>  
>  	struct completion	comp;
>  	struct dma_chan		*dma_chan;
>  
> +	bool			has_nfc;
> +	struct atmel_nfc	nfc;
> +	struct completion	comp_nfc;
> +
>  	bool			has_pmecc;
>  	u8			pmecc_corr_cap;
>  	u16			pmecc_sector_size;
> @@ -1357,6 +1375,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
>  	board->det_pin = of_get_gpio(np, 2);
>  
>  	host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
> +	host->has_nfc = of_property_read_bool(np, "atmel,has-nfc");
>  
>  	if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
>  		return 0;	/* Not using PMECC */
> @@ -1469,6 +1488,275 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +/* SMC interrupt service routine */
> +static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
> +{
> +	struct atmel_nand_host *host = dev_id;
> +	u32 status, mask, pending;
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	status = nfc_readl(host->nfc.hsmc_regs, SR);
> +	mask = nfc_readl(host->nfc.hsmc_regs, IMR);
> +	pending = status & mask;
> +
> +	if (pending & ATMEL_HSMC_NFC_XFR_DONE) {
> +		complete(&host->comp_nfc);
> +		nfc_writel(host->nfc.hsmc_regs, IDR, ATMEL_HSMC_NFC_XFR_DONE);
> +		ret = IRQ_HANDLED;
> +	} else if (pending & ATMEL_HSMC_NFC_RB_EDGE) {
> +		complete(&host->comp_nfc);
> +		nfc_writel(host->nfc.hsmc_regs, IDR, ATMEL_HSMC_NFC_RB_EDGE);
> +		ret = IRQ_HANDLED;
> +	} else if (pending & ATMEL_HSMC_NFC_CMD_DONE) {
> +		complete(&host->comp_nfc);
> +		nfc_writel(host->nfc.hsmc_regs, IDR, ATMEL_HSMC_NFC_CMD_DONE);
> +		ret = IRQ_HANDLED;
> +	}

	put esle ret = IRQ_NONE less code
> +
> +	return ret;
> +}
> +
> +/* NFC(Nand Flash Controller) related functions */
> +static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
> +{
> +	unsigned long timeout;
> +	init_completion(&host->comp_nfc);
> +
> +	/* Enable interrupt that need to wait for */
> +	nfc_writel(host->nfc.hsmc_regs, IER, flag);
> +
> +	timeout = wait_for_completion_timeout(&host->comp_nfc,
> +			msecs_to_jiffies(NFC_TIME_OUT_MS));
> +	if (timeout)
> +		return 0;
> +
> +	/* Time out to wait for the interrupt */
> +	dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag);
> +	return -ETIMEDOUT;
> +}
> +
> +static int nfc_send_command(struct atmel_nand_host *host,
> +	unsigned int cmd, unsigned int addr, unsigned char cycle0)
> +{
> +	unsigned long timeout;
> +	dev_dbg(host->dev,
> +		"nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n",
> +		cmd, addr, cycle0);
> +
> +	timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
> +	while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc.base_cmd_regs)
> +			& NFCADDR_CMD_NFCBUSY) {
> +		if (time_after(jiffies, timeout)) {
> +			dev_err(host->dev,
> +				"Time out to wait CMD_NFCBUSY ready!\n");
> +			break;
			already timeout you still neeed to wait DONE?
> +		}
> +	}
> +	nfc_writel(host->nfc.hsmc_regs, CYCLE0, cycle0);
> +	nfc_cmd_addr1234_writel(cmd, addr, host->nfc.base_cmd_regs);
> +	return nfc_wait_interrupt(host, ATMEL_HSMC_NFC_CMD_DONE);
> +}
> +
> +static int nfc_device_ready(struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand_chip = mtd->priv;
> +	struct atmel_nand_host *host = nand_chip->priv;
> +	if (!nfc_wait_interrupt(host, ATMEL_HSMC_NFC_RB_EDGE))
> +		return 1;
> +	return 0;
> +}
> +
> +static void nfc_select_chip(struct mtd_info *mtd, int chip)
> +{
> +	struct nand_chip *nand_chip = mtd->priv;
> +	struct atmel_nand_host *host = nand_chip->priv;
> +
> +	if (chip == -1)
> +		nfc_writel(host->nfc.hsmc_regs, CTRL, ATMEL_HSMC_NFC_DISABLE);
> +	else
> +		nfc_writel(host->nfc.hsmc_regs, CTRL, ATMEL_HSMC_NFC_ENABLE);
> +}
> +
> +static int nfc_init(struct platform_device *pdev, struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand_chip = mtd->priv;
> +	struct atmel_nand_host *host = nand_chip->priv;
> +	struct atmel_nfc *nfc = &host->nfc;
> +	struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram;
> +
> +	nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 4);
> +	if (!nfc_cmd_regs)
> +		return -EINVAL;
> +
> +	nfc_hsmc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 5);
> +	if (!nfc_hsmc_regs)
> +		return -EINVAL;
> +
> +	nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 6);
> +	if (!nfc_sram)
> +		return -EINVAL;
> +
> +	nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs);
> +	if (IS_ERR(nfc->base_cmd_regs))
> +		return PTR_ERR(nfc->base_cmd_regs);
> +
> +	nfc->hsmc_regs = devm_ioremap_resource(&pdev->dev, nfc_hsmc_regs);
> +	if (IS_ERR(nfc->hsmc_regs))
> +		return PTR_ERR(nfc->hsmc_regs);
> +
> +	nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram);
> +	if (IS_ERR(nfc->sram_bank0))
> +		return PTR_ERR(nfc->sram_bank0);
> +
> +	nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start;
> +
> +	dev_info(host->dev, "Using NFC\n");
> +	return 0;
> +}
> +
> +static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
> +		unsigned int *addr1234, unsigned int *cycle0)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +
> +	int acycle = 0;
> +	unsigned char addr_bytes[8];
> +	int index = 0, bit_shift;
> +
> +	BUG_ON(addr1234 == NULL || cycle0 == NULL);
> +
> +	*cycle0 = 0;
> +	*addr1234 = 0;
> +
> +	if (column != -1) {
> +		if (chip->options & NAND_BUSWIDTH_16)
> +			column >>= 1;
> +		addr_bytes[acycle++] = column & 0xff;
> +		if (mtd->writesize > 512)
> +			addr_bytes[acycle++] = (column >> 8) & 0xff;
> +	}
> +
> +	if (page_addr != -1) {
> +		addr_bytes[acycle++] = page_addr & 0xff;
> +		addr_bytes[acycle++] = (page_addr >> 8) & 0xff;
> +		if (chip->chipsize > (128 << 20))
> +			addr_bytes[acycle++] = (page_addr >> 16) & 0xff;
> +	}
> +
> +	if (acycle > 4)
> +		*cycle0 = addr_bytes[index++];
> +
> +	for (bit_shift = 0; index < acycle; bit_shift += 8)
> +		*addr1234 += addr_bytes[index++] << bit_shift;
> +
> +	return acycle << 19;	/* return acycle in cmd register */
> +}
> +
> +static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> +				int column, int page_addr)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	struct atmel_nand_host *host = chip->priv;
> +	unsigned long timeout;
> +	unsigned int nfc_addr_cmd = 0;
> +
> +	unsigned int cmd1 = command << 2;
> +
> +	/* Set default settings: no cmd2, no addr cycle. read from nand */
> +	unsigned int cmd2 = 0;
> +	unsigned int vcmd2 = 0;
> +	int acycle = NFCADDR_CMD_ACYCLE_NONE;
> +	int csid = NFCADDR_CMD_CSID_3;
> +	int dataen = NFCADDR_CMD_DATADIS;
> +	int nfcwr = NFCADDR_CMD_NFCRD;
> +	unsigned int addr1234 = 0;
> +	unsigned int cycle0 = 0;
> +	bool do_addr = true;
> +
> +	dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
> +	     __func__, command, column, page_addr);
> +
> +	switch (command) {
> +	case NAND_CMD_RESET:
> +		nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr;
> +		nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
> +		udelay(chip->chip_delay);
> +
> +		nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
> +		timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
> +		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) {
> +			if (time_after(jiffies, timeout)) {
> +				dev_err(host->dev,
> +					"Time out to wait status ready!\n");
> +				break;
> +			}
> +		}
> +		return;
> +	case NAND_CMD_STATUS:
> +		do_addr = false;
> +		break;
> +	case NAND_CMD_PARAM:
> +	case NAND_CMD_READID:
> +		do_addr = false;
> +		acycle = NFCADDR_CMD_ACYCLE_1;
> +		if (column != -1)
> +			addr1234 = column;
> +		break;
> +	case NAND_CMD_RNDOUT:
> +		cmd2 = NAND_CMD_RNDOUTSTART << 10;
> +		vcmd2 = NFCADDR_CMD_VCMD2;
> +		break;
> +	case NAND_CMD_READ0:
> +	case NAND_CMD_READOOB:
> +		if (command == NAND_CMD_READOOB) {
> +			column += mtd->writesize;
> +			command = NAND_CMD_READ0; /* only READ0 is valid */
> +			cmd1 = command << 2;
> +		}
> +
> +		cmd2 = NAND_CMD_READSTART << 10;
> +		vcmd2 = NFCADDR_CMD_VCMD2;
> +		break;
> +	/* For prgramming command, the cmd need set to write enable */
> +	case NAND_CMD_PAGEPROG:
> +	case NAND_CMD_SEQIN:
> +	case NAND_CMD_RNDIN:
> +		nfcwr = NFCADDR_CMD_NFCWR;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (do_addr)
> +		acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
> +				&cycle0);
> +
> +	nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
> +	nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
> +
> +	/*
> +	 * Program and erase have their own busy handlers status, sequential
> +	 * in, and deplete1 need no delay.
> +	 */
> +	switch (command) {
> +	case NAND_CMD_CACHEDPROG:
> +	case NAND_CMD_PAGEPROG:
> +	case NAND_CMD_ERASE1:
> +	case NAND_CMD_ERASE2:
> +	case NAND_CMD_RNDIN:
> +	case NAND_CMD_STATUS:
> +	case NAND_CMD_RNDOUT:
> +	case NAND_CMD_SEQIN:
> +	case NAND_CMD_READID:
> +		return;
> +
> +	case NAND_CMD_READ0:
> +		/* fall through */
> +	default:
> +		nfc_wait_interrupt(host, ATMEL_HSMC_NFC_RB_EDGE);
> +	}
> +}
> +
>  /*
>   * Probe for the NAND device.
>   */
> @@ -1479,7 +1767,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>  	struct nand_chip *nand_chip;
>  	struct resource *mem;
>  	struct mtd_part_parser_data ppdata = {};
> -	int res;
> +	int res, irq;
>  	struct pinctrl *pinctrl;
>  
>  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> @@ -1523,7 +1811,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>  	/* Set address of NAND IO lines */
>  	nand_chip->IO_ADDR_R = host->io_base;
>  	nand_chip->IO_ADDR_W = host->io_base;
> -	nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
>  
>  	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
>  	if (IS_ERR(pinctrl)) {
> @@ -1532,41 +1819,69 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>  		goto err_ecc_ioremap;
>  	}
>  
> -	if (gpio_is_valid(host->board.rdy_pin)) {
> -		res = gpio_request(host->board.rdy_pin, "nand_rdy");
> -		if (res < 0) {
> -			dev_err(&pdev->dev,
> -				"can't request rdy gpio %d\n",
> -				host->board.rdy_pin);
> +	if (host->has_nfc) {
		split in 2 function

		nfs_init
> +		res = nfc_init(pdev, mtd);
> +		if (res)
> +			goto err_ecc_ioremap;
> +		nand_chip->select_chip = nfc_select_chip;
> +		nand_chip->dev_ready = nfc_device_ready;
> +		nand_chip->cmdfunc = nfc_nand_command;
> +
> +		/* Initialize the interrupt for NFC */
> +		irq = platform_get_irq(pdev, 0);
> +		if (irq < 0) {
> +			dev_err(host->dev, "Cannot get HSMC irq!\n");
>  			goto err_ecc_ioremap;
>  		}
>  
> -		res = gpio_direction_input(host->board.rdy_pin);
> -		if (res < 0) {
> -			dev_err(&pdev->dev,
> -				"can't request input direction rdy gpio %d\n",
> -				host->board.rdy_pin);
> +		res = request_irq(irq, hsmc_interrupt, 0, "hsmc", host);
		devm_request_irq
> +		if (res) {
> +			dev_err(&pdev->dev, "Unable to request HSMC irq %d\n",
> +				irq);
>  			goto err_ecc_ioremap;
>  		}
>  
> -		nand_chip->dev_ready = atmel_nand_device_ready;
> -	}
> +		host->irq = irq;
> +	} else {
		rest
> +		nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
> +
> +		if (gpio_is_valid(host->board.rdy_pin)) {
> +			res = gpio_request(host->board.rdy_pin, "nand_rdy");
> +			if (res < 0) {
> +				dev_err(&pdev->dev,
> +					"can't request rdy gpio %d\n",
> +					host->board.rdy_pin);
> +				goto err_ecc_ioremap;
> +			}
>  
> -	if (gpio_is_valid(host->board.enable_pin)) {
> -		res = gpio_request(host->board.enable_pin, "nand_enable");
> -		if (res < 0) {
> -			dev_err(&pdev->dev,
> -				"can't request enable gpio %d\n",
> -				host->board.enable_pin);
> -			goto err_ecc_ioremap;
> +			res = gpio_direction_input(host->board.rdy_pin);
> +			if (res < 0) {
> +				dev_err(&pdev->dev,
> +					"can't request input direction rdy gpio %d\n",
> +					host->board.rdy_pin);
> +				goto err_ecc_ioremap;
> +			}
> +
> +			nand_chip->dev_ready = atmel_nand_device_ready;
>  		}
>  
> -		res = gpio_direction_output(host->board.enable_pin, 1);
> -		if (res < 0) {
> -			dev_err(&pdev->dev,
> -				"can't request output direction enable gpio %d\n",
> -				host->board.enable_pin);
> -			goto err_ecc_ioremap;
> +		if (gpio_is_valid(host->board.enable_pin)) {
> +			res = gpio_request(host->board.enable_pin,
we need to use devm_ here too
and everywhere we can in this driver
> +				"nand_enable");
> +			if (res < 0) {
> +				dev_err(&pdev->dev,
> +					"can't request enable gpio %d\n",
> +					host->board.enable_pin);
> +				goto err_ecc_ioremap;
> +			}
> +
> +			res = gpio_direction_output(host->board.enable_pin, 1);
> +			if (res < 0) {
> +				dev_err(&pdev->dev,
> +					"can't request output direction enable gpio %d\n",
> +					host->board.enable_pin);
> +				goto err_ecc_ioremap;
> +			}
>  		}
>  	}
>  
> @@ -1682,6 +1997,8 @@ err_ecc_ioremap:
>  	iounmap(host->io_base);
>  err_nand_ioremap:
>  	kfree(host);
> +	if (host->irq)
> +		free_irq(host->irq, host);
>  	return res;
>  }
>  
> diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h
> new file mode 100644
> index 0000000..ca124de
> --- /dev/null
> +++ b/drivers/mtd/nand/atmel_nand_nfc.h
> @@ -0,0 +1,106 @@
> +/*
> + * Atmel Nand Flash Controller (NFC) - System peripherals regsters.
> + * Based on SAMA5D3 datasheet.
> + *
> + * © Copyright 2013 Atmel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef ATMEL_NAND_NFC_H
> +#define ATMEL_NAND_NFC_H
> +
> +/*
> + * HSMC NFC registers
> + */
> +#define ATMEL_HSMC_NFC_CFG		0x00	/* NFC Configuration Register */
> +#define		ATMEL_HSMC_PAGESIZE_512		(0)
> +#define		ATMEL_HSMC_PAGESIZE_1024	(1)
> +#define		ATMEL_HSMC_PAGESIZE_2048	(2)
> +#define		ATMEL_HSMC_PAGESIZE_4096	(3)
> +#define		ATMEL_HSMC_PAGESIZE_8192	(4)
	keep the << 0
> +#define		ATMEL_HSMC_WSPARE		(1 << 8)
> +#define		ATMEL_HSMC_RSPARE		(1 << 9)
> +#define		ATMEL_HSMC_EDGE_CTRL_RISING	(0 << 12)
> +#define		ATMEL_HSMC_EDGE_CTRL_FALLING	(1 << 12)
> +#define		ATMEL_HSMC_RBEDGE		(1 << 13)
> +#define		ATMEL_HSMC_NFC_DTOCYC		(0xf << 16)
> +#define		ATMEL_HSMC_NFC_DTOMUL		(0x7 << 20)
> +#define		ATMEL_HSMC_NFC_SPARESIZE	(0x7f << 24)
> +
> +#define ATMEL_HSMC_NFC_CTRL		0x04	/* NFC Control Register */
> +#define		ATMEL_HSMC_NFC_ENABLE		(1 << 0)
> +#define		ATMEL_HSMC_NFC_DISABLE		(1 << 1)
> +
> +#define ATMEL_HSMC_NFC_SR		0x08	/* NFC Status Register */
> +#define		ATMEL_HSMC_NFC_STATUS		(1 << 0)
> +#define		ATMEL_HSMC_NFC_RB_RISE		(1 << 4)
> +#define		ATMEL_HSMC_NFC_RB_FALL		(1 << 5)
> +#define		ATMEL_HSMC_NFC_BUSY		(1 << 8)
> +#define		ATMEL_HSMC_NFC_WR		(1 << 11)
> +#define		ATMEL_HSMC_NFC_CSID		(7 << 12)
> +#define		ATMEL_HSMC_NFC_XFR_DONE		(1 << 16)
> +#define		ATMEL_HSMC_NFC_CMD_DONE		(1 << 17)
> +#define		ATMEL_HSMC_NFC_DTOE		(1 << 20)
> +#define		ATMEL_HSMC_NFC_UNDEF		(1 << 21)
> +#define		ATMEL_HSMC_NFC_AWB		(1 << 22)
> +#define		ATMEL_HSMC_NFC_ASE		(1 << 23)
> +#define		ATMEL_HSMC_NFC_RB_EDGE		(1 << 24)
> +
> +#define ATMEL_HSMC_NFC_IER		0x0c
> +#define ATMEL_HSMC_NFC_IDR		0x10
> +#define ATMEL_HSMC_NFC_IMR		0x14
> +#define ATMEL_HSMC_NFC_CYCLE0		0x18	/* NFC Address Cycle Zero */
> +#define		ATMEL_HSMC_NFC_ADDR_CYCLE0	(0xff)
> +
> +#define ATMEL_HSMC_NFC_BANK		0x1c	/* NFC Bank Register */
> +#define		ATMEL_HSMC_NFC_BANK0	(0 << 0)
> +#define		ATMEL_HSMC_NFC_BANK1	(1 << 0)
> +#define		NFC_SRAM_BANK_OFFSET	(0x1200)
> +
> +#define nfc_writel(addr, reg, value) \
> +	writel((value), (addr) + ATMEL_HSMC_NFC_##reg)
> +
> +#define nfc_readl(addr, reg) \
> +	readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg)
> +
> +/*
> + * NFC Address Command definitions
> + */
> +#define NFCADDR_CMD_CMD1	(0xff << 2)	/* Command for Cycle 1 */
> +#define NFCADDR_CMD_CMD2	(0xff << 10)	/* Command for Cycle 2 */
> +#define NFCADDR_CMD_VCMD2	(0x1 << 18)	/* Valid Cycle 2 Command */
> +#define NFCADDR_CMD_ACYCLE	(0x7 << 19)	/* Number of Address required */
> +#define   NFCADDR_CMD_ACYCLE_NONE	(0x0 << 19)
> +#define   NFCADDR_CMD_ACYCLE_1		(0x1 << 19)
> +#define   NFCADDR_CMD_ACYCLE_2		(0x2 << 19)
> +#define   NFCADDR_CMD_ACYCLE_3		(0x3 << 19)
> +#define   NFCADDR_CMD_ACYCLE_4		(0x4 << 19)
> +#define   NFCADDR_CMD_ACYCLE_5		(0x5 << 19)
> +#define NFCADDR_CMD_CSID	(0x7 << 22)	/* Chip Select Identifier */
> +#define   NFCADDR_CMD_CSID_0		(0x0 << 22)
> +#define   NFCADDR_CMD_CSID_1		(0x1 << 22)
> +#define   NFCADDR_CMD_CSID_2		(0x2 << 22)
> +#define   NFCADDR_CMD_CSID_3		(0x3 << 22)
> +#define   NFCADDR_CMD_CSID_4		(0x4 << 22)
> +#define   NFCADDR_CMD_CSID_5		(0x5 << 22)
> +#define   NFCADDR_CMD_CSID_6		(0x6 << 22)
> +#define   NFCADDR_CMD_CSID_7		(0x7 << 22)
> +#define NFCADDR_CMD_DATAEN	(0x1 << 25)	/* Data Transfer Enable */
> +#define NFCADDR_CMD_DATADIS	(0x0 << 25)	/* Data Transfer Disable */
> +#define NFCADDR_CMD_NFCRD	(0x0 << 26)	/* NFC Read Enable */
> +#define NFCADDR_CMD_NFCWR	(0x1 << 26)	/* NFC Write Enable */
> +#define NFCADDR_CMD_NFCBUSY	(0x1 << 27)	/* NFC Busy */
> +
> +#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \
> +	writel((addr1234), (cmd) + nfc_base)
> +
> +#define nfc_cmd_readl(bitstatus, nfc_base) \
> +	readl_relaxed((bitstatus) + nfc_base)
> +
> +#define NFC_TIME_OUT_MS		100
> +
> +#endif
> -- 
> 1.7.9.5
> 



More information about the linux-arm-kernel mailing list