Message ID | 900653cccf2903386a992ea2b702b04dc8205407.1442330503.git.cyrille.pitchen@atmel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 15 September 2015 at 20:58, Cyrille Pitchen <cyrille.pitchen@atmel.com> wrote: > When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor > memories don't reply to the regular Read ID (0x9f) command. Instead they > reply to a new dedicated command Read ID Multiple I/O (0xaf). > > If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's), > then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol > (supported by both Micron and Macronix memories), lately with SPI-2-2-2 > protocol (supported only by Micron memories). > > Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> > --- > drivers/mtd/devices/m25p80.c | 8 +----- > drivers/mtd/spi-nor/fsl-quadspi.c | 2 +- > drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------ > drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++----- > include/linux/mtd/spi-nor.h | 27 +++++++++++++++--- > 5 files changed, 81 insertions(+), 28 deletions(-) > > diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c > index 4b5d7a4655fd..1457866a4930 100644 > --- a/drivers/mtd/devices/m25p80.c > +++ b/drivers/mtd/devices/m25p80.c > @@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi) > struct flash_platform_data *data; > struct m25p *flash; > struct spi_nor *nor; > - enum read_mode mode = SPI_NOR_NORMAL; > char *flash_name = NULL; > int ret; > > @@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi) > spi_set_drvdata(spi, flash); > flash->spi = spi; > > - if (spi->mode & SPI_RX_QUAD) > - mode = SPI_NOR_QUAD; > - else if (spi->mode & SPI_RX_DUAL) > - mode = SPI_NOR_DUAL; > - > if (data && data->name) > nor->mtd.name = data->name; > > @@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi) > else > flash_name = spi->modalias; > > - ret = spi_nor_scan(nor, flash_name, mode); > + ret = spi_nor_scan(nor, flash_name, spi->mode); IMHO, this is certainly incorrect because spi-nor never know anything about spi (Linux) that is why this framework got into picture. > if (ret) > return ret; > > diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c > index 2954f89fc8be..1ded5dbe2240 100644 > --- a/drivers/mtd/spi-nor/fsl-quadspi.c > +++ b/drivers/mtd/spi-nor/fsl-quadspi.c > @@ -1033,7 +1033,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) > /* set the chip address for READID */ > fsl_qspi_set_base_addr(q, nor); > > - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); > + ret = spi_nor_scan(nor, NULL, SPI_RX_QUAD); > if (ret) > goto mutex_failed; > > diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c > index 9e82098ae644..c499f3258245 100644 > --- a/drivers/mtd/spi-nor/nxp-spifi.c > +++ b/drivers/mtd/spi-nor/nxp-spifi.c > @@ -272,7 +272,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > struct device_node *np) > { > struct mtd_part_parser_data ppdata; > - enum read_mode flash_read; > u32 ctrl, property; > u16 mode = 0; > int ret; > @@ -304,16 +303,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > SPIFI_CTRL_CSHIGH(15) | > SPIFI_CTRL_FBCLK; > > - if (mode & SPI_RX_DUAL) { > + if (mode & SPI_RX_DUAL) > ctrl |= SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_DUAL; > - } else if (mode & SPI_RX_QUAD) { > + else if (mode & SPI_RX_QUAD) > ctrl &= ~SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_QUAD; > - } else { > + else > ctrl |= SPIFI_CTRL_DUAL; > - flash_read = SPI_NOR_NORMAL; > - } > > switch (mode & (SPI_CPHA | SPI_CPOL)) { > case SPI_MODE_0: > @@ -349,7 +344,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, > */ > nxp_spifi_dummy_id_read(&spifi->nor); > > - ret = spi_nor_scan(&spifi->nor, NULL, flash_read); > + ret = spi_nor_scan(&spifi->nor, NULL, mode); > if (ret) { > dev_err(spifi->dev, "device scan failed\n"); > return ret; > diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c > index 8818d4325d20..1908038c8f2e 100644 > --- a/drivers/mtd/spi-nor/spi-nor.c > +++ b/drivers/mtd/spi-nor/spi-nor.c > @@ -20,6 +20,7 @@ > #include <linux/mtd/cfi.h> > #include <linux/mtd/mtd.h> > #include <linux/of_platform.h> > +#include <linux/spi/spi.h> Same comment as above - don't use spi on spi-nor. > #include <linux/spi/flash.h> > #include <linux/mtd/spi-nor.h> > > @@ -61,6 +62,11 @@ struct flash_info { > > #define JEDEC_MFR(info) ((info)->id[0]) > > +struct read_id_proto { > + enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */ > + u16 mode; /* SPI controller required caps */ > +}; > + > static const struct flash_info *spi_nor_match_id(const char *name); > > /* > @@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = { > { }, > }; > > -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) > +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode) > { > - int tmp; > + int i, tmp; > u8 id[SPI_NOR_MAX_ID_LEN]; > const struct flash_info *info; > + static const struct read_id_proto proto[] = { > + { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD }, > + { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL } > + }; > > tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); > if (tmp < 0) { > @@ -713,6 +723,35 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) > return ERR_PTR(tmp); > } > > + /* Special case for Micron/Macronix qspi nor. */ > + for (i = 0; i < ARRAY_SIZE(proto); ++i) { > + if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) || > + (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00))) > + break; > + > + /* Check whether the SPI controller supports this protocol. */ > + if ((mode & proto[i].mode) != proto[i].mode) > + continue; > + > + nor->erase_proto = proto[i].proto; > + nor->read_proto = proto[i].proto; > + nor->write_proto = proto[i].proto; > + nor->reg_proto = proto[i].proto; > + > + /* > + * Multiple I/O Read ID only returns the Manufacturer ID > + * (1 byte) and the Device ID (2 bytes). So we reset the > + * remaining bytes. > + */ > + memset(id, 0, sizeof(id)); > + tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3); > + if (tmp < 0) { > + dev_dbg(nor->dev, > + " error %d reading JEDEC ID (MULTI IO)\n", tmp); > + return ERR_PTR(tmp); > + } > + } > + > for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { > info = &spi_nor_ids[tmp]; > if (info->id_len) { > @@ -999,7 +1038,7 @@ static int spi_nor_check(struct spi_nor *nor) > return 0; > } > > -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode) > { > const struct flash_info *info = NULL; > struct device *dev = nor->dev; > @@ -1012,11 +1051,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > if (ret) > return ret; > > + /* Reset SPI protocol for all commands */ > + nor->erase_proto = SPI_PROTO_1_1_1; > + nor->read_proto = SPI_PROTO_1_1_1; > + nor->write_proto = SPI_PROTO_1_1_1; > + nor->reg_proto = SPI_PROTO_1_1_1; > + > if (name) > info = spi_nor_match_id(name); > /* Try to auto-detect if chip name wasn't specified or not found */ > if (!info) > - info = spi_nor_read_id(nor); > + info = spi_nor_read_id(nor, mode); > if (IS_ERR_OR_NULL(info)) > return -ENOENT; > > @@ -1027,7 +1072,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > if (name && info->id_len) { > const struct flash_info *jinfo; > > - jinfo = spi_nor_read_id(nor); > + jinfo = spi_nor_read_id(nor, mode); > if (IS_ERR(jinfo)) { > return PTR_ERR(jinfo); > } else if (jinfo != info) { > @@ -1126,14 +1171,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) > 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) { > + if (mode & SPI_RX_QUAD && info->flags & SPI_NOR_QUAD_READ) { > ret = set_quad_mode(nor, info); > if (ret) { > dev_err(dev, "quad mode not supported\n"); > return ret; > } > nor->flash_read = SPI_NOR_QUAD; > - } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { > + } else if (mode & SPI_RX_DUAL && info->flags & SPI_NOR_DUAL_READ) { > nor->flash_read = SPI_NOR_DUAL; > } > > diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h > index 672595a381c5..32339e82cb9d 100644 > --- a/include/linux/mtd/spi-nor.h > +++ b/include/linux/mtd/spi-nor.h > @@ -57,8 +57,9 @@ > #define SPINOR_OP_BRWR 0x17 /* Bank register write */ > > /* Used for Micron flashes only. */ > -#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ > -#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ > +#define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */ > +#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ > +#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ > > /* Status Register bits. */ > #define SR_WIP 1 /* Write in progress */ > @@ -87,6 +88,16 @@ enum read_mode { > SPI_NOR_QUAD, > }; > > +enum spi_protocol { > + SPI_PROTO_1_1_1, /* SPI */ > + SPI_PROTO_1_1_2, /* Dual Output */ > + SPI_PROTO_1_1_4, /* Quad Output */ > + SPI_PROTO_1_2_2, /* Dual IO */ > + SPI_PROTO_1_4_4, /* Quad IO */ > + SPI_PROTO_2_2_2, /* Dual Command */ > + SPI_PROTO_4_4_4, /* Quad Command */ > +}; > + > #define SPI_NOR_MAX_CMD_SIZE 8 > enum spi_nor_ops { > SPI_NOR_OPS_READ = 0, > @@ -117,6 +128,10 @@ struct mtd_info; > * @flash_read: the mode of the read > * @sst_write_second: used by the SST write operation > * @flags: flag options for the current SPI-NOR (SNOR_F_*) > + * @erase_proto: the SPI protocol used by erase operations > + * @read_proto: the SPI protocol used by read operations > + * @write_proto: the SPI protocol used by write operations > + * @reg_proto the SPI protocol used by read_reg/write_reg operations > * @cmd_buf: used by the write_reg > * @prepare: [OPTIONAL] do some preparations for the > * read/write/erase/lock/unlock operations > @@ -143,6 +158,10 @@ struct spi_nor { > u8 read_opcode; > u8 read_dummy; > u8 program_opcode; > + enum spi_protocol erase_proto; > + enum spi_protocol read_proto; > + enum spi_protocol write_proto; > + enum spi_protocol reg_proto; > enum read_mode flash_read; > bool sst_write_second; > u32 flags; > @@ -169,7 +188,7 @@ struct spi_nor { > * spi_nor_scan() - scan the SPI NOR > * @nor: the spi_nor structure > * @name: the chip type name > - * @mode: the read mode supported by the driver > + * @mode: the bitmask or TX/RX modes supported by the driver > * > * The drivers can use this fuction to scan the SPI NOR. > * In the scanning, it will try to get all the necessary information to > @@ -179,6 +198,6 @@ struct spi_nor { > * > * Return: 0 for success, others for failure. > */ > -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode); > +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode); > > #endif > -- > 1.8.2.2 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel thanks!
Hi Jagan, Le 15/09/2015 19:53, Jagan Teki a écrit : > On 15 September 2015 at 20:58, Cyrille Pitchen > <cyrille.pitchen@atmel.com> wrote: >> When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor >> memories don't reply to the regular Read ID (0x9f) command. Instead they >> reply to a new dedicated command Read ID Multiple I/O (0xaf). >> >> If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's), >> then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol >> (supported by both Micron and Macronix memories), lately with SPI-2-2-2 >> protocol (supported only by Micron memories). >> >> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> >> --- >> drivers/mtd/devices/m25p80.c | 8 +----- >> drivers/mtd/spi-nor/fsl-quadspi.c | 2 +- >> drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------ >> drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++----- >> include/linux/mtd/spi-nor.h | 27 +++++++++++++++--- >> 5 files changed, 81 insertions(+), 28 deletions(-) >> >> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c >> index 4b5d7a4655fd..1457866a4930 100644 >> --- a/drivers/mtd/devices/m25p80.c >> +++ b/drivers/mtd/devices/m25p80.c >> @@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi) >> struct flash_platform_data *data; >> struct m25p *flash; >> struct spi_nor *nor; >> - enum read_mode mode = SPI_NOR_NORMAL; >> char *flash_name = NULL; >> int ret; >> >> @@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi) >> spi_set_drvdata(spi, flash); >> flash->spi = spi; >> >> - if (spi->mode & SPI_RX_QUAD) >> - mode = SPI_NOR_QUAD; >> - else if (spi->mode & SPI_RX_DUAL) >> - mode = SPI_NOR_DUAL; >> - >> if (data && data->name) >> nor->mtd.name = data->name; >> >> @@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi) >> else >> flash_name = spi->modalias; >> >> - ret = spi_nor_scan(nor, flash_name, mode); >> + ret = spi_nor_scan(nor, flash_name, spi->mode); > > IMHO, this is certainly incorrect because spi-nor never know anything > about spi (Linux) that is why this framework got into picture. > OK but what to use instead? Because we need to know the SPI controller capabilities as compared to what the SPI NOR memory expects. SPI_NOR_QUAD is just not enough as it doesn't allow us to make the difference between SPI 1-1-4, 1-4-4 or 4-4-4 protocols. Some manufacturer specific commands like Multiple I/O Read ID (0xaf) only work with the SPI 4-4-4 protocol. Also patch 3 (mtd: spi-nor: set the read op code and protocol based on the manufacturer) makes use of the SPI controller capabilities to select the right Fast Read op code and SPI protocol to match the SPI NOR memory interface. Currently the framework always uses the Fast Read Quad Output 1-1-4 (0x6b). Then for Macronix QSPI memories, the framework also enables their QPI mode. However the datasheet of Macronix MX66L1G claims this command (0x6b) is only supported in in SPI mode but not in QPI mode. Also for Micron QSPI memories, the framework turns their Quad mode on but once enabled the spi-nor memories expected ALL commands to use the SPI 4-4-4 protocol. SPI controllers have no mean to be notified about that protocol change. Besides, the m25p80 driver only checks the SPI_RX_QUAD flag before asking the spi-nor framework to use some Quad SPI mode but in many cases it's not enough; the SPI_TX_QUAD flag is also needed. Of course there are other ways to provide the spi-nor framework with the SPI controller. Maybe we can still use a bitmask for its simplicity of use by changing a little bit the definition of the spi_protocol constants: #define SPI_PROTO_1_1_1 0x0001 #define SPI_PROTO_1_1_2 0x0002 #define SPI_PROTO_1_1_4 0x0004 #define SPI_PROTO_1_2_2 0x0008 #define SPI_PROTO_1_4_4 0x0010 #define SPI_PROTO_2_2_2 0x0020 #define SPI_PROTO_4_4_4 0x0040 Then for instance in m25p80.c, we do something like: unsigned int supported_protocols = SPI_PROTO_1_1_1; if (spi->mode & SPI_RX_QUAD) { supported_protocols |= SPI_PROTO_1_1_4; if (spi->mode & SPI_TX_QUAD) supported_protocols |= (SPI_PROTO_1_4_4 | SPI_PROTO_4_4_4); } if (spi->mode & SPI_RX_DUAL) { supported_protocols |= SPI_PROTO_1_1_2; if (spi->mode & SPI_TX_DUAL) supported_protocols |= (SPI_PROTO_1_2_2 | SPI_PROTO_2_2_2); } ret = spi_nor_scan(nor, flash_name, supported_protocols); Does it sound better to you so we don't use the flags defined in <linux/spi/spi.h> in the spi-nor framework ? [...] >> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c >> index 8818d4325d20..1908038c8f2e 100644 >> --- a/drivers/mtd/spi-nor/spi-nor.c >> +++ b/drivers/mtd/spi-nor/spi-nor.c >> @@ -20,6 +20,7 @@ >> #include <linux/mtd/cfi.h> >> #include <linux/mtd/mtd.h> >> #include <linux/of_platform.h> >> +#include <linux/spi/spi.h> > > Same comment as above - don't use spi on spi-nor. > >> #include <linux/spi/flash.h> >> #include <linux/mtd/spi-nor.h> >> >> @@ -61,6 +62,11 @@ struct flash_info { >> >> #define JEDEC_MFR(info) ((info)->id[0]) >> >> +struct read_id_proto { >> + enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */ >> + u16 mode; /* SPI controller required caps */ >> +}; >> + >> static const struct flash_info *spi_nor_match_id(const char *name); >> >> /* >> @@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = { >> { }, >> }; >> >> -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) >> +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode) >> { >> - int tmp; >> + int i, tmp; >> u8 id[SPI_NOR_MAX_ID_LEN]; >> const struct flash_info *info; >> + static const struct read_id_proto proto[] = { >> + { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD }, >> + { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL } >> + }; >> >> tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); >> if (tmp < 0) { >> @@ -713,6 +723,35 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) >> return ERR_PTR(tmp); >> } >> >> + /* Special case for Micron/Macronix qspi nor. */ >> + for (i = 0; i < ARRAY_SIZE(proto); ++i) { >> + if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) || >> + (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00))) >> + break; >> + >> + /* Check whether the SPI controller supports this protocol. */ >> + if ((mode & proto[i].mode) != proto[i].mode) >> + continue; >> + >> + nor->erase_proto = proto[i].proto; >> + nor->read_proto = proto[i].proto; >> + nor->write_proto = proto[i].proto; >> + nor->reg_proto = proto[i].proto; >> + >> + /* >> + * Multiple I/O Read ID only returns the Manufacturer ID >> + * (1 byte) and the Device ID (2 bytes). So we reset the >> + * remaining bytes. >> + */ >> + memset(id, 0, sizeof(id)); >> + tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3); >> + if (tmp < 0) { >> + dev_dbg(nor->dev, >> + " error %d reading JEDEC ID (MULTI IO)\n", tmp); >> + return ERR_PTR(tmp); >> + } >> + } >> + >> for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { >> info = &spi_nor_ids[tmp]; >> if (info->id_len) { [...] > > > thanks! > Best Regards, Cyrille -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 4b5d7a4655fd..1457866a4930 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi) struct flash_platform_data *data; struct m25p *flash; struct spi_nor *nor; - enum read_mode mode = SPI_NOR_NORMAL; char *flash_name = NULL; int ret; @@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi) spi_set_drvdata(spi, flash); flash->spi = spi; - if (spi->mode & SPI_RX_QUAD) - mode = SPI_NOR_QUAD; - else if (spi->mode & SPI_RX_DUAL) - mode = SPI_NOR_DUAL; - if (data && data->name) nor->mtd.name = data->name; @@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi) else flash_name = spi->modalias; - ret = spi_nor_scan(nor, flash_name, mode); + ret = spi_nor_scan(nor, flash_name, spi->mode); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 2954f89fc8be..1ded5dbe2240 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -1033,7 +1033,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* set the chip address for READID */ fsl_qspi_set_base_addr(q, nor); - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); + ret = spi_nor_scan(nor, NULL, SPI_RX_QUAD); if (ret) goto mutex_failed; diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c index 9e82098ae644..c499f3258245 100644 --- a/drivers/mtd/spi-nor/nxp-spifi.c +++ b/drivers/mtd/spi-nor/nxp-spifi.c @@ -272,7 +272,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, struct device_node *np) { struct mtd_part_parser_data ppdata; - enum read_mode flash_read; u32 ctrl, property; u16 mode = 0; int ret; @@ -304,16 +303,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, SPIFI_CTRL_CSHIGH(15) | SPIFI_CTRL_FBCLK; - if (mode & SPI_RX_DUAL) { + if (mode & SPI_RX_DUAL) ctrl |= SPIFI_CTRL_DUAL; - flash_read = SPI_NOR_DUAL; - } else if (mode & SPI_RX_QUAD) { + else if (mode & SPI_RX_QUAD) ctrl &= ~SPIFI_CTRL_DUAL; - flash_read = SPI_NOR_QUAD; - } else { + else ctrl |= SPIFI_CTRL_DUAL; - flash_read = SPI_NOR_NORMAL; - } switch (mode & (SPI_CPHA | SPI_CPOL)) { case SPI_MODE_0: @@ -349,7 +344,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, */ nxp_spifi_dummy_id_read(&spifi->nor); - ret = spi_nor_scan(&spifi->nor, NULL, flash_read); + ret = spi_nor_scan(&spifi->nor, NULL, mode); if (ret) { dev_err(spifi->dev, "device scan failed\n"); return ret; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 8818d4325d20..1908038c8f2e 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -20,6 +20,7 @@ #include <linux/mtd/cfi.h> #include <linux/mtd/mtd.h> #include <linux/of_platform.h> +#include <linux/spi/spi.h> #include <linux/spi/flash.h> #include <linux/mtd/spi-nor.h> @@ -61,6 +62,11 @@ struct flash_info { #define JEDEC_MFR(info) ((info)->id[0]) +struct read_id_proto { + enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */ + u16 mode; /* SPI controller required caps */ +}; + static const struct flash_info *spi_nor_match_id(const char *name); /* @@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = { { }, }; -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode) { - int tmp; + int i, tmp; u8 id[SPI_NOR_MAX_ID_LEN]; const struct flash_info *info; + static const struct read_id_proto proto[] = { + { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD }, + { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL } + }; tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); if (tmp < 0) { @@ -713,6 +723,35 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) return ERR_PTR(tmp); } + /* Special case for Micron/Macronix qspi nor. */ + for (i = 0; i < ARRAY_SIZE(proto); ++i) { + if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) || + (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00))) + break; + + /* Check whether the SPI controller supports this protocol. */ + if ((mode & proto[i].mode) != proto[i].mode) + continue; + + nor->erase_proto = proto[i].proto; + nor->read_proto = proto[i].proto; + nor->write_proto = proto[i].proto; + nor->reg_proto = proto[i].proto; + + /* + * Multiple I/O Read ID only returns the Manufacturer ID + * (1 byte) and the Device ID (2 bytes). So we reset the + * remaining bytes. + */ + memset(id, 0, sizeof(id)); + tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3); + if (tmp < 0) { + dev_dbg(nor->dev, + " error %d reading JEDEC ID (MULTI IO)\n", tmp); + return ERR_PTR(tmp); + } + } + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { info = &spi_nor_ids[tmp]; if (info->id_len) { @@ -999,7 +1038,7 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode) { const struct flash_info *info = NULL; struct device *dev = nor->dev; @@ -1012,11 +1051,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) if (ret) return ret; + /* Reset SPI protocol for all commands */ + nor->erase_proto = SPI_PROTO_1_1_1; + nor->read_proto = SPI_PROTO_1_1_1; + nor->write_proto = SPI_PROTO_1_1_1; + nor->reg_proto = SPI_PROTO_1_1_1; + if (name) info = spi_nor_match_id(name); /* Try to auto-detect if chip name wasn't specified or not found */ if (!info) - info = spi_nor_read_id(nor); + info = spi_nor_read_id(nor, mode); if (IS_ERR_OR_NULL(info)) return -ENOENT; @@ -1027,7 +1072,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) if (name && info->id_len) { const struct flash_info *jinfo; - jinfo = spi_nor_read_id(nor); + jinfo = spi_nor_read_id(nor, mode); if (IS_ERR(jinfo)) { return PTR_ERR(jinfo); } else if (jinfo != info) { @@ -1126,14 +1171,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) 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) { + if (mode & SPI_RX_QUAD && info->flags & SPI_NOR_QUAD_READ) { ret = set_quad_mode(nor, info); if (ret) { dev_err(dev, "quad mode not supported\n"); return ret; } nor->flash_read = SPI_NOR_QUAD; - } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { + } else if (mode & SPI_RX_DUAL && info->flags & SPI_NOR_DUAL_READ) { nor->flash_read = SPI_NOR_DUAL; } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 672595a381c5..32339e82cb9d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -57,8 +57,9 @@ #define SPINOR_OP_BRWR 0x17 /* Bank register write */ /* Used for Micron flashes only. */ -#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ -#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ +#define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */ +#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ +#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ @@ -87,6 +88,16 @@ enum read_mode { SPI_NOR_QUAD, }; +enum spi_protocol { + SPI_PROTO_1_1_1, /* SPI */ + SPI_PROTO_1_1_2, /* Dual Output */ + SPI_PROTO_1_1_4, /* Quad Output */ + SPI_PROTO_1_2_2, /* Dual IO */ + SPI_PROTO_1_4_4, /* Quad IO */ + SPI_PROTO_2_2_2, /* Dual Command */ + SPI_PROTO_4_4_4, /* Quad Command */ +}; + #define SPI_NOR_MAX_CMD_SIZE 8 enum spi_nor_ops { SPI_NOR_OPS_READ = 0, @@ -117,6 +128,10 @@ struct mtd_info; * @flash_read: the mode of the read * @sst_write_second: used by the SST write operation * @flags: flag options for the current SPI-NOR (SNOR_F_*) + * @erase_proto: the SPI protocol used by erase operations + * @read_proto: the SPI protocol used by read operations + * @write_proto: the SPI protocol used by write operations + * @reg_proto the SPI protocol used by read_reg/write_reg operations * @cmd_buf: used by the write_reg * @prepare: [OPTIONAL] do some preparations for the * read/write/erase/lock/unlock operations @@ -143,6 +158,10 @@ struct spi_nor { u8 read_opcode; u8 read_dummy; u8 program_opcode; + enum spi_protocol erase_proto; + enum spi_protocol read_proto; + enum spi_protocol write_proto; + enum spi_protocol reg_proto; enum read_mode flash_read; bool sst_write_second; u32 flags; @@ -169,7 +188,7 @@ struct spi_nor { * spi_nor_scan() - scan the SPI NOR * @nor: the spi_nor structure * @name: the chip type name - * @mode: the read mode supported by the driver + * @mode: the bitmask or TX/RX modes supported by the driver * * The drivers can use this fuction to scan the SPI NOR. * In the scanning, it will try to get all the necessary information to @@ -179,6 +198,6 @@ struct spi_nor { * * Return: 0 for success, others for failure. */ -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode); +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode); #endif
When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor memories don't reply to the regular Read ID (0x9f) command. Instead they reply to a new dedicated command Read ID Multiple I/O (0xaf). If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's), then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol (supported by both Micron and Macronix memories), lately with SPI-2-2-2 protocol (supported only by Micron memories). Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> --- drivers/mtd/devices/m25p80.c | 8 +----- drivers/mtd/spi-nor/fsl-quadspi.c | 2 +- drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------ drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++----- include/linux/mtd/spi-nor.h | 27 +++++++++++++++--- 5 files changed, 81 insertions(+), 28 deletions(-)