[PATCH v2 1/2] mtd: fsl-quadspi: add support to create dynamic LUT entry

Yogesh Narayan Gaur yogeshnarayan.gaur at nxp.com
Mon Jan 15 02:39:45 PST 2018


Checked performance with this patch set for Read, Write and Erase operation started using mtd_debug utility.
	time mtd_debug <ops> /dev/mtd0 0xc00000 0x100000

For Read/Write perform operation with data size of 1MB and for erase perform erase of 4MB data size.

For write and erase performance degradation is in range of ~(1-2)% for above data size
For read performance degradation is in range of ~(0-1)% for above data size.

--
Regards
Yogesh Gaur.

> -----Original Message-----
> From: Yogesh Gaur [mailto:yogeshnarayan.gaur at nxp.com]
> Sent: Monday, January 15, 2018 3:45 PM
> To: linux-mtd at lists.infradead.org
> Cc: boris.brezillon at free-electrons.com; cyrille.pitchen at wedev4u.fr;
> computersforpeace at gmail.com; Han Xu <han.xu at nxp.com>;
> festevam at gmail.com; Prabhakar Kushwaha <prabhakar.kushwaha at nxp.com>;
> Suresh Gupta <suresh.gupta at nxp.com>; Yogesh Narayan Gaur
> <yogeshnarayan.gaur at nxp.com>
> Subject: [PATCH v2 1/2] mtd: fsl-quadspi: add support to create dynamic LUT
> entry
> 
> Add support to create dynamic LUT entry.
> 
> Current approach of creating LUT entries for various cmds like read, write,
> erase, readid, readsr, we, wd etc is that when QSPI controller gets initialized at
> that time static LUT entries for these cmds get created.
> 
> Patch add support to create the LUT at run time based on the operation being
> performed.
> 
> Added API fsl_qspi_prepare_lut(), this API would going to be called from
> fsl_qspi_read_reg, fsl_qspi_write_reg, fsl_qspi_write, fsl_qspi_read and
> fsl_qspi_erase APIs.
> This API would fetch required info like opcode, protocol info, dummy info for
> creating LUT from instance of 'struct spi_nor' and then prepare LUT entry for the
> required command.
> 
> Signed-off-by: Yogesh Gaur <yogeshnarayan.gaur at nxp.com>
> 
> v1: Swap patch sequences in the series to solve git bissect issue.
> v2: Add STOP instruction for prepared LUT and remove memset of 4 LUT reg.
> ---
>  drivers/mtd/spi-nor/fsl-quadspi.c | 290 ++++++++++++++++++++------------------
>  1 file changed, 149 insertions(+), 141 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index c22e3eb..48d0639 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -183,7 +183,7 @@
> 
>  /* Macros for constructing the LUT register. */
>  #define LUT0(ins, pad, opr)						\
> -		(((opr) << OPRND0_SHIFT) | ((LUT_##pad) << PAD0_SHIFT) | \
> +		(((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) | \
>  		((LUT_##ins) << INSTR0_SHIFT))
> 
>  #define LUT1(ins, pad, opr)	(LUT0(ins, pad, opr) << OPRND1_SHIFT)
> @@ -193,21 +193,21 @@
>  #define QUADSPI_LUT_NUM		64
> 
>  /* SEQID -- we can have 16 seqids at most. */
> +/* LUT0 programmed by bootloader, for run-time create entry for LUT
> +seqid 1 */
>  #define SEQID_READ		0
> -#define SEQID_WREN		1
> -#define SEQID_WRDI		2
> -#define SEQID_RDSR		3
> -#define SEQID_SE		4
> -#define SEQID_CHIP_ERASE	5
> -#define SEQID_PP		6
> -#define SEQID_RDID		7
> -#define SEQID_WRSR		8
> -#define SEQID_RDCR		9
> -#define SEQID_EN4B		10
> -#define SEQID_BRWR		11
> +#define SEQID_RUN_CMD		1
> 
>  #define QUADSPI_MIN_IOMAP SZ_4M
> 
> +enum fsl_qspi_ops {
> +	FSL_QSPI_OPS_READ = 0,
> +	FSL_QSPI_OPS_WRITE,
> +	FSL_QSPI_OPS_ERASE,
> +	FSL_QSPI_OPS_READ_REG,
> +	FSL_QSPI_OPS_WRITE_REG,
> +	FSL_QSPI_OPS_WRITE_BUF_REG,
> +};
> +
>  enum fsl_qspi_devtype {
>  	FSL_QUADSPI_VYBRID,
>  	FSL_QUADSPI_IMX6SX,
> @@ -368,136 +368,140 @@ static irqreturn_t fsl_qspi_irq_handler(int irq, void
> *dev_id)
>  	return IRQ_HANDLED;
>  }
> 
> -static void fsl_qspi_init_lut(struct fsl_qspi *q)
> +static inline s8 pad_count(s8 pad_val)
> +{
> +	s8 count = -1;
> +
> +	if (!pad_val)
> +		return 0;
> +
> +	while (pad_val) {
> +		pad_val >>= 1;
> +		count++;
> +	}
> +	return count;
> +}
> +
> +/*
> + * Prepare LUT entry for the input cmd.
> + * Protocol info is present in instance of struct spi_nor, using which
> +fields
> + * like cmd, data, addrlen along with pad info etc can be parsed.
> + */
> +static void fsl_qspi_prepare_lut(struct spi_nor *nor,
> +				 enum fsl_qspi_ops ops, u8 cmd)
>  {
> +	struct fsl_qspi *q = nor->priv;
>  	void __iomem *base = q->iobase;
>  	int rxfifo = q->devtype_data->rxfifo;
> +	int txfifo = q->devtype_data->txfifo;
>  	u32 lut_base;
> -	int i;
> -
> -	struct spi_nor *nor = &q->nor[0];
> +	u8 cmd_pad, addr_pad, data_pad, dummy_pad;
> +	enum spi_nor_protocol protocol = 0;
>  	u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
> -	u8 read_op = nor->read_opcode;
> -	u8 read_dm = nor->read_dummy;
> +	u8 read_dm, opcode;
> +	int stop_lut;
> +
> +	read_dm = opcode = cmd_pad = addr_pad = data_pad = dummy_pad =
> 0;
> +
> +	switch (ops) {
> +	case FSL_QSPI_OPS_READ_REG:
> +	case FSL_QSPI_OPS_WRITE_REG:
> +	case FSL_QSPI_OPS_WRITE_BUF_REG:
> +		opcode = cmd;
> +		protocol = nor->reg_proto;
> +		break;
> +	case FSL_QSPI_OPS_READ:
> +		opcode = cmd;
> +		read_dm = nor->read_dummy;
> +		protocol = nor->read_proto;
> +		break;
> +	case FSL_QSPI_OPS_WRITE:
> +		opcode = cmd;
> +		protocol = nor->write_proto;
> +		break;
> +	case FSL_QSPI_OPS_ERASE:
> +		opcode = cmd;
> +		break;
> +	default:
> +		dev_err(q->dev, "Unsupported operation 0x%.2x\n", ops);
> +		return;
> +	}
> +
> +	if (protocol) {
> +		cmd_pad = spi_nor_get_protocol_inst_nbits(protocol);
> +		addr_pad = spi_nor_get_protocol_addr_nbits(protocol);
> +		data_pad = spi_nor_get_protocol_data_nbits(protocol);
> +	}
> +
> +	dummy_pad = data_pad;
> +
> +	dev_dbg(q->dev, "ops:%x opcode:%x pad[cmd:%d, addr:%d,
> data:%d]\n",
> +			ops, opcode, cmd_pad, addr_pad, data_pad);
> 
>  	fsl_qspi_unlock_lut(q);
> 
> -	/* Clear all the LUT table */
> -	for (i = 0; i < QUADSPI_LUT_NUM; i++)
> -		qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
> -
> -	/* Read */
> -	lut_base = SEQID_READ * 4;
> -
> -	qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
> -			base + QUADSPI_LUT(lut_base));
> -	qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
> -		    LUT1(FSL_READ, PAD4, rxfifo),
> -			base + QUADSPI_LUT(lut_base + 1));
> -
> -	/* Write enable */
> -	lut_base = SEQID_WREN * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WREN),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Page Program */
> -	lut_base = SEQID_PP * 4;
> -
> -	qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) |
> -		    LUT1(ADDR, PAD1, addrlen),
> -			base + QUADSPI_LUT(lut_base));
> -	qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
> -			base + QUADSPI_LUT(lut_base + 1));
> -
> -	/* Read Status */
> -	lut_base = SEQID_RDSR * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDSR) |
> -			LUT1(FSL_READ, PAD1, 0x1),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Erase a sector */
> -	lut_base = SEQID_SE * 4;
> -
> -	qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) |
> -		    LUT1(ADDR, PAD1, addrlen),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Erase the whole chip */
> -	lut_base = SEQID_CHIP_ERASE * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_CHIP_ERASE),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* READ ID */
> -	lut_base = SEQID_RDID * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDID) |
> -			LUT1(FSL_READ, PAD1, 0x8),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Write Register */
> -	lut_base = SEQID_WRSR * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRSR) |
> -			LUT1(FSL_WRITE, PAD1, 0x2),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Read Configuration Register */
> -	lut_base = SEQID_RDCR * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_RDCR) |
> -			LUT1(FSL_READ, PAD1, 0x1),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Write disable */
> -	lut_base = SEQID_WRDI * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_WRDI),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Enter 4 Byte Mode (Micron) */
> -	lut_base = SEQID_EN4B * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_EN4B),
> -			base + QUADSPI_LUT(lut_base));
> -
> -	/* Enter 4 Byte Mode (Spansion) */
> -	lut_base = SEQID_BRWR * 4;
> -	qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
> -			base + QUADSPI_LUT(lut_base));
> +	/* Dynamic LUT */
> +	lut_base = SEQID_RUN_CMD * 4;
> 
> -	fsl_qspi_lock_lut(q);
> -}
> +	/* default, STOP instruction to be programmed in (lut_base + 1) reg */
> +	stop_lut = 1;
> +	switch (ops) {
> +	case FSL_QSPI_OPS_READ_REG:
> +		qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) |
> +			  LUT1(FSL_READ, pad_count(data_pad), rxfifo),
> +			  base + QUADSPI_LUT(lut_base));
> +		break;
> +	case FSL_QSPI_OPS_WRITE_REG:
> +		qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode),
> +			  base + QUADSPI_LUT(lut_base));
> +		break;
> +	case FSL_QSPI_OPS_WRITE_BUF_REG:
> +		qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) |
> +			  LUT1(FSL_WRITE, pad_count(data_pad), txfifo),
> +			  base + QUADSPI_LUT(lut_base));
> +		break;
> +	case FSL_QSPI_OPS_READ:
> +	case FSL_QSPI_OPS_WRITE:
> +	case FSL_QSPI_OPS_ERASE:
> +		/* Common for Read, Write and Erase ops. */
> +		qspi_writel(q, LUT0(CMD, pad_count(cmd_pad), opcode) |
> +				LUT1(ADDR, pad_count(addr_pad), addrlen),
> +				base + QUADSPI_LUT(lut_base));
> +		/*
> +		 * For Erase ops - Data and Dummy not required.
> +		 * For Write ops - Dummy not required.
> +		 */
> 
> -/* Get the SEQID for the command */
> -static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) -{
> -	switch (cmd) {
> -	case SPINOR_OP_READ_1_1_4:
> -		return SEQID_READ;
> -	case SPINOR_OP_WREN:
> -		return SEQID_WREN;
> -	case SPINOR_OP_WRDI:
> -		return SEQID_WRDI;
> -	case SPINOR_OP_RDSR:
> -		return SEQID_RDSR;
> -	case SPINOR_OP_SE:
> -		return SEQID_SE;
> -	case SPINOR_OP_CHIP_ERASE:
> -		return SEQID_CHIP_ERASE;
> -	case SPINOR_OP_PP:
> -		return SEQID_PP;
> -	case SPINOR_OP_RDID:
> -		return SEQID_RDID;
> -	case SPINOR_OP_WRSR:
> -		return SEQID_WRSR;
> -	case SPINOR_OP_RDCR:
> -		return SEQID_RDCR;
> -	case SPINOR_OP_EN4B:
> -		return SEQID_EN4B;
> -	case SPINOR_OP_BRWR:
> -		return SEQID_BRWR;
> +		if (ops == FSL_QSPI_OPS_READ) {
> +			qspi_writel(q,
> +				    LUT0(DUMMY, pad_count(dummy_pad),
> read_dm) |
> +				    LUT1(FSL_READ, pad_count(data_pad),
> rxfifo),
> +				    base + QUADSPI_LUT(lut_base + 1));
> +			stop_lut = 2;
> +
> +			/* TODO Add condition to check if READ is IP/AHB. */
> +
> +			/* For AHB read, add seqid in BFGENCR register. */
> +			qspi_writel(q,
> +				SEQID_RUN_CMD <<
> QUADSPI_BFGENCR_SEQID_SHIFT,
> +				q->iobase + QUADSPI_BFGENCR);
> +		}
> +
> +		if (ops == FSL_QSPI_OPS_WRITE) {
> +			qspi_writel(q, LUT0(FSL_WRITE, pad_count(data_pad),
> 0),
> +					base + QUADSPI_LUT(lut_base + 1));
> +			stop_lut = 2;
> +		}
> +		break;
>  	default:
> -		if (cmd == q->nor[0].erase_opcode)
> -			return SEQID_SE;
> -		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> +		dev_err(q->dev, "Unsupported operation 0x%.2x\n", ops);
>  		break;
>  	}
> -	return -EINVAL;
> +
> +	/* prepare LUT for STOP instruction. */
> +	qspi_writel(q, 0, base +  QUADSPI_LUT(lut_base + stop_lut));
> +
> +	fsl_qspi_lock_lut(q);
>  }
> 
>  static int
> @@ -532,7 +536,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
>  	} while (1);
> 
>  	/* trigger the LUT now */
> -	seqid = fsl_qspi_get_seqid(q, cmd);
> +	seqid = SEQID_RUN_CMD;
>  	qspi_writel(q, (seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
>  			base + QUADSPI_IPCR);
> 
> @@ -684,8 +688,8 @@ static void fsl_qspi_init_ahb_read(struct fsl_qspi *q)
>  	qspi_writel(q, 0, base + QUADSPI_BUF1IND);
>  	qspi_writel(q, 0, base + QUADSPI_BUF2IND);
> 
> -	/* Set the default lut sequence for AHB Read. */
> -	seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
> +	/* Set dynamic LUT entry as lut sequence for AHB Read . */
> +	seqid = SEQID_RUN_CMD;
>  	qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
>  		q->iobase + QUADSPI_BFGENCR);
>  }
> @@ -728,7 +732,6 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
>  	void __iomem *base = q->iobase;
>  	u32 reg;
>  	int ret;
> -
>  	/* disable and unprepare clock to avoid glitch pass to controller */
>  	fsl_qspi_clk_disable_unprep(q);
> 
> @@ -746,9 +749,6 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
>  		base + QUADSPI_MCR);
>  	udelay(1);
> 
> -	/* Init the LUT table. */
> -	fsl_qspi_init_lut(q);
> -
>  	/* Disable the module */
>  	qspi_writel(q, QUADSPI_MCR_MDIS_MASK |
> QUADSPI_MCR_RESERVED_MASK,
>  			base + QUADSPI_MCR);
> @@ -794,9 +794,6 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
>  	if (ret)
>  		return ret;
> 
> -	/* Init the LUT table again. */
> -	fsl_qspi_init_lut(q);
> -
>  	return 0;
>  }
> 
> @@ -820,6 +817,7 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8
> opcode, u8 *buf, int len)
>  	int ret;
>  	struct fsl_qspi *q = nor->priv;
> 
> +	fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_READ_REG, opcode);
>  	ret = fsl_qspi_runcmd(q, opcode, 0, len);
>  	if (ret)
>  		return ret;
> @@ -834,6 +832,8 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8
> opcode, u8 *buf, int len)
>  	int ret;
> 
>  	if (!buf) {
> +		/* Prepare LUT for WRITE_REG cmd with input BUF as NULL. */
> +		fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE_REG, opcode);
>  		ret = fsl_qspi_runcmd(q, opcode, 0, 1);
>  		if (ret)
>  			return ret;
> @@ -842,6 +842,8 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8
> opcode, u8 *buf, int len)
>  			fsl_qspi_invalid(q);
> 
>  	} else if (len > 0) {
> +		/* Prepare LUT for WRITE_REG cmd with input BUF non-NULL.
> */
> +		fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE_BUF_REG,
> opcode);
>  		ret = fsl_qspi_nor_write(q, nor, opcode, 0,
>  					(u32 *)buf, len);
>  		if (ret > 0)
> @@ -858,8 +860,11 @@ static ssize_t fsl_qspi_write(struct spi_nor *nor, loff_t
> to,
>  			      size_t len, const u_char *buf)  {
>  	struct fsl_qspi *q = nor->priv;
> -	ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
> -					 (u32 *)buf, len);
> +	ssize_t ret;
> +
> +	fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_WRITE, nor-
> >program_opcode);
> +	ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
> +				 (u32 *)buf, len);
> 
>  	/* invalid the data in the AHB buffer. */
>  	fsl_qspi_invalid(q);
> @@ -872,6 +877,8 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t
> from,
>  	struct fsl_qspi *q = nor->priv;
>  	u8 cmd = nor->read_opcode;
> 
> +	fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_READ, nor->read_opcode);
> +
>  	/* if necessary,ioremap buffer before AHB read, */
>  	if (!q->ahb_addr) {
>  		q->memmap_offs = q->chip_base_addr + from; @@ -920,6
> +927,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
>  	dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
>  		nor->mtd.erasesize / 1024, q->chip_base_addr, (u32)offs);
> 
> +	fsl_qspi_prepare_lut(nor, FSL_QSPI_OPS_ERASE, nor->erase_opcode);
>  	ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
>  	if (ret)
>  		return ret;
> --
> 1.9.1




More information about the linux-mtd mailing list