Hi Huang,

Sorry to address this series so late.

I have a few questions about how you determine support for these DDR
modes.

On Mon, Apr 28, 2014 at 11:53:40AM +0800, Huang Shijie wrote:
> This patch adds the DDR quad read support by the following:

To Mark / linux-spi:

Are DDR modes in the scope of drivers/spi/ at all, so that we could
someday wire it up through m25p80.c? Or is 'DDR' such a distortion of
the meaning of 'SPI' such that it will be restricted only to SPI NOR
dedicated controllers?

>   [1] add SPI_NOR_DDR_QUAD read mode.
> 
>   [2] add DDR Quad read opcodes:
>        SPINOR_OP_READ_1_4_4_D / SPINOR_OP_READ4_1_4_4_D
> 
>   [3] add set_ddr_quad_mode() to initialize for the DDR quad read.
>       Currently it only works for Spansion NOR.
> 
>   [3] about the dummy cycles.
>       We set the dummy with 8 for DDR quad read by default.

Why? That seems wrong. You need to know for sure how many cycles should
be used, not just guess a default.

>       The m25p80.c can not support the DDR quad read, but the SPI NOR 
> controller
>       can set the dummy value in its child DT node, and the SPI NOR framework
>       can parse it out.

Why does the dummy value belong in device tree? I think this can be
handled in software. You might, however, want a few other hardware
description parameters in device tree to help you.

So I think spi-nor.c needs to know a few things:

 1. Does the hardware/driver support DDR clocking?
 2. What granularity of dummy cycles are supported? So m25p80.c needs to
    communicate that it only supports dummy cycles of multiples of 8,
    and fsl-quadspi supports single clock cycles.

And spi-nor.c should be able to do the following:

 3. Set how many dummy cycles should be used.
 4. Write to the configuration register, to choose a Latency Code
    according to what the flash supports. I see modes that support 3, 6,
    7, or 8. We'd probably just go for the fastest mode, which requires
    8, right?

So far, none of this seems to require a DT binding, unless there's
something I'm missing about your fsl-quadspi controller.

