[PATCH 3/6] mtd: atmel_nand: add Nand Flash Controller (NFC) support

Jean-Christophe PLAGNIOL-VILLARD plagnioj at jcrosoft.com
Mon Jun 10 09:22:11 EDT 2013


> +
>  /*
>   * Minimal-overhead PIO for data access.
>   */
> @@ -1341,6 +1409,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
>  
>  	host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
>  
> +	/* load the nfc driver if there is */
> +	of_platform_populate(np, NULL, NULL, host->dev);

what is this?????


> +
>  	if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
>  		return 0;	/* Not using PMECC */
>  
> @@ -1452,6 +1523,238 @@ 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_HANDLED;
> +
> +	status = nfc_readl(host->nfc->hsmc_regs, SR);
> +	mask = nfc_readl(host->nfc->hsmc_regs, IMR);
> +	pending = status & mask;
> +
> +	if (pending & NFC_SR_XFR_DONE) {
> +		complete(&host->nfc->comp_nfc);
> +		nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
> +	} else if (pending & NFC_SR_RB_EDGE) {
> +		complete(&host->nfc->comp_nfc);
> +		nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE);
> +	} else if (pending & NFC_SR_CMD_DONE) {
> +		complete(&host->nfc->comp_nfc);
> +		nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE);
> +	} else {
> +		ret = IRQ_NONE;
> +	}
> +
> +	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->nfc->comp_nfc);
> +
> +	/* Enable interrupt that need to wait for */
> +	nfc_writel(host->nfc->hsmc_regs, IER, flag);
> +
> +	timeout = wait_for_completion_timeout(&host->nfc->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");
> +			return -ETIMEDOUT;
> +		}
> +	}
> +	nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0);
> +	nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs);
> +	return nfc_wait_interrupt(host, NFC_SR_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, NFC_SR_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, NFC_CTRL_DISABLE);
> +	else
> +		nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
> +}
> +
> +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, NFC_SR_RB_EDGE);
> +	}
> +}
> +
> +static struct platform_driver atmel_nand_nfc_driver;
>  /*
>   * Probe for the NAND device.
>   */
> @@ -1462,7 +1765,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);
> @@ -1478,6 +1781,10 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  
> +	res = platform_driver_register(&atmel_nand_nfc_driver);
> +	if (res)
> +		printk(KERN_ERR "atmel_nand: can't register NFC driver\n");
> +

and this????
>  	host->io_base = devm_request_and_ioremap(&pdev->dev, mem);
>  	if (host->io_base == NULL) {
>  		printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -1505,7 +1812,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)) {
> @@ -1514,44 +1820,34 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>  		goto err_nand_ioremap;
>  	}
>  
Best Regards,
J.



More information about the linux-mtd mailing list