diff mbox

[v3] mtd/nand: don't use {read,write}_buf for 8-bit transfers

Message ID 1385500515-5376-1-git-send-email-u.kleine-koenig@pengutronix.de
State Superseded, archived
Headers show

Commit Message

Uwe Kleine-König Nov. 26, 2013, 9:15 p.m. UTC
According to the Open NAND Flash Interface Specification (ONFI) Revision
3.1 "Parameters are always transferred on the lower 8-bits of the data
bus." for the Get Features and Set Features commands.

So using read_buf and write_buf is wrong for 16-bit wide nand chips as
they use I/O[15:0]. The Get Features command is easily fixed using 4
times the read_byte callback. For Set Features implement a new
overwritable callback "write_byte". Still I expect the default to work
just fine for all controllers and making it overwriteable was just done
for symmetry.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/mtd/nand/nand_base.c | 61 +++++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/nand.h     |  3 +++
 2 files changed, 60 insertions(+), 4 deletions(-)

Comments

Brian Norris Nov. 27, 2013, 7:35 a.m. UTC | #1
+ Pekon, Ezequiel

Can one of you see how this patch works with your BeagleBones w/ x16
NAND?

Also, do you think on of you could take a look at killing the
nand_chip.read_word() callback, as Artem was originally requesting with
this patch? It only has one user (for bad block checking in
nand_block_bad(), which is currently only used when there is no BBT at
all), and it looks like it could be replaced with a call to
read_buf(mtd, buf, 2).

Hi Uwe,

On Tue, Nov 26, 2013 at 10:15:15PM +0100, Uwe Kleine-König wrote:
> According to the Open NAND Flash Interface Specification (ONFI) Revision
> 3.1 "Parameters are always transferred on the lower 8-bits of the data
> bus." for the Get Features and Set Features commands.
> 
> So using read_buf and write_buf is wrong for 16-bit wide nand chips as
> they use I/O[15:0]. The Get Features command is easily fixed using 4
> times the read_byte callback. For Set Features implement a new
> overwritable callback "write_byte". Still I expect the default to work
> just fine for all controllers and making it overwriteable was just done
> for symmetry.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_base.c | 61 +++++++++++++++++++++++++++++++++++++++++---
>  include/linux/mtd/nand.h     |  3 +++
>  2 files changed, 60 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index bd39f7b..27d755b 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
...
> @@ -2706,7 +2751,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
>  }
>  
>  /**
> - * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
> + * nand_onfi_set_features - [REPLACEABLE] set features for ONFI nand

This change is unrelated. (Yes, the whitespace should be fixed, but
probably not in this patch.)

>   * @mtd: MTD device structure
>   * @chip: nand chip info structure
>   * @addr: feature address.
...
> @@ -2731,7 +2779,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
>  }
>  
>  /**
> - * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
> + * nand_onfi_get_features - [REPLACEABLE] get features for ONFI nand

Ditto.

>   * @mtd: MTD device structure
>   * @chip: nand chip info structure
>   * @addr: feature address.
...
> @@ -2812,6 +2863,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
>  		chip->block_markbad = nand_default_block_markbad;
>  	if (!chip->write_buf || chip->write_buf == nand_write_buf)
>  		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
> +	if (!chip->write_byte)

This check should be:

	if (!chip->write_byte || chip->write_byte == nand_write_byte)

in order to match the rest of the functions, so they can be reset if the
buswidth is detected to be x16, and we re-call nand_set_defaults()
(e.g., when using NAND_BUSWIDTH_AUTO). See the comment:

  /* If called twice, pointers that depend on busw may need to be reset */

I know it's kind of ugly, but that's what we have for now.

> +		chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
>  	if (!chip->read_buf || chip->read_buf == nand_read_buf)
>  		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
>  	if (!chip->scan_bbt)

Brian
Ezequiel Garcia Nov. 29, 2013, 12:20 p.m. UTC | #2
Brian,

