Patchwork [v3,6/7] mtd: m25p80: add a read function to read page by page

login
register
mail settings
Submitter Mingkai Hu
Date Sept. 30, 2010, 8 a.m.
Message ID <1285833646-12006-7-git-send-email-Mingkai.hu@freescale.com>
Download mbox | patch
Permalink /patch/66131/
State New
Headers show

Comments

Mingkai Hu - Sept. 30, 2010, 8 a.m.
For Freescale's eSPI controller, the max transaction length one time
is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used
mkfs.ext2 command to create ext2 filesystem on the flash, the read
length will exceed the max value of the SPCOM[TRANSLEN] field, so
change the read function to read page by page.

For other SPI flash driver, also needed to supply the read function
if used the eSPI controller.

Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
 - Add a quirks member for the SPI master to handle the contrains of the
   SPI controller. I can't think of other method. :-(

 drivers/mtd/devices/m25p80.c |   78 ++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_fsl_lib.c    |    4 ++
 include/linux/spi/spi.h      |    5 +++
 3 files changed, 87 insertions(+), 0 deletions(-)
Grant Likely - Sept. 30, 2010, 9:41 p.m.
Hmmm.... for some reason the previous replies didn't get picked up by
patchwork, so I'm replying with my comment again for the public
record.

In this case the eSPI controller driver is buggy and needs to be
fixed.  If the hardware can only support small transfers, then it is
the responsibilty of the driver to chain up smaller chunks into one
big transfer, and make sure that the CS line doesn't go low in the
middle of it.

g.

On Thu, Sep 30, 2010 at 5:00 PM, Mingkai Hu <Mingkai.hu@freescale.com> wrote:
> For Freescale's eSPI controller, the max transaction length one time
> is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used
> mkfs.ext2 command to create ext2 filesystem on the flash, the read
> length will exceed the max value of the SPCOM[TRANSLEN] field, so
> change the read function to read page by page.
>
> For other SPI flash driver, also needed to supply the read function
> if used the eSPI controller.
>
> Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
> ---
> v3:
>  - Add a quirks member for the SPI master to handle the contrains of the
>   SPI controller. I can't think of other method. :-(
>
>  drivers/mtd/devices/m25p80.c |   78 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/spi/spi_fsl_lib.c    |    4 ++
>  include/linux/spi/spi.h      |    5 +++
>  3 files changed, 87 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 47d53c7..f65cca8 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -377,6 +377,81 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
>  }
>
>  /*
> + * Read an address range from the flash chip page by page.
> + * Some controller has transaction length limitation such as the
> + * Freescale's eSPI controller can only trasmit 0xFFFF bytes one
> + * time, so we have to read page by page if the len is more than
> + * the limitation.
> + */
> +static int m25p80_page_read(struct mtd_info *mtd, loff_t from, size_t len,
> +       size_t *retlen, u_char *buf)
> +{
> +       struct m25p *flash = mtd_to_m25p(mtd);
> +       struct spi_transfer t[2];
> +       struct spi_message m;
> +       u32 i, page_size = 0;
> +
> +       DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
> +                       dev_name(&flash->spi->dev), __func__, "from",
> +                       (u32)from, len);
> +
> +       /* sanity checks */
> +       if (!len)
> +               return 0;
> +
> +       if (from + len > flash->mtd.size)
> +               return -EINVAL;
> +
> +       spi_message_init(&m);
> +       memset(t, 0, (sizeof t));
> +
> +       /* NOTE:
> +        * OPCODE_FAST_READ (if available) is faster.
> +        * Should add 1 byte DUMMY_BYTE.
> +        */
> +       t[0].tx_buf = flash->command;
> +       t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
> +       spi_message_add_tail(&t[0], &m);
> +
> +       t[1].rx_buf = buf;
> +       spi_message_add_tail(&t[1], &m);
> +
> +       /* Byte count starts at zero. */
> +       if (retlen)
> +               *retlen = 0;
> +
> +       mutex_lock(&flash->lock);
> +
> +       /* Wait till previous write/erase is done. */
> +       if (wait_till_ready(flash)) {
> +               /* REVISIT status return?? */
> +               mutex_unlock(&flash->lock);
> +               return 1;
> +       }
> +
> +       /* Set up the write data buffer. */
> +       flash->command[0] = OPCODE_READ;
> +
> +       for (i = page_size; i < len; i += page_size) {
> +               page_size = len - i;
> +               if (page_size > flash->page_size)
> +                       page_size = flash->page_size;
> +               m25p_addr2cmd(flash, from + i, flash->command);
> +               t[1].len = page_size;
> +               t[1].rx_buf = buf + i;
> +
> +               spi_sync(flash->spi, &m);
> +
> +               *retlen += m.actual_length - m25p_cmdsz(flash)
> +                       - FAST_READ_DUMMY_BYTE;
> +       }
> +
> +       mutex_unlock(&flash->lock);
> +
> +       return 0;
> +}
> +
> +/*
>  * Write an address range to the flash chip.  Data must be written in
>  * FLASH_PAGESIZE chunks.  The address range may be any size provided
>  * it is within the physical boundaries.
> @@ -874,6 +949,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
>        flash->mtd.erase = m25p80_erase;
>        flash->mtd.read = m25p80_read;
>
> +       if (spi->master->quirks & SPI_QUIRK_TRANS_LEN_LIMIT)
> +               flash->mtd.read = m25p80_page_read;
> +
>        /* sst flash chips use AAI word program */
>        if (info->jedec_id >> 16 == 0xbf)
>                flash->mtd.write = sst_write;
> diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
> index 5cd741f..c8d8c2d 100644
> --- a/drivers/spi/spi_fsl_lib.c
> +++ b/drivers/spi/spi_fsl_lib.c
> @@ -135,6 +135,10 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
>        master->cleanup = mpc8xxx_spi_cleanup;
>        master->dev.of_node = dev->of_node;
>
> +       if (of_get_property(dev->of_node,
> +                               "fsl,spi-quirk-trans-len-limit", NULL))
> +               master->quirks |= SPI_QUIRK_TRANS_LEN_LIMIT;
> +
>        mpc8xxx_spi = spi_master_get_devdata(master);
>        mpc8xxx_spi->dev = dev;
>        mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 92e52a1..4234dfd 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -304,6 +304,11 @@ struct spi_master {
>
>        /* called on release() to free memory provided by spi_master */
>        void                    (*cleanup)(struct spi_device *spi);
> +
> +       /* some constraints of the controller */
> +       u16                     quirks;
> +#define SPI_QUIRK_TRANS_LEN_LIMIT      BIT(0)  /* have trans length limit */
> +
>  };
>
>  static inline void *spi_master_get_devdata(struct spi_master *master)
> --
> 1.6.4
>
>
>

