
HI Jagan, Vignesh,
Any comments please.
Thanks, Ashok
-----Original Message----- From: Michal Simek michal.simek@xilinx.com Sent: Tuesday, November 26, 2019 12:40 PM To: Ashok Reddy Soma ashokred@xilinx.com; u-boot@lists.denx.de; jagan@amarulasolutions.com; vigneshr@ti.com Cc: Michal Simek michals@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@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);
pr_debug("error %d reading SR\n", (int)ret); return ret;if (ret < 0) {
}}
- 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);
pr_debug("error %d reading FSR\n", ret); return ret;if (ret < 0) {
}}
- 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);
if (ret) goto erase_err;ret = spi_nor_erase_sector(nor, offset);
@@ -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;
if (ret == 0) { /* We shouldn't see 0-length reads */ ret = -EIO;ret = nor->read(nor, offset, read_len, buf);
@@ -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);
if (ret < 0) goto write_err; written = ret;ret = nor->write(nor, offset, page_remain, buf + i);
@@ -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;
} else if (info->flags & SECT_4K_PMC) { nor->erase_opcode = SPINOR_OP_BE_4K_PMC;mtd->erasesize = 4096 << shift;
mtd->erasesize = 4096;
} elsemtd->erasesize = 4096 << shift;
#endif { nor->erase_opcode = SPINOR_OP_SE;
mtd->erasesize = info->sector_size;
} return 0;mtd->erasesize = info->sector_size << shift;
} @@ -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