All of lore.kernel.org
 help / color / mirror / Atom feed
From: Brian Norris <computersforpeace@gmail.com>
To: Punnaiah Choudary Kalluri <punnaiah.choudary.kalluri@xilinx.com>
Cc: dwmw2@infradead.org, geert@linux-m68k.org, michals@xilinx.com,
	wangzhou1@hisilicon.com, f.fainelli@gmail.com, gsi@denx.de,
	andriy.shevchenko@linux.intel.com, haokexin@gmail.com,
	rogerq@ti.com, linux-kernel@vger.kernel.org,
	linux-mtd@lists.infradead.org, kalluripunnaiahchoudary@gmail.com,
	kpc528@gmail.com, Punnaiah Choudary Kalluri <punnaia@xilinx.com>,
	Boris Brezillon <boris.brezillon@free-electrons.com>
Subject: Re: [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller
Date: Mon, 7 Mar 2016 16:07:12 -0800	[thread overview]
Message-ID: <20160308000712.GD55664@google.com> (raw)
In-Reply-To: <1448116788-9802-1-git-send-email-punnaia@xilinx.com>

+ Boris

Punnaiah,

Can you fix the threading on your mail client? Your patch series does
not appear as a thread, because you didn't get the mail headers right
(References and In-Reply-To). If you're having trouble, try git-send-email.

On Sat, Nov 21, 2015 at 08:09:48PM +0530, Punnaiah Choudary Kalluri wrote:
> Added the basic driver for Arasan Nand Flash Controller used in
> Zynq UltraScale+ MPSoC. It supports only Hw Ecc and upto 24bit
> correction.
> 
> Signed-off-by: Punnaiah Choudary Kalluri <punnaia@xilinx.com>
> ---
> Changes in v5:
> - Renamed the driver filei as arasan_nand.c 
> - Fixed all comments relaqted coding style
> - Fixed comments related to propagating the errors
> - Modified the anfc_write_page_hwecc as per the write_page
>   prototype
> Changes in v4:
> - Added support for onfi timing mode configuration
> - Added clock supppport
> - Added support for multiple chipselects
> Changes in v3:
> - Removed unused variables
> - Avoided busy loop and used jifies based implementation
> - Fixed compiler warnings "right shift count >= width of type"
> - Removed unneeded codei and improved error reporting
> - Added onfi version check to ensure reading the valid address cycles
> Changes in v2:
> - Added missing of.h to avoid kbuild system report error
> ---
>  drivers/mtd/nand/Kconfig       |    6 +
>  drivers/mtd/nand/Makefile      |    1 +
>  drivers/mtd/nand/arasan_nand.c | 1010 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1017 insertions(+)
>  create mode 100644 drivers/mtd/nand/arasan_nand.c
> 
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 2896640..9c620fb 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -546,4 +546,10 @@ config MTD_NAND_HISI504
>  	help
>  	  Enables support for NAND controller on Hisilicon SoC Hip04.
>  
> +config MTD_NAND_ARASAN

I think you have some missing dependencies here, like HAS_IOMEM.

> +	tristate "Support for Arasan Nand Flash controller"
> +	help
> +	  Enables the driver for the Arasan Nand Flash controller on
> +	  Zynq UltraScale+ MPSoC.
> +
>  endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 2c7f014..3b993cb 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
>  obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
>  obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
>  obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
> +obj-$(CONFIG_MTD_NAND_ARASAN)		+= arasan_nand.o
>  
>  nand-objs := nand_base.o nand_bbt.o nand_timings.o
> diff --git a/drivers/mtd/nand/arasan_nand.c b/drivers/mtd/nand/arasan_nand.c
> new file mode 100644
> index 0000000..e882e63
> --- /dev/null
> +++ b/drivers/mtd/nand/arasan_nand.c
> @@ -0,0 +1,1010 @@
> +/*
> + * Arasan Nand Flash Controller Driver
> + *
> + * Copyright (C) 2014 - 2015 Xilinx, Inc.
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/of.h>
> +#include <linux/of_mtd.h>
> +#include <linux/platform_device.h>
> +
> +#define DRIVER_NAME			"arasan_nand"
> +#define EVNT_TIMEOUT			1000
> +#define STATUS_TIMEOUT			2000
> +
> +#define PKT_OFST			0x00
> +#define MEM_ADDR1_OFST			0x04
> +#define MEM_ADDR2_OFST			0x08
> +#define CMD_OFST			0x0C
> +#define PROG_OFST			0x10
> +#define INTR_STS_EN_OFST		0x14
> +#define INTR_SIG_EN_OFST		0x18
> +#define INTR_STS_OFST			0x1C
> +#define READY_STS_OFST			0x20
> +#define DMA_ADDR1_OFST			0x24
> +#define FLASH_STS_OFST			0x28
> +#define DATA_PORT_OFST			0x30
> +#define ECC_OFST			0x34
> +#define ECC_ERR_CNT_OFST		0x38
> +#define ECC_SPR_CMD_OFST		0x3C
> +#define ECC_ERR_CNT_1BIT_OFST		0x40
> +#define ECC_ERR_CNT_2BIT_OFST		0x44
> +#define DMA_ADDR0_OFST			0x50
> +#define DATA_INTERFACE_REG		0x6C
> +
> +#define PKT_CNT_SHIFT			12
> +
> +#define ECC_ENABLE			BIT(31)
> +#define DMA_EN_MASK			GENMASK(27, 26)
> +#define DMA_ENABLE			0x2
> +#define DMA_EN_SHIFT			26
> +#define REG_PAGE_SIZE_MASK		GENMASK(25, 23)
> +#define REG_PAGE_SIZE_SHIFT		23
> +#define REG_PAGE_SIZE_512		0
> +#define REG_PAGE_SIZE_1K		5
> +#define REG_PAGE_SIZE_2K		1
> +#define REG_PAGE_SIZE_4K		2
> +#define REG_PAGE_SIZE_8K		3
> +#define REG_PAGE_SIZE_16K		4
> +#define CMD2_SHIFT			8
> +#define ADDR_CYCLES_SHIFT		28
> +
> +#define XFER_COMPLETE			BIT(2)
> +#define READ_READY			BIT(1)
> +#define WRITE_READY			BIT(0)
> +#define MBIT_ERROR			BIT(3)
> +#define ERR_INTRPT			BIT(4)
> +
> +#define PROG_PGRD			BIT(0)
> +#define PROG_ERASE			BIT(2)
> +#define PROG_STATUS			BIT(3)
> +#define PROG_PGPROG			BIT(4)
> +#define PROG_RDID			BIT(6)
> +#define PROG_RDPARAM			BIT(7)
> +#define PROG_RST			BIT(8)
> +#define PROG_GET_FEATURE		BIT(9)
> +#define PROG_SET_FEATURE		BIT(10)
> +
> +#define ONFI_STATUS_FAIL		BIT(0)
> +#define ONFI_STATUS_READY		BIT(6)
> +
> +#define PG_ADDR_SHIFT			16
> +#define BCH_MODE_SHIFT			25
> +#define BCH_EN_SHIFT			27
> +#define ECC_SIZE_SHIFT			16
> +
> +#define MEM_ADDR_MASK			GENMASK(7, 0)
> +#define BCH_MODE_MASK			GENMASK(27, 25)
> +
> +#define CS_MASK				GENMASK(31, 30)
> +#define CS_SHIFT			30
> +
> +#define PAGE_ERR_CNT_MASK		GENMASK(16, 8)
> +#define PKT_ERR_CNT_MASK		GENMASK(7, 0)
> +
> +#define NVDDR_MODE			BIT(9)
> +#define NVDDR_TIMING_MODE_SHIFT		3
> +
> +#define ONFI_ID_LEN			8
> +#define TEMP_BUF_SIZE			512
> +#define NVDDR_MODE_PACKET_SIZE		8
> +#define SDR_MODE_PACKET_SIZE		4
> +
> +/**
> + * struct anfc_ecc_matrix - Defines ecc information storage format
> + * @pagesize:		Page size in bytes.
> + * @codeword_size:	Code word size information.
> + * @eccbits:		Number of ecc bits.
> + * @bch:		Bch / Hamming mode enable/disable.
> + * @eccsize:		Ecc size information.
> + */
> +struct anfc_ecc_matrix {
> +	u32 pagesize;
> +	u32 codeword_size;
> +	u8 eccbits;
> +	u8 bch;
> +	u16 eccsize;
> +};
> +
> +static const struct anfc_ecc_matrix ecc_matrix[] = {
> +	{512,	512,	1,	0,	0x3},
> +	{512,	512,	4,	1,	0x7},
> +	{512,	512,	8,	1,	0xD},
> +	/* 2K byte page */
> +	{2048,	512,	1,	0,	0xC},
> +	{2048,	512,	4,	1,	0x1A},
> +	{2048,	512,	8,	1,	0x34},
> +	{2048,	512,	12,	1,	0x4E},
> +	{2048,	1024,	24,	1,	0x54},
> +	/* 4K byte page */
> +	{4096,	512,	1,	0,	0x18},
> +	{4096,	512,	4,	1,	0x34},
> +	{4096,	512,	8,	1,	0x68},
> +	{4096,	512,	12,	1,	0x9C},
> +	{4096,	1024,	4,	1,	0xA8},
> +	/* 8K byte page */
> +	{8192,	512,	1,	0,	0x30},
> +	{8192,	512,	4,	1,	0x68},
> +	{8192,	512,	8,	1,	0xD0},
> +	{8192,	512,	12,	1,	0x138},
> +	{8192,	1024,	24,	1,	0x150},
> +	/* 16K byte page */
> +	{16384,	512,	1,	0,	0x60},
> +	{16384,	512,	4,	1,	0xD0},
> +	{16384,	512,	8,	1,	0x1A0},
> +	{16384,	512,	12,	1,	0x270},
> +	{16384,	1024,	24,	1,	0x2A0}
> +};
> +
> +/**
> + * struct anfc - Defines the Arasan NAND flash driver instance
> + * @chip:		NAND chip information structure.
> + * @mtd:		MTD information structure.
> + * @dev:		Pointer to the device structure.
> + * @base:		Virtual address of the NAND flash device.
> + * @curr_cmd:		Current command issued.
> + * @clk_sys:		Pointer to the system clock.
> + * @clk_flash:		Pointer to the flash clock.
> + * @dma:		Dma enable/disable.
> + * @bch:		Bch / Hamming mode enable/disable.
> + * @err:		Error identifier.
> + * @iswriteoob:		Identifies if oob write operation is required.
> + * @buf:		Buffer used for read/write byte operations.
> + * @raddr_cycles:	Row address cycle information.
> + * @caddr_cycles:	Column address cycle information.
> + * @irq:		irq number
> + * @pktsize:		Packet size for read / write operation.
> + * @bufshift:		Variable used for indexing buffer operation
> + * @rdintrmask:		Interrupt mask value for read operation.
> + * @num_cs:		Number of chip selects in use.
> + * @spktsize:		Packet size in ddr mode for status operation.
> + * @bufrdy:		Completion event for buffer ready.
> + * @xfercomp:		Completion event for transfer complete.
> + * @ecclayout:		Ecc layout object
> + */
> +struct anfc {
> +	struct nand_chip chip;
> +	struct mtd_info mtd;
> +	struct device *dev;
> +
> +	void __iomem *base;
> +	int curr_cmd;
> +	struct clk *clk_sys;
> +	struct clk *clk_flash;
> +
> +	bool dma;
> +	bool bch;
> +	bool err;
> +	bool iswriteoob;
> +
> +	u8 buf[TEMP_BUF_SIZE];
> +
> +	u16 raddr_cycles;
> +	u16 caddr_cycles;
> +
> +	u32 irq;
> +	u32 pktsize;
> +	u32 bufshift;
> +	u32 rdintrmask;
> +	u32 num_cs;
> +	u32 spktsize;
> +
> +	struct completion bufrdy;
> +	struct completion xfercomp;
> +	struct nand_ecclayout ecclayout;
> +};
> +
> +static inline struct anfc *to_anfc(struct mtd_info *mtd)
> +{
> +	return container_of(mtd, struct anfc, mtd);
> +}
> +
> +static u8 anfc_page(u32 pagesize)
> +{
> +	switch (pagesize) {
> +	case 512:
> +		return REG_PAGE_SIZE_512;
> +	case 1024:
> +		return REG_PAGE_SIZE_1K;
> +	case 2048:
> +		return REG_PAGE_SIZE_2K;
> +	case 4096:
> +		return REG_PAGE_SIZE_4K;
> +	case 8192:
> +		return REG_PAGE_SIZE_8K;
> +	case 16384:
> +		return REG_PAGE_SIZE_16K;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void anfc_enable_intrs(struct anfc *nfc, u32 val)
> +{
> +	writel(val, nfc->base + INTR_STS_EN_OFST);
> +	writel(val, nfc->base + INTR_SIG_EN_OFST);
> +}
> +
> +static int anfc_wait_for_event(struct anfc *nfc, u32 event)
> +{
> +	struct completion *comp;
> +
> +	if (event == XFER_COMPLETE)
> +		comp = &nfc->xfercomp;
> +	else
> +		comp = &nfc->bufrdy;
> +
> +	return wait_for_completion_timeout(comp,
> +					msecs_to_jiffies(EVNT_TIMEOUT));
> +}
> +
> +static inline void anfc_setpktszcnt(struct anfc *nfc, u32 pktsize,
> +				    u32 pktcount)
> +{
> +	writel(pktsize | (pktcount << PKT_CNT_SHIFT), nfc->base + PKT_OFST);
> +}
> +
> +static inline void anfc_set_eccsparecmd(struct anfc *nfc, u8 cmd1, u8 cmd2)
> +{
> +	writel(cmd1 | (cmd2 << CMD2_SHIFT) |
> +	       (nfc->caddr_cycles << ADDR_CYCLES_SHIFT),
> +	       nfc->base + ECC_SPR_CMD_OFST);
> +}
> +
> +static void anfc_setpagecoladdr(struct anfc *nfc, u32 page, u16 col)
> +{
> +	u32 val;
> +
> +	writel(col | (page << PG_ADDR_SHIFT), nfc->base + MEM_ADDR1_OFST);
> +
> +	val = readl(nfc->base + MEM_ADDR2_OFST);
> +	val = (val & ~MEM_ADDR_MASK) |
> +	      ((page >> PG_ADDR_SHIFT) & MEM_ADDR_MASK);
> +	writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static void anfc_prepare_cmd(struct anfc *nfc, u8 cmd1, u8 cmd2,
> +			     u8 dmamode, u32 pagesize, u8 addrcycles)
> +{
> +	u32 regval;
> +
> +	regval = cmd1 | (cmd2 << CMD2_SHIFT);
> +	if (dmamode && nfc->dma)
> +		regval |= DMA_ENABLE << DMA_EN_SHIFT;
> +	if (addrcycles)
> +		regval |= addrcycles << ADDR_CYCLES_SHIFT;
> +	if (pagesize)
> +		regval |= anfc_page(pagesize) << REG_PAGE_SIZE_SHIFT;
> +	writel(regval, nfc->base + CMD_OFST);
> +}
> +
> +static int anfc_device_ready(struct mtd_info *mtd,
> +			     struct nand_chip *chip)
> +{
> +	u8 status;
> +	unsigned long timeout = jiffies + STATUS_TIMEOUT;
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	do {
> +		chip->cmdfunc(mtd, NAND_CMD_STATUS, 0, 0);
> +		status = chip->read_byte(mtd);
> +		if (status & ONFI_STATUS_READY)
> +			break;
> +		cpu_relax();
> +	} while (!time_after_eq(jiffies, timeout));
> +
> +	if (status & ONFI_STATUS_FAIL)
> +		return NAND_STATUS_FAIL;
> +
> +	if (time_after_eq(jiffies, timeout)) {
> +		dev_err(nfc->dev, "%s timed out\n", __func__);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int anfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
> +			 int page)
> +{
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> +	if (nfc->dma)
> +		nfc->rdintrmask = XFER_COMPLETE;
> +	else
> +		nfc->rdintrmask = READ_READY;
> +	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> +	return 0;
> +}
> +
> +static int anfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
> +			  int page)
> +{
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	nfc->iswriteoob = true;
> +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
> +	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
> +	nfc->iswriteoob = false;
> +
> +	return 0;
> +}
> +
> +static void anfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> +	u32 pktcount, pktsize;
> +	unsigned int buf_rd_cnt = 0;
> +	u32 *bufptr = (u32 *)buf;
> +	struct anfc *nfc = to_anfc(mtd);
> +	dma_addr_t paddr;
> +
> +	if (nfc->curr_cmd == NAND_CMD_READ0) {
> +		pktsize = nfc->pktsize;
> +		pktcount = DIV_ROUND_UP(mtd->writesize, pktsize);
> +	} else {
> +		pktsize = len;
> +		pktcount = 1;
> +	}
> +
> +	anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> +	if (nfc->dma) {
> +		paddr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
> +		if (dma_mapping_error(nfc->dev, paddr)) {
> +			dev_err(nfc->dev, "Read buffer mapping error");
> +			return;
> +		}
> +		lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> +		anfc_enable_intrs(nfc, nfc->rdintrmask);
> +		writel(PROG_PGRD, nfc->base + PROG_OFST);
> +		anfc_wait_for_event(nfc, XFER_COMPLETE);
> +		dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
> +		return;
> +	}
> +
> +	anfc_enable_intrs(nfc, nfc->rdintrmask);
> +	writel(PROG_PGRD, nfc->base + PROG_OFST);
> +
> +	while (buf_rd_cnt < pktcount) {
> +
> +		anfc_wait_for_event(nfc, READ_READY);
> +		buf_rd_cnt++;
> +
> +		if (buf_rd_cnt == pktcount)
> +			anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> +		readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> +		bufptr += pktsize/4;
> +
> +		if (buf_rd_cnt < pktcount)
> +			anfc_enable_intrs(nfc, nfc->rdintrmask);
> +	}
> +
> +	anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
> +{
> +	u32 pktcount, pktsize;
> +	unsigned int buf_wr_cnt = 0;
> +	u32 *bufptr = (u32 *)buf;
> +	struct anfc *nfc = to_anfc(mtd);
> +	dma_addr_t paddr;
> +
> +	if (nfc->iswriteoob) {
> +		pktsize = len;
> +		pktcount = 1;
> +	} else {
> +		pktsize = nfc->pktsize;
> +		pktcount = mtd->writesize / pktsize;
> +	}
> +
> +	anfc_setpktszcnt(nfc, pktsize, pktcount);
> +
> +	if (nfc->dma) {
> +		paddr = dma_map_single(nfc->dev, (void *)buf, len,
> +				       DMA_TO_DEVICE);
> +		if (dma_mapping_error(nfc->dev, paddr)) {
> +			dev_err(nfc->dev, "Write buffer mapping error");
> +			return;
> +		}
> +		lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
> +		anfc_enable_intrs(nfc, XFER_COMPLETE);
> +		writel(PROG_PGPROG, nfc->base + PROG_OFST);
> +		anfc_wait_for_event(nfc, XFER_COMPLETE);
> +		dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
> +		return;
> +	}
> +
> +	anfc_enable_intrs(nfc, WRITE_READY);
> +	writel(PROG_PGPROG, nfc->base + PROG_OFST);
> +
> +	while (buf_wr_cnt < pktcount) {
> +		anfc_wait_for_event(nfc, WRITE_READY);
> +
> +		buf_wr_cnt++;
> +		if (buf_wr_cnt == pktcount)
> +			anfc_enable_intrs(nfc, XFER_COMPLETE);
> +
> +		writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
> +		bufptr += pktsize/4;
> +
> +		if (buf_wr_cnt < pktcount)
> +			anfc_enable_intrs(nfc, WRITE_READY);
> +	}
> +
> +	anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static int anfc_read_page_hwecc(struct mtd_info *mtd,
> +				struct nand_chip *chip, uint8_t *buf,
> +				int oob_required, int page)
> +{
> +	u32 val;
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	anfc_set_eccsparecmd(nfc, NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART);
> +
> +	val = readl(nfc->base + CMD_OFST);
> +	val = val | ECC_ENABLE;
> +	writel(val, nfc->base + CMD_OFST);
> +
> +	if (nfc->dma)
> +		nfc->rdintrmask = XFER_COMPLETE;
> +	else
> +		nfc->rdintrmask = READ_READY;
> +
> +	if (!nfc->bch)
> +		nfc->rdintrmask = MBIT_ERROR;
> +
> +	chip->read_buf(mtd, buf, mtd->writesize);
> +
> +	val = readl(nfc->base + ECC_ERR_CNT_OFST);
> +	if (nfc->bch) {
> +		mtd->ecc_stats.corrected += val & PAGE_ERR_CNT_MASK;
> +	} else {
> +		val = readl(nfc->base + ECC_ERR_CNT_1BIT_OFST);
> +		mtd->ecc_stats.corrected += val;
> +		val = readl(nfc->base + ECC_ERR_CNT_2BIT_OFST);
> +		mtd->ecc_stats.failed += val;
> +		/* Clear ecc error count register 1Bit, 2Bit */
> +		writel(0x0, nfc->base + ECC_ERR_CNT_1BIT_OFST);
> +		writel(0x0, nfc->base + ECC_ERR_CNT_2BIT_OFST);
> +	}
> +	nfc->err = false;
> +
> +	if (oob_required)
> +		chip->ecc.read_oob(mtd, chip, page);
> +
> +	return 0;
> +}
> +
> +static int anfc_write_page_hwecc(struct mtd_info *mtd,
> +				 struct nand_chip *chip, const uint8_t *buf,
> +				 int oob_required, int page)
> +{
> +	u32 val;
> +	unsigned int i;
> +	struct anfc *nfc = to_anfc(mtd);
> +	uint8_t *ecc_calc = chip->buffers->ecccalc;
> +	uint32_t *eccpos = chip->ecc.layout->eccpos;
> +
> +	anfc_set_eccsparecmd(nfc, NAND_CMD_RNDIN, 0);
> +
> +	val = readl(nfc->base + CMD_OFST);
> +	val = val | ECC_ENABLE;
> +	writel(val, nfc->base + CMD_OFST);
> +
> +	chip->write_buf(mtd, buf, mtd->writesize);
> +
> +	if (oob_required) {
> +		anfc_device_ready(mtd, chip);
> +		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> +		if (nfc->dma)
> +			nfc->rdintrmask = XFER_COMPLETE;
> +		else
> +			nfc->rdintrmask = READ_READY;
> +		chip->read_buf(mtd, ecc_calc, mtd->oobsize);
> +		for (i = 0; i < chip->ecc.total; i++)
> +			chip->oob_poi[eccpos[i]] = ecc_calc[eccpos[i]];
> +		chip->ecc.write_oob(mtd, chip, page);
> +	}
> +
> +	return 0;
> +}
> +
> +static u8 anfc_read_byte(struct mtd_info *mtd)
> +{
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	return nfc->buf[nfc->bufshift++];
> +}
> +
> +static void anfc_writefifo(struct anfc *nfc, u32 prog, u32 size, u8 *buf)
> +{
> +	u32 *bufptr = (u32 *)buf;
> +
> +	anfc_enable_intrs(nfc, WRITE_READY);
> +
> +	writel(prog, nfc->base + PROG_OFST);
> +	anfc_wait_for_event(nfc, WRITE_READY);
> +
> +	anfc_enable_intrs(nfc, XFER_COMPLETE);
> +	writesl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> +	anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static void anfc_readfifo(struct anfc *nfc, u32 prog, u32 size)
> +{
> +	u32 *bufptr = (u32 *)nfc->buf;
> +
> +	anfc_enable_intrs(nfc, READ_READY);
> +
> +	writel(prog, nfc->base + PROG_OFST);
> +	anfc_wait_for_event(nfc, READ_READY);
> +
> +	anfc_enable_intrs(nfc, XFER_COMPLETE);
> +	readsl(nfc->base + DATA_PORT_OFST, bufptr, size/4);
> +	anfc_wait_for_event(nfc, XFER_COMPLETE);
> +}
> +
> +static int anfc_ecc_init(struct mtd_info *mtd,
> +			 struct nand_ecc_ctrl *ecc)
> +{
> +	u32 ecc_addr, regval;
> +	unsigned int bchmode = 0, i, oob_index;
> +	struct nand_chip *nand_chip = mtd->priv;
> +	struct anfc *nfc = to_anfc(mtd);
> +	int found = -1;
> +
> +	nand_chip->ecc.mode = NAND_ECC_HW;
> +	nand_chip->ecc.read_page = anfc_read_page_hwecc;
> +	nand_chip->ecc.write_page = anfc_write_page_hwecc;
> +	nand_chip->ecc.write_oob = anfc_write_oob;
> +	nand_chip->ecc.read_oob = anfc_read_oob;
> +
> +	for (i = 0; i < ARRAY_SIZE(ecc_matrix); i++) {
> +		if ((ecc_matrix[i].pagesize == mtd->writesize) &&
> +		    (ecc_matrix[i].codeword_size >= nand_chip->ecc_step_ds)) {
> +			found = i;
> +			if (ecc_matrix[i].eccbits >= nand_chip->ecc_strength_ds)
> +				break;
> +		}
> +	}
> +
> +	if (found < 0) {
> +		dev_err(nfc->dev, "ECC scheme not supported");
> +		return 1;
> +	}
> +	if (ecc_matrix[found].bch) {
> +		switch (ecc_matrix[found].eccbits) {
> +		case 12:
> +			bchmode = 0x1;
> +			break;
> +		case 8:
> +			bchmode = 0x2;
> +			break;
> +		case 4:
> +			bchmode = 0x3;
> +			break;
> +		case 24:
> +			bchmode = 0x4;
> +			break;
> +		default:
> +			bchmode = 0x0;
> +		}
> +	}
> +
> +	nand_chip->ecc.strength = ecc_matrix[found].eccbits;
> +	nand_chip->ecc.size = ecc_matrix[found].codeword_size;
> +	nand_chip->ecc.steps = ecc_matrix[found].pagesize /
> +			       ecc_matrix[found].codeword_size;
> +	nand_chip->ecc.bytes = ecc_matrix[found].eccsize /
> +			       nand_chip->ecc.steps;
> +	nfc->ecclayout.eccbytes = ecc_matrix[found].eccsize;
> +	nfc->bch = ecc_matrix[found].bch;
> +	oob_index = mtd->oobsize - nfc->ecclayout.eccbytes;
> +	ecc_addr = mtd->writesize + oob_index;
> +
> +	for (i = 0; i < nand_chip->ecc.size; i++)
> +		nfc->ecclayout.eccpos[i] = oob_index + i;
> +
> +	nfc->ecclayout.oobfree->offset = 2;
> +	nfc->ecclayout.oobfree->length = oob_index -
> +					 nfc->ecclayout.oobfree->offset;
> +
> +	nand_chip->ecc.layout = &nfc->ecclayout;

FYI, we're deprecating this usage of ecclayout. You might take a look at Boris'
latest (which we'll probably merge very soon):

http://lists.infradead.org/pipermail/linux-mtd/2016-March/065925.html

> +	regval = ecc_addr | (ecc_matrix[found].eccsize << ECC_SIZE_SHIFT) |
> +		 (ecc_matrix[found].bch << BCH_EN_SHIFT);
> +	writel(regval, nfc->base + ECC_OFST);
> +
> +	regval = readl(nfc->base + MEM_ADDR2_OFST);
> +	regval = (regval & ~(BCH_MODE_MASK)) | (bchmode << BCH_MODE_SHIFT);
> +	writel(regval, nfc->base + MEM_ADDR2_OFST);
> +
> +	if (nand_chip->ecc_step_ds >= 1024)
> +		nfc->pktsize = 1024;
> +	else
> +		nfc->pktsize = 512;
> +
> +	return 0;
> +}
> +
> +static void anfc_cmd_function(struct mtd_info *mtd,
> +			      unsigned int cmd, int column, int page_addr)
> +{
> +	struct anfc *nfc = to_anfc(mtd);
> +	bool wait = false, read = false;
> +	u32 addrcycles, prog;
> +	u32 *bufptr = (u32 *)nfc->buf;
> +
> +	nfc->bufshift = 0;
> +	nfc->curr_cmd = cmd;
> +
> +	if (page_addr == -1)
> +		page_addr = 0;
> +	if (column == -1)
> +		column = 0;
> +
> +	switch (cmd) {
> +	case NAND_CMD_RESET:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> +		prog = PROG_RST;
> +		wait = true;
> +		break;
> +	case NAND_CMD_SEQIN:
> +		addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> +		anfc_prepare_cmd(nfc, cmd, NAND_CMD_PAGEPROG, 1,
> +				 mtd->writesize, addrcycles);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		break;
> +	case NAND_CMD_READOOB:
> +		column += mtd->writesize;
> +	case NAND_CMD_READ0:
> +	case NAND_CMD_READ1:
> +		addrcycles = nfc->raddr_cycles + nfc->caddr_cycles;
> +		anfc_prepare_cmd(nfc, NAND_CMD_READ0, NAND_CMD_READSTART, 1,
> +				 mtd->writesize, addrcycles);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		break;
> +	case NAND_CMD_RNDOUT:
> +		anfc_prepare_cmd(nfc, cmd, NAND_CMD_RNDOUTSTART, 1,
> +				 mtd->writesize, 2);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		if (nfc->dma)
> +			nfc->rdintrmask = XFER_COMPLETE;
> +		else
> +			nfc->rdintrmask = READ_READY;
> +		break;
> +	case NAND_CMD_PARAM:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		anfc_setpktszcnt(nfc, sizeof(struct nand_onfi_params), 1);
> +		anfc_readfifo(nfc, PROG_RDPARAM,
> +				sizeof(struct nand_onfi_params));
> +		break;
> +	case NAND_CMD_READID:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		anfc_setpktszcnt(nfc, ONFI_ID_LEN, 1);
> +		anfc_readfifo(nfc, PROG_RDID, ONFI_ID_LEN);
> +		break;
> +	case NAND_CMD_ERASE1:
> +		addrcycles = nfc->raddr_cycles;
> +		prog = PROG_ERASE;
> +		anfc_prepare_cmd(nfc, cmd, NAND_CMD_ERASE2, 0, 0, addrcycles);
> +		column = page_addr & 0xffff;
> +		page_addr = (page_addr >> PG_ADDR_SHIFT) & 0xffff;
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		wait = true;
> +		break;
> +	case NAND_CMD_STATUS:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 0);
> +		anfc_setpktszcnt(nfc, nfc->spktsize/4, 1);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		prog = PROG_STATUS;
> +		wait = read = true;
> +		break;
> +	case NAND_CMD_GET_FEATURES:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> +		anfc_readfifo(nfc, PROG_GET_FEATURE, 4);
> +		break;
> +	case NAND_CMD_SET_FEATURES:
> +		anfc_prepare_cmd(nfc, cmd, 0, 0, 0, 1);
> +		anfc_setpagecoladdr(nfc, page_addr, column);
> +		anfc_setpktszcnt(nfc, nfc->spktsize, 1);
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	if (wait) {
> +		anfc_enable_intrs(nfc, XFER_COMPLETE);
> +		writel(prog, nfc->base + PROG_OFST);
> +		anfc_wait_for_event(nfc, XFER_COMPLETE);
> +	}
> +
> +	if (read)
> +		bufptr[0] = readl(nfc->base + FLASH_STS_OFST);
> +}
> +
> +static void anfc_select_chip(struct mtd_info *mtd, int num)
> +{
> +	u32 val;
> +	struct anfc *nfc = to_anfc(mtd);
> +
> +	if (num == -1)
> +		return;
> +
> +	val = readl(nfc->base + MEM_ADDR2_OFST);
> +	val = (val & ~(CS_MASK)) | (num << CS_SHIFT);
> +	writel(val, nfc->base + MEM_ADDR2_OFST);
> +}
> +
> +static irqreturn_t anfc_irq_handler(int irq, void *ptr)
> +{
> +	struct anfc *nfc = ptr;
> +	u32 regval = 0, status;
> +
> +	status = readl(nfc->base + INTR_STS_OFST);
> +	if (status & XFER_COMPLETE) {
> +		complete(&nfc->xfercomp);
> +		regval |= XFER_COMPLETE;
> +	}
> +
> +	if (status & READ_READY) {
> +		complete(&nfc->bufrdy);
> +		regval |= READ_READY;
> +	}
> +
> +	if (status & WRITE_READY) {
> +		complete(&nfc->bufrdy);
> +		regval |= WRITE_READY;
> +	}
> +
> +	if (status & MBIT_ERROR) {
> +		nfc->err = true;
> +		complete(&nfc->bufrdy);
> +		regval |= MBIT_ERROR;
> +	}
> +
> +	if (regval) {
> +		writel(regval, nfc->base + INTR_STS_OFST);
> +		writel(0, nfc->base + INTR_STS_EN_OFST);
> +		writel(0, nfc->base + INTR_SIG_EN_OFST);
> +
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static int anfc_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> +				int addr, uint8_t *subfeature_param)
> +{
> +	struct anfc *nfc = to_anfc(mtd);
> +	int status;
> +
> +	if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd)
> +		& ONFI_OPT_CMD_SET_GET_FEATURES))
> +		return -EINVAL;
> +
> +	chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
> +	anfc_writefifo(nfc, PROG_SET_FEATURE, nfc->spktsize, subfeature_param);
> +
> +	status = chip->waitfunc(mtd, chip);
> +	if (status & NAND_STATUS_FAIL)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static int anfc_init_timing_mode(struct anfc *nfc)
> +{
> +	int mode, err;
> +	unsigned int feature[2], regval, i;
> +	struct nand_chip *chip = &nfc->chip;
> +	struct mtd_info *mtd = &nfc->mtd;
> +
> +	memset(feature, 0, NVDDR_MODE_PACKET_SIZE);
> +	/* Get nvddr timing modes */
> +	mode = onfi_get_sync_timing_mode(chip) & 0xff;
> +	if (!mode) {
> +		mode = fls(onfi_get_async_timing_mode(&nfc->chip)) - 1;
> +		regval = mode;
> +	} else {
> +		mode = fls(mode) - 1;
> +		regval = NVDDR_MODE | mode << NVDDR_TIMING_MODE_SHIFT;
> +		mode |= ONFI_DATA_INTERFACE_NVDDR;
> +	}
> +
> +	feature[0] = mode;
> +	for (i = 0; i < nfc->num_cs; i++) {
> +		chip->select_chip(mtd, i);
> +		err = chip->onfi_set_features(mtd, chip,
> +					ONFI_FEATURE_ADDR_TIMING_MODE,
> +					(uint8_t *)feature);
> +		if (err)
> +			return err;
> +	}
> +	writel(regval, nfc->base + DATA_INTERFACE_REG);
> +
> +	if (mode & ONFI_DATA_INTERFACE_NVDDR)
> +		nfc->spktsize = NVDDR_MODE_PACKET_SIZE;
> +
> +	return 0;
> +}
> +
> +static int anfc_probe(struct platform_device *pdev)
> +{
> +	struct anfc *nfc;
> +	struct mtd_info *mtd;
> +	struct nand_chip *nand_chip;
> +	struct resource *res;
> +	struct mtd_part_parser_data ppdata;
> +	int err;
> +
> +	nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
> +	if (!nfc)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	nfc->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(nfc->base))
> +		return PTR_ERR(nfc->base);
> +
> +	mtd = &nfc->mtd;
> +	nand_chip = &nfc->chip;
> +	nand_chip->priv = nfc;
> +	mtd->priv = nand_chip;
> +	mtd->name = DRIVER_NAME;
> +	nfc->dev = &pdev->dev;
> +	mtd->dev.parent = &pdev->dev;
> +
> +	nand_chip->cmdfunc = anfc_cmd_function;
> +	nand_chip->waitfunc = anfc_device_ready;
> +	nand_chip->chip_delay = 30;
> +	nand_chip->read_buf = anfc_read_buf;
> +	nand_chip->write_buf = anfc_write_buf;
> +	nand_chip->read_byte = anfc_read_byte;
> +	nand_chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> +	nand_chip->bbt_options = NAND_BBT_USE_FLASH;
> +	nand_chip->select_chip = anfc_select_chip;
> +	nand_chip->onfi_set_features = anfc_onfi_set_features;
> +	nfc->dma = of_property_read_bool(pdev->dev.of_node,
> +					 "arasan,has-mdma");
> +	nfc->num_cs = 1;
> +	of_property_read_u32(pdev->dev.of_node, "num-cs", &nfc->num_cs);

I get the feeling that this device tree binding doesn't support multiple
chips very well, but I can't find the v5 binding to confirm...

Normally, we suggest that you represent the NAND chip separate from the
NAND controller. See, e.g.,
Documentation/devicetree/bindings/mtd/sunxi-nand.txt which has:

nfc: nand@01c03000 {
        compatible = "allwinner,sun4i-a10-nand";
        reg = <0x01c03000 0x1000>;
	...
        nand@0 {
                reg = <0>;
		...
	};

	// you could have nand@1, nand@2, etc.
};


> +	platform_set_drvdata(pdev, nfc);
> +	init_completion(&nfc->bufrdy);
> +	init_completion(&nfc->xfercomp);
> +	nfc->irq = platform_get_irq(pdev, 0);
> +	if (nfc->irq < 0) {

drivers/mtd/nand/arasan_nand.c: In function 'anfc_probe':
drivers/mtd/nand/arasan_nand.c:900:15: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits]
  if (nfc->irq < 0) {
               ^

> +		dev_err(&pdev->dev, "platform_get_irq failed\n");
> +		return -ENXIO;
> +	}
> +	err = devm_request_irq(&pdev->dev, nfc->irq, anfc_irq_handler,
> +			       0, "arasannfc", nfc);
> +	if (err)
> +		return err;
> +	nfc->clk_sys = devm_clk_get(&pdev->dev, "clk_sys");
> +	if (IS_ERR(nfc->clk_sys)) {
> +		dev_err(&pdev->dev, "sys clock not found.\n");
> +		return PTR_ERR(nfc->clk_sys);
> +	}
> +
> +	nfc->clk_flash = devm_clk_get(&pdev->dev, "clk_flash");
> +	if (IS_ERR(nfc->clk_flash)) {
> +		dev_err(&pdev->dev, "flash clock not found.\n");
> +		return PTR_ERR(nfc->clk_flash);
> +	}
> +
> +	err = clk_prepare_enable(nfc->clk_sys);
> +	if (err) {
> +		dev_err(&pdev->dev, "Unable to enable sys clock.\n");
> +		return err;
> +	}
> +
> +	err = clk_prepare_enable(nfc->clk_flash);
> +	if (err) {
> +		dev_err(&pdev->dev, "Unable to enable flash clock.\n");
> +		goto clk_dis_sys;
> +	}
> +
> +	nfc->spktsize = SDR_MODE_PACKET_SIZE;
> +	err = nand_scan_ident(mtd, nfc->num_cs, NULL);
> +	if (err) {
> +		dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
> +		goto clk_dis_all;
> +	}
> +	if (nand_chip->onfi_version) {
> +		nfc->raddr_cycles = nand_chip->onfi_params.addr_cycles & 0xf;
> +		nfc->caddr_cycles =
> +				(nand_chip->onfi_params.addr_cycles >> 4) & 0xf;
> +	} else {
> +		/*For non-ONFI devices, configuring the address cyles as 5 */

Space after /*

> +		nfc->raddr_cycles = nfc->caddr_cycles = 5;
> +	}
> +
> +	err = anfc_init_timing_mode(nfc);
> +	if (err) {
> +		dev_err(&pdev->dev, "timing mode init failed\n");
> +		goto clk_dis_all;
> +	}
> +
> +	err = anfc_ecc_init(mtd, &nand_chip->ecc);
> +	if (err)
> +		goto clk_dis_all;
> +
> +	err = nand_scan_tail(mtd);
> +	if (err) {
> +		dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
> +		goto clk_dis_all;
> +	}
> +
> +	ppdata.of_node = pdev->dev.of_node;

The of_node field is gone now. Just use nand_set_flash_node(). (Please rebase
on l2-mtd.git before submitting MTD changes. See:
http://linux-mtd.infradead.org/source.html )

> +
> +	err = mtd_device_parse_register(&nfc->mtd, NULL, &ppdata, NULL, 0);

Then, you won't need ppdata, so you can just do:

	err = mtd_device_register(&nfc->mtd, NULL, 0);

> +	if (err)
> +		goto clk_dis_all;
> +
> +	return 0;
> +
> +clk_dis_all:
> +	clk_disable_unprepare(nfc->clk_flash);
> +clk_dis_sys:
> +	clk_disable_unprepare(nfc->clk_sys);
> +
> +	return err;
> +}
> +
> +static int anfc_remove(struct platform_device *pdev)
> +{
> +	struct anfc *nfc = platform_get_drvdata(pdev);
> +
> +	clk_disable_unprepare(nfc->clk_sys);
> +	clk_disable_unprepare(nfc->clk_flash);
> +
> +	nand_release(&nfc->mtd);

Probably want to disable the clocks only after nand_release()?
Otherwise, you might have a race.

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id anfc_ids[] = {
> +	{ .compatible = "arasan,nfc-v3p10" },
> +	{  }
> +};
> +MODULE_DEVICE_TABLE(of, anfc_ids);
> +
> +static struct platform_driver anfc_driver = {
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.of_match_table = anfc_ids,
> +	},
> +	.probe = anfc_probe,
> +	.remove = anfc_remove,
> +};
> +module_platform_driver(anfc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xilinx, Inc");
> +MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");

Brian

  parent reply	other threads:[~2016-03-08  0:07 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-21 14:39 [PATCH v5 2/3] mtd: nand: Add support for Arasan Nand Flash Controller Punnaiah Choudary Kalluri
2016-01-18 14:14 ` punnaiah choudary kalluri
2016-03-08  0:07 ` Brian Norris [this message]
2016-03-08  2:47   ` punnaiah choudary kalluri
2016-03-08 14:38 ` Boris Brezillon
2016-03-08 18:40   ` punnaiah choudary kalluri
2016-03-09  9:50     ` Boris Brezillon
2016-12-05  9:01       ` Boris Brezillon
2016-12-05 16:49         ` Punnaiah Choudary Kalluri

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160308000712.GD55664@google.com \
    --to=computersforpeace@gmail.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=boris.brezillon@free-electrons.com \
    --cc=dwmw2@infradead.org \
    --cc=f.fainelli@gmail.com \
    --cc=geert@linux-m68k.org \
    --cc=gsi@denx.de \
    --cc=haokexin@gmail.com \
    --cc=kalluripunnaiahchoudary@gmail.com \
    --cc=kpc528@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mtd@lists.infradead.org \
    --cc=michals@xilinx.com \
    --cc=punnaia@xilinx.com \
    --cc=punnaiah.choudary.kalluri@xilinx.com \
    --cc=rogerq@ti.com \
    --cc=wangzhou1@hisilicon.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.