[RFC PATCH] spi: spi-nor: Add dual flash support in spi-nor framework

Vignesh Raghavendra vigneshr at ti.com
Wed Dec 4 11:06:20 CET 2019


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.soma at 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(&params->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, &params);
>>  	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


More information about the U-Boot mailing list