On Tue, Nov 26, 2013 at 11:35:12PM -0800, Brian Norris wrote:
> + Pekon, Ezequiel
> 
> Can one of you see how this patch works with your BeagleBones w/ x16
> NAND?
> 
> Also, do you think on of you could take a look at killing the
> nand_chip.read_word() callback, as Artem was originally requesting with
> this patch? It only has one user (for bad block checking in
> nand_block_bad(), which is currently only used when there is no BBT at
> all), and it looks like it could be replaced with a call to
> read_buf(mtd, buf, 2).
> 

Sure, sounds doable.

> Hi Uwe,
> 

Uwe,

If you're preparing a v4 to address Brian's feedback, then I'll see about testing
it with 8/16-bit flashes on my AM335x.

> On Tue, Nov 26, 2013 at 10:15:15PM +0100, Uwe Kleine-König wrote:
> > According to the Open NAND Flash Interface Specification (ONFI) Revision
> > 3.1 "Parameters are always transferred on the lower 8-bits of the data
> > bus." for the Get Features and Set Features commands.
> > 
> > So using read_buf and write_buf is wrong for 16-bit wide nand chips as
> > they use I/O[15:0]. The Get Features command is easily fixed using 4
> > times the read_byte callback. For Set Features implement a new
> > overwritable callback "write_byte". Still I expect the default to work
> > just fine for all controllers and making it overwriteable was just done
> > for symmetry.
> > 
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> >  drivers/mtd/nand/nand_base.c | 61 +++++++++++++++++++++++++++++++++++++++++---
> >  include/linux/mtd/nand.h     |  3 +++
> >  2 files changed, 60 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index bd39f7b..27d755b 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> ...
> > @@ -2706,7 +2751,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
> >  }
> >  
> >  /**
> > - * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
> > + * nand_onfi_set_features - [REPLACEABLE] set features for ONFI nand
> 
> This change is unrelated. (Yes, the whitespace should be fixed, but
> probably not in this patch.)
> 
> >   * @mtd: MTD device structure
> >   * @chip: nand chip info structure
> >   * @addr: feature address.
> ...
> > @@ -2731,7 +2779,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> >  }
> >  
> >  /**
> > - * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
> > + * nand_onfi_get_features - [REPLACEABLE] get features for ONFI nand
> 
> Ditto.
> 
> >   * @mtd: MTD device structure
> >   * @chip: nand chip info structure
> >   * @addr: feature address.
> ...
> > @@ -2812,6 +2863,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
> >  		chip->block_markbad = nand_default_block_markbad;
> >  	if (!chip->write_buf || chip->write_buf == nand_write_buf)
> >  		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
> > +	if (!chip->write_byte)
> 
> This check should be:
> 
> 	if (!chip->write_byte || chip->write_byte == nand_write_byte)
> 
> in order to match the rest of the functions, so they can be reset if the
> buswidth is detected to be x16, and we re-call nand_set_defaults()
> (e.g., when using NAND_BUSWIDTH_AUTO). See the comment:
> 
>   /* If called twice, pointers that depend on busw may need to be reset */
> 
> I know it's kind of ugly, but that's what we have for now.
> 
> > +		chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
> >  	if (!chip->read_buf || chip->read_buf == nand_read_buf)
> >  		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
> >  	if (!chip->scan_bbt)
> 
> Brian
diff mbox

