[LINUX PATCH v8 2/2] mtd: rawnand: pl353: Add basic driver for arm pl353 smc nand interface

Miquel Raynal miquel.raynal at bootlin.com
Mon Mar 26 14:53:59 PDT 2018


Hi Naga,

> > > +/**
> > > + * pl353_nand_read_buf_l - read chip data into buffer
> > > + * @chip:	Pointer to the NAND chip info structure
> > > + * @in:		Pointer to the buffer to store read data
> > > + * @len:	Number of bytes to read
> > > + * Return:	Always return zero
> > > + */
> > > +static int pl353_nand_read_buf_l(struct nand_chip *chip,
> > > +				     uint8_t *in,
> > > +				     unsigned int len)
> > > +{
> > > +	int i;
> > > +	unsigned long *ptr = (unsigned long *)in;
> > > +
> > > +	len >>= 2;
> > 
> > Can you please let the compiler optimize things? I don't find this very readable, I
> > would prefer a division here. And if this division by 4 is related to the size of *ptr,
> > please use the sizeof() macro. Otherwise please document this value.
> At a time, we are reading 4bytes. Hence >> 2.
> I didn't get your point.
> Are you saying instead of shifting, just use divide by 4?

I do.

> 
> > 
> > > +	for (i = 0; i < len; i++)
> > > +		ptr[i] = readl(chip->IO_ADDR_R);
> > 
> > Space
> Ok, I will update it
> > 
> > > +	return 0;
> > > +}
> > > +
> > > +static void pl353_nand_write_buf_l(struct nand_chip *chip, const uint8_t
> > *buf,
> > > +				int len)
> > > +{
> > > +	int i;
> > > +	unsigned long *ptr = (unsigned long *)buf;
> > > +
> > > +	for (i = 0; i < len; i++)
> > > +		writeb(ptr[i], chip->IO_ADDR_W);
> > 
> > Here you use writeb (as opposed to readl previously). Then, I guess you can also
> > read byte per byte. If so, you can drop both helpers and let the core use its
> > defaults ones: nand_read/write_buf().
> May be the function name I have written wrongly.
> When using writel, it should be nand_write_buf_l.
> But the thing is, when using exec_op, core is not calling chip->read_byte(), hence I added
> Byte reading.

Well, the point of using ->exec_op() is to forget about these hooks.
->exec_op() will ask you to read one byte if needed. You should forget
about ->read/write_byte/word/buf() hooks and delete them entirely.

> > 
> > Same for the next functions. Plus, if you don't use them inside
> > ->exec_op() implementation, they have to be removed anyway.
> The name of the function should change to buf_l, to do 4byte writes.
> The name is creating confusion.
> > 
> > > +}
> > > +
> > > +/**
> > > + * pl353_nand_write_buf - write buffer to chip
> > > + * @mtd:	Pointer to the mtd info structure
> > > + * @buf:	Pointer to the buffer to store read data
> > > + * @len:	Number of bytes to write
> > > + */
> > > +static void pl353_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
> > > +				int len)
> > > +{
> > > +	int i;
> > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > +	unsigned long *ptr = (unsigned long *)buf;
> > > +
> > > +	len >>= 2;
> > > +
> > > +	for (i = 0; i < len; i++)
> > > +		writel(ptr[i], chip->IO_ADDR_W);
> > > +}
> > > +
> > > +/**
> > > + * pl353_nand_read_buf - read chip data into buffer
> > > + * @chip:	Pointer to the NAND chip info structure
> > > + * @in:	Pointer to the buffer to store read data
> > > + * @len:	Number of bytes to read
> > > + * Return:	0 on success or error value on failure
> > > + */
> > > +static int pl353_nand_read_buf(struct nand_chip *chip,
> > > +				     uint8_t *in,
> > > +				     unsigned int len)
> > > +{
> > > +	int i;
> > > +
> > > +	for (i = 0; i < len; i++)
> > > +		in[i] = readb(chip->IO_ADDR_R);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/**
> > > + * pl353_nand_calculate_hwecc - Calculate Hardware ECC
> > > + * @mtd:	Pointer to the mtd_info structure
> > > + * @data:	Pointer to the page data
> > > + * @ecc_code:	Pointer to the ECC buffer where ECC data needs to be
> > stored
> > 
> > You store ECC in a variable called "code", can you please make it consistent?
> Miquel, I am not using any variable called "code"

I see "ecc_code", and ECC already stands for "Error Correcting Code".

> > 
> > > + *
> > > + * This function retrieves the Hardware ECC data from the controller
> > > +and returns
> > > + * ECC data back to the MTD subsystem.
> > > + *
> > > + * Return:	0 on success or error value on failure
> > > + */
> > > +static int pl353_nand_calculate_hwecc(struct mtd_info *mtd,
> > > +				const u8 *data, u8 *ecc_code)
> > > +{
> > > +	u32 ecc_value, ecc_status;
> > > +	u8 ecc_reg, ecc_byte;
> > > +	unsigned long timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT;
> > > +	/* Wait till the ECC operation is complete or timeout */
> > > +	do {
> > > +		if (pl353_smc_ecc_is_busy())
> > 
> > Where does this function come from?
> The pl353 SMC has memory controller driver and this NAND driver is using those APIs.
> I sent patches to add the memory controller driver for pl353.
> https://www.spinics.net/lists/kernel/msg2748832.html
> https://www.spinics.net/lists/kernel/msg2748834.html
> https://www.spinics.net/lists/kernel/msg2748840.html
> 

ok, please add a reference in your cover letter to the functions that
are not yet merged and you would use in this series.


> > > +
> > > +	cmd_phase_addr = (unsigned long __force)xnand->nand_base + (
> > > +			 (((xnand->row_addr_cycles) + (xnand-
> > >col_addr_cycles))
> > > +			 << ADDR_CYCLES_SHIFT) |
> > > +			 (end_cmd_valid << END_CMD_VALID_SHIFT)
> > 	|
> > > +			 (COMMAND_PHASE)				|
> > > +			 (end_cmd << END_CMD_SHIFT)			|
> > 
> > Please don't align the '|'
> You mean, tabbing? 

Yes

> > 
> > > +			 (start_cmd << START_CMD_SHIFT));
> > > +	cmd_addr = (void __iomem * __force)cmd_phase_addr;
> > > +
> > > +	/* Get the data phase address */
> > > +	data_phase_addr = (unsigned long __force)xnand->nand_base + (
> > > +			  (0x0 << CLEAR_CS_SHIFT)			|
> > > +			  (0 << END_CMD_VALID_SHIFT)	|
> > > +			  (DATA_PHASE)					|
> > > +			  (end_cmd << END_CMD_SHIFT)			|
> > > +			  (0x0 << ECC_LAST_SHIFT));
> > > +
> > > +	chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
> > > +	chip->IO_ADDR_W = chip->IO_ADDR_R;
> > > +	if (chip->options & NAND_BUSWIDTH_16)
> > > +		column >>= 1;
> > 
> >  / 2
> > 
> > > +	cmd_data = column;
> > > +	if (mtd->writesize > PL353_NAND_ECC_SIZE) {
> > > +		cmd_data |= page << 16;
> > > +		/* Another address cycle for devices > 128MiB */
> > > +		if (chip->chipsize > (128 << 20)) {
> > 
> > Now there is a flag for that in the core, called NAND_ROW_ADDR_3.
> I will check and update.
> > 
> > > +			pl353_nand_write32(cmd_addr, cmd_data);
> > > +			cmd_data = (page >> 16);
> > > +		}
> > > +	} else {
> > > +		cmd_data |= page << 8;
> > > +	}
> > 
> > Space
> Ok, I will update.
> > 
> > > +	pl353_nand_write32(cmd_addr, cmd_data); }
> > > +
> > > +/**
> > > + * pl353_nand_read_oob - [REPLACEABLE] the most common OOB data read
> > function
> > > + * @mtd:	Pointer to the mtd info structure
> > > + * @chip:	Pointer to the NAND chip info structure
> > > + * @page:	Page number to read
> > > + *
> > > + * Return:	Always return zero
> > > + */
> > > +static int pl353_nand_read_oob(struct mtd_info *mtd, struct nand_chip
> > *chip,
> > > +			    int page)
> > > +{
> > > +
> > > +	unsigned long data_phase_addr;
> > > +	uint8_t *p;
> > > +
> > > +	chip->pagebuf = -1;
> > > +	if (mtd->writesize < PL353_NAND_ECC_SIZE)
> > > +		return 0;
> > > +
> > > +	pl353_prepare_cmd(mtd, chip, page, mtd->writesize,
> > NAND_CMD_READ0,
> > > +		NAND_CMD_READSTART, 1);
> > 
> > Alignment
> Are you running any script apart from checkpatch?
> Any way I will correct it.

All my comments have been made "manually" without tool but you should
definitively run checkpatch.pl --strict on all your patches before
sending them.


> > > +
> > > +	return (status & NAND_STATUS_FAIL) ? -EIO : 0; }
> > > +
> > > +/**
> > > + * pl353_nand_read_page_raw - [Intern] read raw page data without ecc
> > > + * @mtd:		Pointer to the mtd info structure
> > > + * @chip:		Pointer to the NAND chip info structure
> > > + * @buf:		Pointer to the data buffer
> > > + * @oob_required:	Caller requires OOB data read to chip->oob_poi
> > > + * @page:		Page number to read
> > > + *
> > > + * Return:	Always return zero
> > > + */
> > > +static int pl353_nand_read_page_raw(struct mtd_info *mtd,
> > > +				struct nand_chip *chip,
> > > +				uint8_t *buf, int oob_required, int page)
> > 
> > Do you really need raw accessors?
> Yes, when using on-die ecc, this function is getting called.
> i.e. nand_micron.c is calling nand_set_features_op, with DATA_OUT_INSTR, and there
> we are using this.

I don't see any link between doing a nad_set_features_op and calling a
raw accessor. It's almost like the ->read_byte() hook, if there is
nothing specific to implement, just forget about it, ->exec_op() is
probably enough.

> > > +/**
> > > + * pl353_nand_select_chip - Select the flash device
> > > + * @mtd:	Pointer to the mtd info structure
> > > + * @chip:	Pointer to the NAND chip info structure
> > > + *
> > > + * This function is empty as the NAND controller handles chip select
> > > +line
> > > + * internally based on the chip address passed in command and data phase.
> > > + */
> > > +static void pl353_nand_select_chip(struct mtd_info *mtd, int chip) {
> > > +}
> > > +
> > > +/* NAND framework ->exec_op() hooks and related helpers */ static
> > > +void pl353_nfc_parse_instructions(struct nand_chip *chip,
> > > +					   const struct nand_subop *subop,
> > > +					   struct pl353_nfc_op *nfc_op)
> > > +{
> > > +	const struct nand_op_instr *instr = NULL;
> > > +	unsigned int op_id, offset, naddrs;
> > > +	int i;
> > > +	const u8 *addrs;
> > > +
> > > +	memset(nfc_op, 0, sizeof(struct pl353_nfc_op));
> > > +	for (op_id = 0; op_id < subop->ninstrs; op_id++) {
> > > +
> > 
> > What is this for-loop for? I don't get it as you break the switch in every case?
> I think, breaking switch case only not for loop.

My bad, ok.

> > 
> > > +		nfc_op->len = nand_subop_get_data_len(subop, op_id);
> > > +
> > > +		instr = &subop->instrs[op_id];
> > > +		if (subop->ninstrs == 1)
> > > +			nfc_op->cmnds[0] = -1;
> > > +		switch (instr->type) {
> > > +		case NAND_OP_CMD_INSTR:
> > > +			nfc_op->type = NAND_OP_CMD_INSTR;
> > > +			nfc_op->end_cmd = op_id - 1;
> > > +			if (op_id)
> > 
> > You should put { } on the if also if the else statement needs braces.
> Ok, but I didn't see any warning from checkpatch.

Maybe with the --strict option?
Otherwise this is clearly stated in the kernel coding style
documentation.


> > > +static const struct nand_op_parser pl353_nfc_op_parser =
> > NAND_OP_PARSER(
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_ADDR_ELEM(false, 7),
> > > +		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)),
> > > +	 NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_ADDR_ELEM(false, 7),
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)),
> > > +	 NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_ADDR_ELEM(false, 7),
> > > +		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_ADDR_ELEM(false, 8),
> > > +		NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 2048),
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(true),
> > > +		NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false),
> > > +		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 2048)),
> > > +	NAND_OP_PARSER_PATTERN(
> > > +		pl353_nand_cmd_function,
> > > +		NAND_OP_PARSER_PAT_CMD_ELEM(false)),
> > 
> > I am pretty sure you can factorize all these patterns now. Use the "optional"
> > parameter for that.
> Can you explain little bit?  I didn't get.

All the patterns refer to the same function. This is fine.
But maybe you can factorize the parents using the "optional" parameter.
For example, if you have
* CMD + ADDR + DATA_IN
* CMD + DATA_IN
Maybe you can just state:
* CMD + [ADDR] + DATA_IN
With "[]" meaning the element is optional.


Thanks for addressing these comments.

Regards,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com



More information about the linux-mtd mailing list