Hi Vignesh, Jagan, Thanks for the review comments.
I will split in to patches and revert. Vignesh, I will do some cleanup and try to address your comments wherever applicable in the next drop. Thanks, Ashok > -----Original Message----- > From: Vignesh Raghavendra <vigne...@ti.com> > Sent: Wednesday, December 4, 2019 3:36 PM > To: Ashok Reddy Soma <ashok...@xilinx.com>; u-boot@lists.denx.de; > ja...@amarulasolutions.com > Cc: Michal Simek <mich...@xilinx.com> > Subject: Re: [RFC PATCH] spi: spi-nor: Add dual flash support in spi-nor > framework > > Hi, > > On 26/11/19 12:39 pm, Michal Simek wrote: > > On 19. 11. 19 15:20, Ashok Reddy Soma wrote: > >> Add dual parallel and dual stacked support in spi-nor framework. > >> Add dual flash support for nor-scan, read and write. > >> > > How does the DT representation of these flashes look like? > Is it in alignment with Linux proposal here: > https://patchwork.ozlabs.org/patch/1201508/? > > Also could you provide a concise summary of what changes in programming > sequence to support dual flashes? This will ease process of review. > > >> Signed-off-by: Ashok Reddy Soma <ashok.reddy.s...@xilinx.com> > >> --- > [...] > > >> diff --git a/drivers/mtd/spi/sf_internal.h > >> b/drivers/mtd/spi/sf_internal.h index 5c643034c6..d836261f01 100644 > >> --- a/drivers/mtd/spi/sf_internal.h > >> +++ b/drivers/mtd/spi/sf_internal.h > >> @@ -15,6 +15,15 @@ > >> #define SPI_NOR_MAX_ID_LEN 6 > >> #define SPI_NOR_MAX_ADDR_WIDTH 4 > >> > >> +/* Dual SPI flash memories - see SPI_COMM_DUAL_... */ enum > >> +spi_dual_flash { > >> + SF_SINGLE_FLASH = 0, > >> + SF_DUAL_STACKED_FLASH = BIT(0), > >> + SF_DUAL_PARALLEL_FLASH = BIT(1), > >> +}; > >> + > >> +#define SPI_FLASH_16MB_BOUN 0x1000000 > >> + > > Do we need this constant? Can we just use SZ_16M instead? > > >> struct flash_info { > >> #if !CONFIG_IS_ENABLED(SPI_FLASH_TINY) > >> char *name; > >> diff --git a/drivers/mtd/spi/spi-nor-core.c > >> b/drivers/mtd/spi/spi-nor-core.c index 5a8c084255..a53c78ff22 100644 > >> --- a/drivers/mtd/spi/spi-nor-core.c > >> +++ b/drivers/mtd/spi/spi-nor-core.c > >> @@ -21,6 +21,7 @@ > >> #include <spi-mem.h> > >> #include <spi.h> > >> > >> +#include <linux/mtd/spi-nor.h> > >> #include "sf_internal.h" > >> > >> /* Define max times to check status register before we give up. */ > >> @@ -34,6 +35,11 @@ > >> > >> #define DEFAULT_READY_WAIT_JIFFIES (40UL * HZ) > >> > >> +#define SPI_MASTER_QUAD_MODE BIT(6) /* support quad > mode */ > > Why can't SPI_RX_QUAD/SPI_TX_QUAD be used? > > >> +#define SPI_MASTER_DATA_STRIPE BIT(7) /* support data stripe > */ > > What does this mean? In particular what is "data stripe"? > > >> +#define SPI_MASTER_BOTH_CS BIT(8) /* assert both chip > >> selects > */ > > I don't see any users of above flags. > > >> +#define SPI_MASTER_U_PAGE BIT(9) /* select upper flash */ > >> + > > These macros need to be moved to include/spi.h where rest of the flag fields > are defined. > I see this flag is being set in nor->spi->flags, but who uses it? Its very > difficult > to review patches when there are no users. Without any users, this comes a > dead code which will be dropped while cleaning up. > > > >> static int spi_nor_read_write_reg(struct spi_nor *nor, struct > spi_mem_op > >> *op, void *buf) > >> { > >> @@ -146,15 +152,24 @@ static ssize_t spi_nor_write_data(struct > >> spi_nor *nor, loff_t to, size_t len, 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) { > >> + if (nor->isparallel) { > >> + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 2); > >> + if (ret < 0) { > >> + pr_debug("error %d reading SR\n", (int)ret); > >> + return ret; > >> + } > >> + val[0] |= val[1]; > >> + } else { > >> + ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 1); > >> + if (ret < 0) { > >> pr_debug("error %d reading SR\n", (int)ret); > >> return ret; > > > Above bit of code is repeated more than once in this patch. > You should refractor and add a wrapper for nor->read_reg() that takes care > of these manipulations for dual parallel flash. Can it be extend further to > handle stacked as well? > > Individual helpers (such as read_fsr() below) to read/write registers should > not have to bother about flash geometry but just use above helper. > > >> + } > >> } > >> > >> - return val; > >> + return val[0]; > >> } > >> > >> /* > >> @@ -165,15 +180,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) { > >> + if (nor->isparallel) { > >> + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 2); > >> + if (ret < 0) { > >> + pr_debug("error %d reading SR\n", (int)ret); > >> + return ret; > >> + } > >> + val[0] &= val[1]; > >> + } else { > >> + ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 1); > >> + if (ret < 0) { > >> pr_debug("error %d reading FSR\n", ret); > >> return ret; > >> + } > >> } > >> > >> - return val; > >> + return val[0]; > >> } > >> > >> /* > >> @@ -288,12 +312,17 @@ static u8 spi_nor_convert_3to4_erase(u8 > opcode) > >> static void spi_nor_set_4byte_opcodes(struct spi_nor *nor, > >> const struct flash_info *info) { > >> + bool shift = 0; > >> + > >> + if (nor->isparallel) > >> + shift = 1; > >> + > >> /* Do some manufacturer fixups first */ > >> switch (JEDEC_MFR(info)) { > >> case SNOR_MFR_SPANSION: > >> /* No small sector erase for 4-byte command set */ > >> nor->erase_opcode = SPINOR_OP_SE; > >> - nor->mtd.erasesize = info->sector_size; > >> + nor->mtd.erasesize = info->sector_size << shift; > >> break; > >> > >> default: > >> @@ -465,12 +494,19 @@ static int clean_bar(struct spi_nor *nor) > >> > >> static int write_bar(struct spi_nor *nor, u32 offset) { > >> - u8 cmd, bank_sel; > >> + u8 cmd, bank_sel, upage_curr; > >> int ret; > >> > >> - bank_sel = offset / SZ_16M; > >> - if (bank_sel == nor->bank_curr) > >> - goto bar_end; > >> + bank_sel = offset / (SZ_16M << nor->shift); > >> + > >> + upage_curr = nor->spi->flags & SPI_XFER_U_PAGE; > >> + > >> + if ((!nor->isstacked) || (nor->upage_prev == upage_curr)) { > >> + if (bank_sel == nor->bank_curr) > >> + goto bar_end; > >> + } else { > >> + nor->upage_prev = upage_curr; > >> + } > >> > >> cmd = nor->bank_write_cmd; > >> write_enable(nor); > >> @@ -488,8 +524,12 @@ bar_end: > >> static int read_bar(struct spi_nor *nor, const struct flash_info > >> *info) { > >> u8 curr_bank = 0; > >> + u8 curr_bank_up = 0; > >> int ret; > >> > >> + if (nor->size <= SPI_FLASH_16MB_BOUN) > >> + goto bar_end; > >> + > > Hmm, is read_bar() called when flash size is <=16M? > > >> switch (JEDEC_MFR(info)) { > >> case SNOR_MFR_SPANSION: > >> nor->bank_read_cmd = SPINOR_OP_BRRD; @@ -500,12 > +540,31 @@ static > >> int read_bar(struct spi_nor *nor, const struct flash_info *info) > >> nor->bank_write_cmd = SPINOR_OP_WREAR; > >> } > >> > >> - ret = nor->read_reg(nor, nor->bank_read_cmd, > >> + if (nor->isparallel) { > >> + nor->spi->flags |= SPI_XFER_LOWER; > >> + ret = nor->read_reg(nor, nor->bank_read_cmd, > >> &curr_bank, 1); > >> - if (ret) { > >> - debug("SF: fail to read bank addr register\n"); > >> - return ret; > >> + if (ret) > >> + return ret; > >> + > >> + nor->spi->flags |= SPI_XFER_UPPER; > >> + ret = nor->read_reg(nor, nor->bank_read_cmd, > >> + &curr_bank_up, 1); > >> + if (ret) > >> + return ret; > >> + if (curr_bank != curr_bank_up) { > >> + printf("Incorrect Bank selections Dual parallel\n"); > >> + return -EINVAL; > >> + } > >> + } else { > >> + ret = nor->read_reg(nor, nor->bank_read_cmd, > >> + &curr_bank, 1); > >> + if (ret) { > >> + debug("SF: fail to read bank addr register\n"); > >> + return ret; > >> + } > >> } > >> +bar_end: > >> nor->bank_curr = curr_bank; > >> > >> return 0; > >> @@ -540,7 +599,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, rem; > >> + u32 addr, len, rem, offset; > >> int ret; > >> > >> dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, > >> @@ -554,14 +613,37 @@ static int spi_nor_erase(struct mtd_info *mtd, > struct erase_info *instr) > >> len = instr->len; > >> > >> while (len) { > >> + write_enable(nor); > >> + offset = addr; > >> + if (nor->isparallel) { > >> + offset /= 2; > >> + nor->spi->flags |= SPI_XFER_STRIPE; > >> + } > >> + > >> + if (nor->isstacked) { > >> + if (offset >= (mtd->size / 2)) { > >> + offset = offset - (mtd->size / 2); > >> + nor->spi->flags |= SPI_MASTER_U_PAGE; > >> + } else { > >> + nor->spi->flags &= ~SPI_MASTER_U_PAGE; > >> + } > >> + } > >> + if (nor->addr_width == 3) { > >> #ifdef CONFIG_SPI_FLASH_BAR > >> - ret = write_bar(nor, addr); > >> - if (ret < 0) > >> - return ret; > >> + /* Update Extended Address Register */ > >> + ret = write_bar(nor, offset); > >> + if (ret) > >> + goto erase_err; > >> #endif > >> + } > >> + > >> + ret = spi_nor_wait_till_ready(nor); > >> + if (ret) > >> + goto erase_err; > >> + > >> write_enable(nor); > >> > >> - ret = spi_nor_erase_sector(nor, addr); > >> + ret = spi_nor_erase_sector(nor, offset); > >> if (ret) > >> goto erase_err; > >> > >> @@ -861,12 +943,33 @@ static int stm_unlock(struct spi_nor *nor, > >> loff_t ofs, uint64_t len) static int stm_is_locked(struct spi_nor > >> *nor, loff_t ofs, uint64_t len) { > >> int status; > >> + u8 sr; > >> + int status_up; > >> + u8 sr_up; > >> + > >> + if (nor->isparallel) { > >> + nor->spi->flags |= SPI_XFER_LOWER; > >> + sr = read_sr(nor); > >> + if (sr < 0) > >> + return 0; > >> + status = stm_is_locked_sr(nor, ofs, len, sr); > >> + > >> + nor->spi->flags |= SPI_XFER_UPPER; > >> + sr_up = read_sr(nor); > >> + if (sr_up < 0) > >> + return sr_up; > >> + status_up = stm_is_locked_sr(nor, ofs, len, sr_up); > >> + status = status && status_up; > >> + > >> + return status; > >> + } else { > >> > >> status = read_sr(nor); > >> if (status < 0) > >> return status; > >> > >> return stm_is_locked_sr(nor, ofs, len, status); > >> + } > >> } > >> #endif /* CONFIG_SPI_FLASH_STMICRO */ > >> > >> @@ -876,6 +979,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; > >> > >> + if (nor->isparallel) > >> + nor->spi->flags |= SPI_XFER_LOWER; > >> + > >> 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); > @@ -900,28 > >> +1006,98 @@ 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; > >> + u32 stack_shift = 0; > >> + u32 read_len = 0; > >> + u32 rem_bank_len = 0; > >> + u8 bank; > >> + u8 is_ofst_odd = 0; > >> + u8 cur_bank; > >> + u8 nxt_bank; > >> + u32 bank_size; > >> + > >> +#define OFFSET_16_MB 0x1000000 > >> > >> dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len); > >> > >> + if ((nor->isparallel) && (offset & 1)) { > >> + /* We can hit this case when we use file system like ubifs */ > >> + from = (loff_t)(from - 1); > >> + len = (size_t)(len + 1); > >> + is_ofst_odd = 1; > >> + } > >> + > >> while (len) { > >> - loff_t addr = from; > >> - size_t read_len = len; > >> + if (nor->addr_width == 3) { > >> + bank = (u32)from / (OFFSET_16_MB << nor->shift); > >> + rem_bank_len = ((OFFSET_16_MB << nor->shift) * > >> + (bank + 1)) - from; > >> + } > >> + offset = from; > >> > >> -#ifdef CONFIG_SPI_FLASH_BAR > >> - u32 remain_len; > >> + if (nor->isparallel) { > >> + offset /= 2; > >> + nor->spi->flags = SPI_XFER_STRIPE; > >> + } > >> > >> - ret = write_bar(nor, addr); > >> - if (ret < 0) > >> - return log_ret(ret); > >> - remain_len = (SZ_16M * (nor->bank_curr + 1)) - addr; > >> + if (nor->isstacked) { > >> + stack_shift = 1; > >> + if (offset >= (mtd->size / 2)) { > >> + offset = offset - (mtd->size / 2); > >> + nor->spi->flags |= SPI_MASTER_U_PAGE; > >> + } else { > >> + nor->spi->flags &= ~SPI_MASTER_U_PAGE; > >> + } > >> + } > >> > >> - if (len < remain_len) > >> - read_len = len; > >> - else > >> - read_len = remain_len; > >> + if (nor->addr_width == 4) { > >> + /* > >> + * Some flash devices like N25Q512 have multiple dies > >> + * in it. Read operation in these devices is bounded > >> + * by its die segment. In a continuous read, across > >> + * multiple dies, when the last byte of the selected > >> + * die segment is read, the next byte read is the > >> + * first byte of the same die segment. This is Die > >> + * cross over issue. So to handle this issue, split > >> + * a read transaction, that spans across multiple > >> + * banks, into one read per bank. Bank size is 16MB > >> + * for single and dual stacked mode and 32MB for > dual > >> + * parallel mode. > >> + */ > >> + if (nor->spi && nor->spi->multi_die) { > > Who sets nor->spi->multi_die? > > I have stopped reviewing at this point... This series is incomplete and > difficult > to review. > Please split this into two patch, one adding support for dual parallel flash > and > one adding dual stacked. Also, include all dependent controller changes and > other changes as separate patches. > > BTW Is there a datasheet for dual flashes or an appnote? > > Regards > Vignesh > > >> + bank_size = (OFFSET_16_MB << nor->shift); > >> + cur_bank = offset / bank_size; > >> + nxt_bank = (offset + len) / bank_size; > >> + if (cur_bank != nxt_bank) > >> + rem_bank_len = (bank_size * > >> + (cur_bank + 1)) - > >> + offset; > >> + else > >> + rem_bank_len = (mtd->size >> > >> + stack_shift) - > >> + (offset << nor->shift); > >> + } else { > >> + rem_bank_len = (mtd->size >> stack_shift) - > >> + (offset << nor->shift); > >> + } > >> + } > >> + > >> + if (nor->addr_width == 3) { > >> +#ifdef CONFIG_SPI_FLASH_BAR > >> + write_bar(nor, offset); > >> #endif > >> + } > >> + > >> + if (len < rem_bank_len) > >> + read_len = len; > >> + else > >> + read_len = rem_bank_len; > >> > >> - ret = nor->read(nor, addr, read_len, buf); > >> + ret = spi_nor_wait_till_ready(nor); > >> + if (ret) > >> + goto read_err; > >> + > >> + ret = nor->read(nor, offset, read_len, buf); > >> if (ret == 0) { > >> /* We shouldn't see 0-length reads */ > >> ret = -EIO; > >> @@ -930,7 +1106,12 @@ static int spi_nor_read(struct mtd_info *mtd, > loff_t from, size_t len, > >> if (ret < 0) > >> goto read_err; > >> > >> - *retlen += ret; > >> + if (is_ofst_odd == 1) { > >> + memcpy(buf, (buf + 1), (len - 1)); > >> + *retlen += (ret - 1); > >> + } else { > >> + *retlen += ret; > >> + } > >> buf += ret; > >> from += ret; > >> len -= ret; > >> @@ -1223,12 +1404,41 @@ 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; > >> + ssize_t written; > >> + loff_t addr; > >> + u8 bank = 0; > >> + u32 offset, stack_shift = 0; > >> + u32 rem_bank_len = 0; > >> > >> dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); > >> > >> + /* > >> + * Cannot write to odd offset in parallel mode, > >> + * so write 2 bytes first > >> + */ > >> + if ((nor->isparallel) && (to & 1)) { > >> + > >> + u8 two[2] = {0xff, buf[0]}; > >> + size_t local_retlen; > >> + > >> + ret = spi_nor_write(mtd, to & ~1, 2, &local_retlen, two); > >> + if (ret < 0) > >> + return ret; > >> + > >> + *retlen += 1; /* We've written only one actual byte */ > >> + ++buf; > >> + --len; > >> + ++to; > >> + } > >> + > >> for (i = 0; i < len; ) { > >> - ssize_t written; > >> - loff_t addr = to + i; > >> + addr = to + i; > >> + > >> + if (nor->addr_width == 3) { > >> + bank = (u32)to / (OFFSET_16_MB << nor->shift); > >> + rem_bank_len = ((OFFSET_16_MB << nor->shift) * > >> + (bank + 1)) - to; > >> + } > >> > >> /* > >> * If page_size is a power of two, the offset can be quickly > @@ > >> -1245,17 +1455,51 @@ static int spi_nor_write(struct mtd_info *mtd, > >> loff_t to, size_t len, > >> > >> page_offset = do_div(aux, nor->page_size); > >> } > >> - /* 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->isparallel) { > >> + offset /= 2; > >> + nor->spi->flags |= SPI_XFER_STRIPE; > >> + } > >> + > >> + if (nor->isstacked) { > >> + stack_shift = 1; > >> + if (offset >= (mtd->size / 2)) { > >> + offset = offset - (mtd->size / 2); > >> + nor->spi->flags |= SPI_MASTER_U_PAGE; > >> + } else { > >> + nor->spi->flags &= ~SPI_MASTER_U_PAGE; > >> + } > >> + } > >> + > >> + /* Die cross over issue is not handled */ > >> + if (nor->addr_width == 4) > >> + rem_bank_len = (mtd->size >> stack_shift) - offset; > >> + if (nor->addr_width == 3) { > >> #ifdef CONFIG_SPI_FLASH_BAR > >> - ret = write_bar(nor, addr); > >> - if (ret < 0) > >> - return ret; > >> + write_bar(nor, offset); > >> #endif > >> + } > >> + if (nor->isstacked) { > >> + if (len <= rem_bank_len) { > >> + page_remain = min_t(size_t, nor->page_size > - > >> + page_offset, len - i); > >> + } else { > >> + /* size of data remaining on the first page */ > >> + page_remain = rem_bank_len; > >> + } > >> + } else { > >> + page_remain = min_t(size_t, nor->page_size - > >> + page_offset, len - i); > >> + } > >> + > >> + ret = spi_nor_wait_till_ready(nor); > >> + if (ret) > >> + goto write_err; > >> + > >> + > >> write_enable(nor); > >> - ret = nor->write(nor, addr, page_remain, buf + i); > >> + ret = nor->write(nor, offset, page_remain, buf + i); > >> if (ret < 0) > >> goto write_err; > >> written = ret; > >> @@ -1265,6 +1509,13 @@ static int spi_nor_write(struct mtd_info *mtd, > loff_t to, size_t len, > >> goto write_err; > >> *retlen += written; > >> i += written; > >> + if (written != page_remain) { > >> + dev_err(nor->dev, > >> + "While writing %zu bytes written %zd > bytes\n", > >> + page_remain, written); > >> + ret = -EIO; > >> + goto write_err; > >> + } > >> } > >> > >> write_err: > >> @@ -2094,6 +2345,12 @@ static int spi_nor_init_params(struct spi_nor > *nor, > >> params->size = info->sector_size * info->n_sectors; > >> params->page_size = info->page_size; > >> > >> + if (nor->isparallel) > >> + params->page_size <<= nor->shift; > >> + > >> + if (nor->isparallel || nor->isstacked) > >> + params->size <<= nor->shift; > >> + > >> /* (Fast) Read settings. */ > >> params->hwcaps.mask |= SNOR_HWCAPS_READ; > >> spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ], > >> @@ -2280,24 +2537,27 @@ static int spi_nor_select_erase(struct spi_nor > *nor, > >> const struct flash_info *info) > >> { > >> struct mtd_info *mtd = &nor->mtd; > >> + bool shift = 0; > >> > >> /* Do nothing if already configured from SFDP. */ > >> if (mtd->erasesize) > >> return 0; > >> > >> + if (nor->isparallel) > >> + shift = 1; > >> #ifdef CONFIG_SPI_FLASH_USE_4K_SECTORS > >> /* prefer "small sector" erase if possible */ > >> if (info->flags & SECT_4K) { > >> nor->erase_opcode = SPINOR_OP_BE_4K; > >> - mtd->erasesize = 4096; > >> + mtd->erasesize = 4096 << shift; > >> } else if (info->flags & SECT_4K_PMC) { > >> nor->erase_opcode = SPINOR_OP_BE_4K_PMC; > >> - mtd->erasesize = 4096; > >> + mtd->erasesize = 4096 << shift; > >> } else > >> #endif > >> { > >> nor->erase_opcode = SPINOR_OP_SE; > >> - mtd->erasesize = info->sector_size; > >> + mtd->erasesize = info->sector_size << shift; > >> } > >> return 0; > >> } > >> @@ -2442,9 +2702,14 @@ int spi_nor_scan(struct spi_nor *nor) > >> hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2; > >> } > >> > >> - info = spi_nor_read_id(nor); > >> + nor->isparallel = (spi->option == SF_DUAL_PARALLEL_FLASH) ? 1 : 0; > >> + nor->isstacked = (spi->option == SF_DUAL_STACKED_FLASH) ? 1 : 0; > >> + nor->shift = (nor->isparallel || nor->isstacked) ? 1 : 0; > >> + > >> + info = (struct flash_info *)spi_nor_read_id(nor); > >> if (IS_ERR_OR_NULL(info)) > >> return -ENOENT; > >> + > >> /* Parse the Serial Flash Discoverable Parameters table. */ > >> ret = spi_nor_init_params(nor, info, ¶ms); > >> if (ret) > >> diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index > >> 7788ab9953..2471132f41 100644 > >> --- a/drivers/spi/spi-mem.c > >> +++ b/drivers/spi/spi-mem.c > >> @@ -202,7 +202,7 @@ int spi_mem_exec_op(struct spi_slave *slave, > const struct spi_mem_op *op) > >> const u8 *tx_buf = NULL; > >> u8 *rx_buf = NULL; > >> int op_len; > >> - u32 flag; > >> + u32 flag = 0; > >> int ret; > >> int i; > >> > >> @@ -362,24 +362,35 @@ int spi_mem_exec_op(struct spi_slave *slave, > const struct spi_mem_op *op) > >> if (op->dummy.nbytes) > >> memset(op_buf + pos, 0xff, op->dummy.nbytes); > >> > >> + if (slave->flags & SPI_XFER_U_PAGE) > >> + flag |= SPI_XFER_U_PAGE; > >> + > >> + if (slave->flags & SPI_XFER_LOWER) > >> + flag |= SPI_XFER_LOWER; > >> + if (slave->flags & SPI_XFER_UPPER) > >> + flag |= SPI_XFER_UPPER; > >> + if (slave->flags & SPI_XFER_STRIPE) > >> + flag |= SPI_XFER_STRIPE; > >> + > >> /* 1st transfer: opcode + address + dummy cycles */ > >> - flag = SPI_XFER_BEGIN; > >> /* Make sure to set END bit if no tx or rx data messages follow */ > >> if (!tx_buf && !rx_buf) > >> flag |= SPI_XFER_END; > >> > >> - ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag); > >> + ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag | > >> +SPI_XFER_BEGIN); > >> if (ret) > >> return ret; > >> > >> /* 2nd transfer: rx or tx data path */ > >> if (tx_buf || rx_buf) { > >> ret = spi_xfer(slave, op->data.nbytes * 8, tx_buf, > >> - rx_buf, SPI_XFER_END); > >> + rx_buf, flag | SPI_XFER_END); > >> if (ret) > >> return ret; > >> } > >> > >> + slave->flags &= ~SPI_XFER_MASK; > >> + > >> spi_release_bus(slave); > >> > >> for (i = 0; i < pos; i++) > >> diff --git a/include/linux/mtd/spi-nor.h > >> b/include/linux/mtd/spi-nor.h index f9964a7664..c7475e7a12 100644 > >> --- a/include/linux/mtd/spi-nor.h > >> +++ b/include/linux/mtd/spi-nor.h > >> @@ -309,11 +309,15 @@ struct spi_nor { > >> u8 bank_read_cmd; > >> u8 bank_write_cmd; > >> u8 bank_curr; > >> + u8 upage_prev; > >> #endif > >> enum spi_nor_protocol read_proto; > >> enum spi_nor_protocol write_proto; > >> enum spi_nor_protocol reg_proto; > >> bool sst_write_second; > >> + bool shift; > >> + bool isparallel; > >> + bool isstacked; > >> u32 flags; > >> u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; > >> > >> diff --git a/include/spi.h b/include/spi.h index > >> 6fbb4336ce..69f69e590c 100644 > >> --- a/include/spi.h > >> +++ b/include/spi.h > >> @@ -31,6 +31,12 @@ > >> #define SPI_RX_DUAL BIT(12) /* receive with 2 > wires */ > >> #define SPI_RX_QUAD BIT(13) /* receive with 4 > wires */ > >> > >> +/* SPI transfer flags */ > >> +#define SPI_XFER_STRIPE (1 << 6) > >> +#define SPI_XFER_MASK (3 << 8) > >> +#define SPI_XFER_LOWER (1 << 8) > >> +#define SPI_XFER_UPPER (2 << 8) > >> + > >> /* Header byte that marks the start of the message */ > >> #define SPI_PREAMBLE_END_BYTE 0xec > >> > >> @@ -108,13 +114,16 @@ struct spi_slave { > >> unsigned int max_read_size; > >> unsigned int max_write_size; > >> void *memory_map; > >> + u8 option; > >> > >> u8 flags; > >> + bool multi_die; /* flash with multiple dies*/ > >> #define SPI_XFER_BEGIN BIT(0) /* Assert CS before transfer > */ > >> #define SPI_XFER_END BIT(1) /* Deassert CS after transfer > */ > >> #define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) > >> #define SPI_XFER_MMAP BIT(2) /* Memory Mapped start */ > >> #define SPI_XFER_MMAP_END BIT(3) /* Memory Mapped End */ > >> +#define SPI_XFER_U_PAGE BIT(4) > >> }; > >> > >> /** > >> > > > > Jagan: any comment? > > > > M > > > > -- > Regards > Vignesh