[RFC PATCH] spi: spi-nor: Add dual flash support in spi-nor framework
Ashok Reddy Soma
ashokred at xilinx.com
Tue Dec 3 14:09:04 CET 2019
HI Jagan, Vignesh,
Any comments please.
Thanks,
Ashok
> -----Original Message-----
> From: Michal Simek <michal.simek at xilinx.com>
> Sent: Tuesday, November 26, 2019 12:40 PM
> To: Ashok Reddy Soma <ashokred at xilinx.com>; u-boot at lists.denx.de;
> jagan at amarulasolutions.com; vigneshr at ti.com
> Cc: Michal Simek <michals at xilinx.com>
> Subject: Re: [RFC PATCH] spi: spi-nor: Add dual flash support in spi-nor
> framework
>
> 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.
> >
> > Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma at xilinx.com>
> > ---
> > Hi,
> >
> > I have added dual parallel and dual stacked related changes to the
> > spi-nor framework. I have tested this for dual parallel configuration
> > on zynqmp platform. I am in process of testing dual stacked
> > configuration.
> >
> > Please let me know if my approach is correct. If it is okay, i will
> > split this in to meaningful patches and send.
> >
> > Thanks in advance,
> > Ashok Reddy Soma
> > ---
> > drivers/mtd/spi/sf_internal.h | 9 +
> > drivers/mtd/spi/spi-nor-core.c | 363 ++++++++++++++++++++++++++++-
> ----
> > drivers/spi/spi-mem.c | 19 +-
> > include/linux/mtd/spi-nor.h | 4 +
> > include/spi.h | 9 +
> > 5 files changed, 351 insertions(+), 53 deletions(-)
> >
> > 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
> > +
> > 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 */
> > +#define SPI_MASTER_DATA_STRIPE BIT(7) /* support data stripe */
> > +#define SPI_MASTER_BOTH_CS BIT(8) /* assert both chip selects
> */
> > +#define SPI_MASTER_U_PAGE BIT(9) /* select upper flash */
> > +
> > 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;
> > + }
> > }
> >
> > - 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;
> > +
> > 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) {
> > + 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
More information about the U-Boot
mailing list