> Test this patch for Spansion s25fl128s NOR flash.
> 
> Signed-off-by: Huang Shijie <[email protected]>
> ---
>  drivers/mtd/spi-nor/spi-nor.c |   54 +++++++++++++++++++++++++++++++++++++++-
>  include/linux/mtd/spi-nor.h   |    8 ++++-
>  2 files changed, 58 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index f374e44..e0bc11a 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -73,7 +73,20 @@ static int read_cr(struct spi_nor *nor)
>   */
>  static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
>  {
> +     u32 dummy;
> +
>       switch (nor->flash_read) {
> +     case SPI_NOR_DDR_QUAD:
> +             /*
> +              * The m25p80.c can not support the DDR quad read.
> +              * We set the dummy cycles to 8 by default. The SPI NOR
> +              * controller driver can set it in its child DT node.
> +              * We parse it out here.
> +              */
> +             if (nor->np && !of_property_read_u32(nor->np,
> +                             "spi-nor,ddr-quad-read-dummy", &dummy)) {
> +                     return dummy;
> +             }
>       case SPI_NOR_FAST:
>       case SPI_NOR_DUAL:
>       case SPI_NOR_QUAD:
> @@ -402,6 +415,7 @@ struct flash_info {
>  #define      SECT_4K_PMC             0x10    /* SPINOR_OP_BE_4K_PMC works 
> uniformly */
>  #define      SPI_NOR_DUAL_READ       0x20    /* Flash supports Dual Read */
>  #define      SPI_NOR_QUAD_READ       0x40    /* Flash supports Quad Read */
> +#define      SPI_NOR_DDR_QUAD_READ   0x80    /* Flash supports DDR Quad Read 
> */
>  };
>  
>  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)   \
> @@ -846,6 +860,24 @@ static int spansion_quad_enable(struct spi_nor *nor)
>       return 0;
>  }
>  
> +static int set_ddr_quad_mode(struct spi_nor *nor, u32 jedec_id)
> +{
> +     int status;
> +
> +     switch (JEDEC_MFR(jedec_id)) {
> +     case CFI_MFR_AMD: /* Spansion, actually */
> +             status = spansion_quad_enable(nor);
> +             if (status) {
> +                     dev_err(nor->dev,
> +                             "Spansion DDR quad-read not enabled\n");
> +                     return status;
> +             }
> +             return status;
> +     default:
> +             return -EINVAL;
> +     }
> +}
> +
>  static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
>  {
>       int status;
> @@ -1016,8 +1048,15 @@ int spi_nor_scan(struct spi_nor *nor, const struct 
> spi_device_id *id,
>       if (info->flags & SPI_NOR_NO_FR)
>               nor->flash_read = SPI_NOR_NORMAL;
>  
> -     /* Quad/Dual-read mode takes precedence over fast/normal */
> -     if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
> +     /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */
> +     if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) {

Hmm, I think I should probably take another look at the design of
spi-nor.c... Why does spi_nor_scan() take a single 'mode' argument? The
driver should be communicating which (multiple) modes it supports, not
selecting a single mode. spi-nor.c is the only one which knows what the
*flash* supports, so it should be combining knowledge from the
controller driver with its own knowledge of the flash.

> +             ret = set_ddr_quad_mode(nor, info->jedec_id);
> +             if (ret) {
> +                     dev_err(dev, "DDR quad mode not supported\n");
> +                     return ret;

A ramification of my comment above is that we should not be returning an
error in a situation like this; we should be able to fall back to
another known-supported mode, like SDR QUAD, SDR DUAL, or just plain
SPI -- if they're supported by the driver -- and spi-nor.c doesn't
currently have enough information to do this.

> +             }
> +             nor->flash_read = SPI_NOR_DDR_QUAD;
> +     } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
>               ret = set_quad_mode(nor, info->jedec_id);
>               if (ret) {
>                       dev_err(dev, "quad mode not supported\n");
> @@ -1030,6 +1069,14 @@ int spi_nor_scan(struct spi_nor *nor, const struct 
> spi_device_id *id,
>  
>       /* Default commands */
>       switch (nor->flash_read) {
> +     case SPI_NOR_DDR_QUAD:
> +             if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { /* Spansion */
> +                     nor->read_opcode = SPINOR_OP_READ_1_4_4_D;
> +             } else {
> +                     dev_err(dev, "DDR Quad Read is not supported.\n");
> +                     return -EINVAL;
> +             }
> +             break;
>       case SPI_NOR_QUAD:
>               nor->read_opcode = SPINOR_OP_READ_1_1_4;
>               break;
> @@ -1057,6 +1104,9 @@ int spi_nor_scan(struct spi_nor *nor, const struct 
> spi_device_id *id,
>               if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
>                       /* Dedicated 4-byte command set */
>                       switch (nor->flash_read) {
> +                     case SPI_NOR_DDR_QUAD:
> +                             nor->read_opcode = SPINOR_OP_READ4_1_4_4_D;
> +                             break;
>                       case SPI_NOR_QUAD:
>                               nor->read_opcode = SPINOR_OP_READ4_1_1_4;
>                               break;
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index 48fe9fc..d191a6b 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -12,10 +12,11 @@
>  
>  /*
>   * Note on opcode nomenclature: some opcodes have a format like
> - * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
> + * SPINOR_OP_FUNCTION{4,}_x_y_z{_D}. The numbers x, y, and z stand for the 
> number
>   * of I/O lines used for the opcode, address, and data (respectively). The
>   * FUNCTION has an optional suffix of '4', to represent an opcode which
> - * requires a 4-byte (32-bit) address.
> + * requires a 4-byte (32-bit) address. The suffix of 'D' stands for the
> + * DDR mode.
>   */
>  
>  /* Flash opcodes. */
> @@ -26,6 +27,7 @@
>  #define SPINOR_OP_READ_FAST  0x0b    /* Read data bytes (high frequency) */
>  #define SPINOR_OP_READ_1_1_2 0x3b    /* Read data bytes (Dual SPI) */
>  #define SPINOR_OP_READ_1_1_4 0x6b    /* Read data bytes (Quad SPI) */
> +#define SPINOR_OP_READ_1_4_4_D       0xed    /* Read data bytes (DDR Quad 
> SPI) */
>  #define SPINOR_OP_PP         0x02    /* Page program (up to 256 bytes) */
>  #define SPINOR_OP_BE_4K              0x20    /* Erase 4KiB block */
>  #define SPINOR_OP_BE_4K_PMC  0xd7    /* Erase 4KiB block on PMC chips */
> @@ -40,6 +42,7 @@
>  #define SPINOR_OP_READ4_FAST 0x0c    /* Read data bytes (high frequency) */
>  #define SPINOR_OP_READ4_1_1_2        0x3c    /* Read data bytes (Dual SPI) */
>  #define SPINOR_OP_READ4_1_1_4        0x6c    /* Read data bytes (Quad SPI) */
> +#define SPINOR_OP_READ4_1_4_4_D      0xee    /* Read data bytes (DDR Quad 
> SPI) */
>  #define SPINOR_OP_PP_4B              0x12    /* Page program (up to 256 
> bytes) */
>  #define SPINOR_OP_SE_4B              0xdc    /* Sector erase (usually 64KiB) 
> */
>  
> @@ -74,6 +77,7 @@ enum read_mode {
>       SPI_NOR_FAST,
>       SPI_NOR_DUAL,
>       SPI_NOR_QUAD,
> +     SPI_NOR_DDR_QUAD,
>  };
>  
>  /**

So, I'll have to take another hard look at spi-nor.c soon. I think we
may need to work on the abstractions here a bit more before I merge any
new features like this.

Regards,
Brian

P.S. Is there a good reason we've defined a whole read_xfer/write_xfer
API that is completely unused?
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to