Hi Cyrille, > I have not finished to review the whole series yet but here some first > comments: Thanks for reviewing these patch series. > > Le 27/11/2016 à 09:33, Naga Sureshkumar Relli a écrit : > > This patch adds stripe support and it is needed for GQSPI parallel > > configuration mode by: > > > > - Adding required parameters like stripe and shift to spi_nor > > structure. > > - Initializing all added parameters in spi_nor_scan() > > - Updating read_sr() and read_fsr() for getting status from both > > flashes > > - Increasing page_size, sector_size, erase_size and toatal flash > > size as and when required. > > - Dividing address by 2 > > - Updating spi->master->flags for qspi driver to change CS > > > > Signed-off-by: Naga Sureshkumar Relli <nagasure@xxxxxxxxxx> > > --- > > Changes for v4: > > - rename isparallel to stripe > > Changes for v3: > > - No change > > Changes for v2: > > - Splitted to separate MTD layer changes from SPI core changes > > --- > > drivers/mtd/spi-nor/spi-nor.c | 130 > ++++++++++++++++++++++++++++++++---------- > > include/linux/mtd/spi-nor.h | 2 + > > 2 files changed, 103 insertions(+), 29 deletions(-) > > > > diff --git a/drivers/mtd/spi-nor/spi-nor.c > > b/drivers/mtd/spi-nor/spi-nor.c index d0fc165..4252239 100644 > > --- a/drivers/mtd/spi-nor/spi-nor.c > > +++ b/drivers/mtd/spi-nor/spi-nor.c > > @@ -22,6 +22,7 @@ > > #include <linux/of_platform.h> > > #include <linux/spi/flash.h> > > #include <linux/mtd/spi-nor.h> > > +#include <linux/spi/spi.h> > > > > /* Define max times to check status register before we give up. */ > > > > @@ -89,15 +90,24 @@ static const struct flash_info > > *spi_nor_match_id(const char *name); static int read_sr(struct > > spi_nor *nor) { > > int ret; > > - u8 val; > > + u8 val[2]; > > > > - ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1); > > - if (ret < 0) { > > - pr_err("error %d reading SR\n", (int) ret); > > - return ret; > > + if (nor->stripe) { > > + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 2); > > + if (ret < 0) { > > + pr_err("error %d reading SR\n", (int) ret); > > + return ret; > > + } > > + val[0] |= val[1]; > Why '|' rather than '&' ? > I guess because of the 'Write In Progress/Busy' bit: when called by > spi_nor_sr_ready(), you want to be sure that this 'BUSY' bit is cleared on > both memories. > > But what about when the Status Register is read for purpose other than > checking the state of the 'BUSY' bit? > Yes you are correct, I will change this. > What about SPI controllers supporting more than 2 memories in parallel? > > This solution might fit the ZynqMP controller but doesn't look so generic. > > > + } else { > > + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 1); > > + if (ret < 0) { > > + pr_err("error %d reading SR\n", (int) ret); > > + return ret; > > + } > > } > > > > - return val; > > + return val[0]; > > } > > > > /* > > @@ -108,15 +118,24 @@ static int read_sr(struct spi_nor *nor) static > > int read_fsr(struct spi_nor *nor) { > > int ret; > > - u8 val; > > + u8 val[2]; > > > > - ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1); > > - if (ret < 0) { > > - pr_err("error %d reading FSR\n", ret); > > - return ret; > > + if (nor->stripe) { > > + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 2); > > + if (ret < 0) { > > + pr_err("error %d reading FSR\n", ret); > > + return ret; > > + } > > + val[0] &= val[1]; > Same comment here: why '&' rather than '|'? > Surely because of the the 'READY' bit which should be set for both memories. I will update this also. > > > + } else { > > + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 1); > > + if (ret < 0) { > > + pr_err("error %d reading FSR\n", ret); > > + return ret; > > + } > > } > > > > - return val; > > + return val[0]; > > } > > > > /* > > @@ -290,9 +309,16 @@ static int spi_nor_wait_till_ready(struct spi_nor > *nor) > > */ > > static int erase_chip(struct spi_nor *nor) { > > + u32 ret; > > + > > dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); > > > > - return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); > > + ret = nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); > > + if (ret) > > + return ret; > > + > > + return ret; > > + > > if (ret) > return ret; > else > return ret; > > This chunk should be removed, it doesn't ease the patch review ;) Ok, I will remove. > > > } > > > > static int spi_nor_lock_and_prep(struct spi_nor *nor, enum > > spi_nor_ops ops) @@ -349,7 +375,7 @@ static int > > spi_nor_erase_sector(struct spi_nor *nor, u32 addr) static int > > spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { > > struct spi_nor *nor = mtd_to_spi_nor(mtd); > > - u32 addr, len; > > + u32 addr, len, offset; > > uint32_t rem; > > int ret; > > > > @@ -399,9 +425,13 @@ static int spi_nor_erase(struct mtd_info *mtd, > struct erase_info *instr) > > /* "sector"-at-a-time erase */ > > } else { > > while (len) { > > + > > write_enable(nor); > > + offset = addr; > > + if (nor->stripe) > > + offset /= 2; > > I guess this should be /= 4 for controllers supporting 4 memories in parallel. > Shouldn't you use nor->shift and define shift as an unsigned int instead of a > bool? > offset >>= nor->shift; > Yes we can use this shift, I will update > Anyway, by tuning the address here in spi-nor.c rather than in the SPI > controller driver, you impose a model to support parallel memories that > might not be suited to other controllers. For this ZynqMP GQSPI controller parallel configuration, globally spi-nor should know about this stripe feature And based on that address has to change. As I mentioned in cover letter, this controller in parallel configuration will work with even addresses only. i.e. Before creating address format(m25p_addr2cmd) in mtd layer, spi-nor should change that address based on stripe option. I am updating this offset based on stripe option, and stripe option will update by reading dt property in nor_scan(). So the controller which doesn't support, then the stripe will be zero. Or Can you please suggest any other way? > > > > > - ret = spi_nor_erase_sector(nor, addr); > > + ret = spi_nor_erase_sector(nor, offset); > > if (ret) > > goto erase_err; > > > > @@ -525,6 +555,8 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, > uint64_t len) > > bool use_top; > > int ret; > > > > + ofs = ofs >> nor->shift; > > + > > status_old = read_sr(nor); > > if (status_old < 0) > > return status_old; > > @@ -610,6 +642,8 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, > uint64_t len) > > bool use_top; > > int ret; > > > > + ofs = ofs >> nor->shift; > > + > > status_old = read_sr(nor); > > if (status_old < 0) > > return status_old; > > @@ -709,6 +743,8 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t > ofs, uint64_t len) > > if (ret) > > return ret; > > > > + ofs = ofs >> nor->shift; > > + > > ret = nor->flash_lock(nor, ofs, len); > > > > spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK); @@ - > 724,6 +760,8 > > @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t > len) > > if (ret) > > return ret; > > > > + ofs = ofs >> nor->shift; > > + > > ret = nor->flash_unlock(nor, ofs, len); > > > > spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK); @@ - > 1018,6 +1056,9 > > @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) > > u8 id[SPI_NOR_MAX_ID_LEN]; > > const struct flash_info *info; > > > > + nor->spi->master->flags &= ~(SPI_MASTER_BOTH_CS | > > + SPI_MASTER_DATA_STRIPE); > > + > > tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, > SPI_NOR_MAX_ID_LEN); > > if (tmp < 0) { > > dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); > @@ -1041,6 > > +1082,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, > > size_t len, { > > struct spi_nor *nor = mtd_to_spi_nor(mtd); > > int ret; > > + u32 offset = from; > > > > dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); > > > > @@ -1049,7 +1091,13 @@ static int spi_nor_read(struct mtd_info *mtd, > loff_t from, size_t len, > > return ret; > > > > while (len) { > > - ret = nor->read(nor, from, len, buf); > > + > > + offset = from; > > + > > + if (nor->stripe) > > + offset /= 2; > > + > > + ret = nor->read(nor, offset, len, buf); > > if (ret == 0) { > > /* We shouldn't see 0-length reads */ > > ret = -EIO; > > @@ -1161,6 +1209,7 @@ static int spi_nor_write(struct mtd_info *mtd, > loff_t to, size_t len, > > struct spi_nor *nor = mtd_to_spi_nor(mtd); > > size_t page_offset, page_remain, i; > > ssize_t ret; > > + u32 offset; > > > > dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); > > > > @@ -1178,9 +1227,13 @@ static int spi_nor_write(struct mtd_info *mtd, > loff_t to, size_t len, > > /* the size of data remaining on the first page */ > > page_remain = min_t(size_t, > > nor->page_size - page_offset, len - i); > > + offset = (to + i); > > + > > + if (nor->stripe) > > + offset /= 2; > > > > write_enable(nor); > > - ret = nor->write(nor, to + i, page_remain, buf + i); > > + ret = nor->write(nor, (offset), page_remain, buf + i); > > if (ret < 0) > > goto write_err; > > written = ret; > > @@ -1302,22 +1355,22 @@ static int spi_nor_check(struct spi_nor *nor) > > > > int spi_nor_scan(struct spi_nor *nor, const char *name, enum > > read_mode mode) { > > - const struct flash_info *info = NULL; > > + struct flash_info *info = NULL; > > You should not remove the const and should not try to modify members of > *info. > > > struct device *dev = nor->dev; > > struct mtd_info *mtd = &nor->mtd; > > struct device_node *np = spi_nor_get_flash_node(nor); > > - int ret; > > - int i; > > + struct device_node *np_spi; > > + int ret, i, xlnx_qspi_mode; > > > > ret = spi_nor_check(nor); > > if (ret) > > return ret; > > > > if (name) > > - info = spi_nor_match_id(name); > > + info = (struct flash_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 = (struct flash_info *)spi_nor_read_id(nor); > > if (IS_ERR_OR_NULL(info)) > > return -ENOENT; > > > Both spi_nor_match_id() and spi_nor_read_id(), when they succeed, return > a pointer to an entry of the spi_nor_ids[] array, which is located in a read- > only memory area. > > Since your patch doesn't remove the const attribute of the spi_nor_ids[], I > wonder whether it has been tested. I expect it not to work on most > architecture. > > Anyway spi_nor_ids[] should remain const. Let's take the case of eXecution > In Place (XIP) from an external memory: if spi_nor_ids[] is const, it can be > read directly from this external (read-only) memory and we never need to > copy the array in RAM, so we save some KB of RAM. > This is just an example but I guess we can find other reasons to keep this > array const. > > > @@ -1341,7 +1394,7 @@ int spi_nor_scan(struct spi_nor *nor, const char > *name, enum read_mode mode) > > */ > > dev_warn(dev, "found %s, expected %s\n", > > jinfo->name, info->name); > > - info = jinfo; > > + info = (struct flash_info *)jinfo; > > } > > } > > > > @@ -1370,6 +1423,27 @@ int spi_nor_scan(struct spi_nor *nor, const char > *name, enum read_mode mode) > > mtd->size = info->sector_size * info->n_sectors; > > mtd->_erase = spi_nor_erase; > > mtd->_read = spi_nor_read; > > +#ifdef CONFIG_OF > > + np_spi = of_get_next_parent(np); > > + > > + if (of_property_read_u32(np_spi, "xlnx,qspi-mode", > > + &xlnx_qspi_mode) < 0) { > This really looks controller specific so should not be placed in the generic spi- > nor.c file. Const is removed in info struct, because to update info members based parallel configuration. As I mentioned above, for this parallel configuration, mtd and spi-nor should know the details like mtd->size, info->sectors, sector_size and page_size. So during spi_nor_scan only mtd will update all these details, that's why I have added controller specific check to update those. Can you please suggest, any other way to let mtd and spi-nor to know about this configuration without touching the core layers? Please let me know, if I missed providing any required info. > > > + nor->shift = 0; > > + nor->stripe = 0; > > + } else if (xlnx_qspi_mode == 2) { > > + nor->shift = 1; > > + info->sector_size <<= nor->shift; > > + info->page_size <<= nor->shift; > Just don't modify *info members! :) > > > > + mtd->size <<= nor->shift; > > + nor->stripe = 1; > > + nor->spi->master->flags |= (SPI_MASTER_BOTH_CS | > > + SPI_MASTER_DATA_STRIPE); > > + } > > +#else > > + /* Default to single */ > > + nor->shift = 0; > > + nor->stripe = 0; > > +#endif > Best regards, > > Cyrille Thanks, Naga Sureshkumar Relli -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html