Patch

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index bd39f7b..27d755b 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -202,6 +202,51 @@  static void nand_select_chip(struct mtd_info *mtd, int chipnr)
 }
 
 /**
+ * nand_write_byte - [DEFAULT] write single byte to chip
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0]
+ */
+static void nand_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	chip->write_buf(mtd, &byte, 1);
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write single byte to a chip with width 16
+ * @mtd: MTD device structure
+ * @byte: value to write
+ *
+ * Default function to write a byte to I/O[7:0] on a 16-bit wide chip.
+ */
+static void nand_write_byte16(struct mtd_info *mtd, uint8_t byte)
+{
+	struct nand_chip *chip = mtd->priv;
+	uint16_t word = byte;
+
+	/*
+	 * It's not entirely clear what should happen to I/O[15:8] when writing
+	 * a byte. The ONFi spec (Revision 3.1; 2012-09-19, Section 2.16) reads:
+	 *
+	 *    When the host supports a 16-bit bus width, only data is
+	 *    transferred at the 16-bit width. All address and command line
+	 *    transfers shall use only the lower 8-bits of the data bus. During
+	 *    command transfers, the host may place any value on the upper
+	 *    8-bits of the data bus. During address transfers, the host shall
+	 *    set the upper 8-bits of the data bus to 00h.
+	 *
+	 * One user of the write_byte callback is nand_onfi_set_features. The
+	 * four parameters are specified to be written to I/O[7:0], but this is
+	 * neither an address nor a command transfer. Let's assume a 0 on the
+	 * upper I/O lines is OK.
+	 */
+	chip->write_buf(mtd, &word, 2);
+}
+
+/**
  * nand_write_buf - [DEFAULT] write buffer to chip
  * @mtd: MTD device structure
  * @buf: data buffer
@@ -2706,7 +2751,7 @@  static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 }
 
 /**
- * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
+ * nand_onfi_set_features - [REPLACEABLE] set features for ONFI nand
  * @mtd: MTD device structure
  * @chip: nand chip info structure
  * @addr: feature address.
@@ -2716,6 +2761,7 @@  static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
 			int addr, uint8_t *subfeature_param)
 {
 	int status;
+	int i;
 
 	if (!chip->onfi_version ||
 	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
@@ -2723,7 +2769,9 @@  static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
 		return -EINVAL;
 
 	chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
-	chip->write_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		chip->write_byte(mtd, subfeature_param[i]);
+
 	status = chip->waitfunc(mtd, chip);
 	if (status & NAND_STATUS_FAIL)
 		return -EIO;
@@ -2731,7 +2779,7 @@  static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
 }
 
 /**
- * nand_onfi_get_features- [REPLACEABLE] get features for ONFI nand
+ * nand_onfi_get_features - [REPLACEABLE] get features for ONFI nand
  * @mtd: MTD device structure
  * @chip: nand chip info structure
  * @addr: feature address.
@@ -2740,6 +2788,8 @@  static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
 			int addr, uint8_t *subfeature_param)
 {
+	int i;
+
 	if (!chip->onfi_version ||
 	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
 	      & ONFI_OPT_CMD_SET_GET_FEATURES))
@@ -2749,7 +2799,8 @@  static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
 	memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
 
 	chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
-	chip->read_buf(mtd, subfeature_param, ONFI_SUBFEATURE_PARAM_LEN);
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		*subfeature_param++ = chip->read_byte(mtd);
 	return 0;
 }
 
@@ -2812,6 +2863,8 @@  static void nand_set_defaults(struct nand_chip *chip, int busw)
 		chip->block_markbad = nand_default_block_markbad;
 	if (!chip->write_buf || chip->write_buf == nand_write_buf)
 		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+	if (!chip->write_byte)
+		chip->write_byte = busw ? nand_write_byte16 : nand_write_byte;
 	if (!chip->read_buf || chip->read_buf == nand_read_buf)
 		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
 	if (!chip->scan_bbt)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9e6c8f9..e843942 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -432,6 +432,8 @@  struct nand_buffers {
  *			flash device.
  * @read_byte:		[REPLACEABLE] read one byte from the chip
  * @read_word:		[REPLACEABLE] read one word from the chip
+ * @write_byte		[REPLACEABLE] write a single byte to the chip on the
+ *			low 8 I/O lines
  * @write_buf:		[REPLACEABLE] write data from the buffer to the chip
  * @read_buf:		[REPLACEABLE] read data from the chip into the buffer
  * @select_chip:	[REPLACEABLE] select chip nr
@@ -521,6 +523,7 @@  struct nand_chip {
 
 	uint8_t (*read_byte)(struct mtd_info *mtd);
 	u16 (*read_word)(struct mtd_info *mtd);
+	void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
 	void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
 	void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
 	void (*select_chip)(struct mtd_info *mtd, int chip);