Patch

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 47d53c7..f65cca8 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -377,6 +377,81 @@  static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
 }
 
 /*
+ * Read an address range from the flash chip page by page.
+ * Some controller has transaction length limitation such as the
+ * Freescale's eSPI controller can only trasmit 0xFFFF bytes one
+ * time, so we have to read page by page if the len is more than
+ * the limitation.
+ */
+static int m25p80_page_read(struct mtd_info *mtd, loff_t from, size_t len,
+	size_t *retlen, u_char *buf)
+{
+	struct m25p *flash = mtd_to_m25p(mtd);
+	struct spi_transfer t[2];
+	struct spi_message m;
+	u32 i, page_size = 0;
+
+	DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
+			dev_name(&flash->spi->dev), __func__, "from",
+			(u32)from, len);
+
+	/* sanity checks */
+	if (!len)
+		return 0;
+
+	if (from + len > flash->mtd.size)
+		return -EINVAL;
+
+	spi_message_init(&m);
+	memset(t, 0, (sizeof t));
+
+	/* NOTE:
+	 * OPCODE_FAST_READ (if available) is faster.
+	 * Should add 1 byte DUMMY_BYTE.
+	 */
+	t[0].tx_buf = flash->command;
+	t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	spi_message_add_tail(&t[1], &m);
+
+	/* Byte count starts at zero. */
+	if (retlen)
+		*retlen = 0;
+
+	mutex_lock(&flash->lock);
+
+	/* Wait till previous write/erase is done. */
+	if (wait_till_ready(flash)) {
+		/* REVISIT status return?? */
+		mutex_unlock(&flash->lock);
+		return 1;
+	}
+
+	/* Set up the write data buffer. */
+	flash->command[0] = OPCODE_READ;
+
+	for (i = page_size; i < len; i += page_size) {
+		page_size = len - i;
+		if (page_size > flash->page_size)
+			page_size = flash->page_size;
+		m25p_addr2cmd(flash, from + i, flash->command);
+		t[1].len = page_size;
+		t[1].rx_buf = buf + i;
+
+		spi_sync(flash->spi, &m);
+
+		*retlen += m.actual_length - m25p_cmdsz(flash)
+			- FAST_READ_DUMMY_BYTE;
+	}
+
+	mutex_unlock(&flash->lock);
+
+	return 0;
+}
+
+/*
  * Write an address range to the flash chip.  Data must be written in
  * FLASH_PAGESIZE chunks.  The address range may be any size provided
  * it is within the physical boundaries.
@@ -874,6 +949,9 @@  static int __devinit m25p_probe(struct spi_device *spi)
 	flash->mtd.erase = m25p80_erase;
 	flash->mtd.read = m25p80_read;
 
+	if (spi->master->quirks & SPI_QUIRK_TRANS_LEN_LIMIT)
+		flash->mtd.read = m25p80_page_read;
+
 	/* sst flash chips use AAI word program */
 	if (info->jedec_id >> 16 == 0xbf)
 		flash->mtd.write = sst_write;
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
index 5cd741f..c8d8c2d 100644
--- a/drivers/spi/spi_fsl_lib.c
+++ b/drivers/spi/spi_fsl_lib.c
@@ -135,6 +135,10 @@  int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
 	master->cleanup = mpc8xxx_spi_cleanup;
 	master->dev.of_node = dev->of_node;
 
+	if (of_get_property(dev->of_node,
+				"fsl,spi-quirk-trans-len-limit", NULL))
+		master->quirks |= SPI_QUIRK_TRANS_LEN_LIMIT;
+
 	mpc8xxx_spi = spi_master_get_devdata(master);
 	mpc8xxx_spi->dev = dev;
 	mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 92e52a1..4234dfd 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -304,6 +304,11 @@  struct spi_master {
 
 	/* called on release() to free memory provided by spi_master */
 	void			(*cleanup)(struct spi_device *spi);
+
+	/* some constraints of the controller */
+	u16			quirks;
+#define SPI_QUIRK_TRANS_LEN_LIMIT	BIT(0)	/* have trans length limit */
+
 };
 
 static inline void *spi_master_get_devdata(struct spi_master *master)