summaryrefslogtreecommitdiff
path: root/drivers/mtd/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/spi')
-rw-r--r--drivers/mtd/spi/Kconfig2
-rw-r--r--drivers/mtd/spi/Makefile10
-rw-r--r--drivers/mtd/spi/sandbox.c2
-rw-r--r--drivers/mtd/spi/sf-uclass.c2
-rw-r--r--drivers/mtd/spi/sf_dataflash.c2
-rw-r--r--drivers/mtd/spi/spi-nor-core.c483
-rw-r--r--drivers/mtd/spi/spi-nor-ids.c29
7 files changed, 444 insertions, 86 deletions
diff --git a/drivers/mtd/spi/Kconfig b/drivers/mtd/spi/Kconfig
index bedc4e970e4..63b0fd899fd 100644
--- a/drivers/mtd/spi/Kconfig
+++ b/drivers/mtd/spi/Kconfig
@@ -2,7 +2,7 @@ menu "SPI Flash Support"
config DM_SPI_FLASH
bool "Enable Driver Model for SPI flash"
- depends on DM && DM_SPI
+ depends on DM_SPI
imply SPI_FLASH
help
Enable driver model for SPI flash. This SPI flash interface
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile
index 409395382f5..44e67cd913a 100644
--- a/drivers/mtd/spi/Makefile
+++ b/drivers/mtd/spi/Makefile
@@ -3,12 +3,12 @@
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-obj-$(CONFIG_$(SPL_TPL_)DM_SPI_FLASH) += sf-uclass.o
+obj-$(CONFIG_$(PHASE_)DM_SPI_FLASH) += sf-uclass.o
spi-nor-y := sf_probe.o spi-nor-ids.o
-ifdef CONFIG_SPL_BUILD
+ifdef CONFIG_XPL_BUILD
obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o
-ifeq ($(CONFIG_$(SPL_TPL_)SPI_FLASH_TINY),y)
+ifeq ($(CONFIG_$(PHASE_)SPI_FLASH_TINY),y)
spi-nor-y += spi-nor-tiny.o
else
spi-nor-y += spi-nor-core.o
@@ -19,6 +19,6 @@ endif
obj-$(CONFIG_SPI_FLASH) += spi-nor.o
obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
-obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_MTD) += sf_mtd.o
+obj-$(CONFIG_$(PHASE_)SPI_FLASH_MTD) += sf_mtd.o
obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
-obj-$(CONFIG_$(SPL_TPL_)BOOTDEV_SPI_FLASH) += sf_bootdev.o
+obj-$(CONFIG_$(PHASE_)BOOTDEV_SPI_FLASH) += sf_bootdev.o
diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c
index 2d5a16bf6a2..e5ebc3479fb 100644
--- a/drivers/mtd/spi/sandbox.c
+++ b/drivers/mtd/spi/sandbox.c
@@ -138,7 +138,7 @@ static int sandbox_sf_probe(struct udevice *dev)
return ret;
}
slave_plat = dev_get_parent_plat(dev);
- cs = slave_plat->cs;
+ cs = slave_plat->cs[0];
debug("found at cs %d\n", cs);
if (!pdata->filename) {
diff --git a/drivers/mtd/spi/sf-uclass.c b/drivers/mtd/spi/sf-uclass.c
index a4d15bd64aa..102a9236826 100644
--- a/drivers/mtd/spi/sf-uclass.c
+++ b/drivers/mtd/spi/sf-uclass.c
@@ -54,7 +54,7 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
struct udevice *bus;
char *str;
-#if defined(CONFIG_SPL_BUILD) && CONFIG_IS_ENABLED(USE_TINY_PRINTF)
+#if defined(CONFIG_XPL_BUILD) && CONFIG_IS_ENABLED(USE_TINY_PRINTF)
str = "spi_flash";
#else
char name[30];
diff --git a/drivers/mtd/spi/sf_dataflash.c b/drivers/mtd/spi/sf_dataflash.c
index 6db24189c8e..438eb3698d5 100644
--- a/drivers/mtd/spi/sf_dataflash.c
+++ b/drivers/mtd/spi/sf_dataflash.c
@@ -438,7 +438,7 @@ static int add_dataflash(struct udevice *dev, char *name, int nr_pages,
spi_flash->size = nr_pages * pagesize;
spi_flash->erase_size = pagesize;
-#ifndef CONFIG_SPL_BUILD
+#ifndef CONFIG_XPL_BUILD
printf("SPI DataFlash: Detected %s with page size ", spi_flash->name);
print_size(spi_flash->page_size, ", erase size ");
print_size(spi_flash->erase_size, ", total ");
diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index 8f7a77e7169..6f5395c7b9b 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -467,8 +467,9 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
}
/*
- * Read the status register, returning its value in the location
- * Return the status register value.
+ * Return the status register value. If the chip is parallel, then the
+ * read will be striped, so we should read 2 bytes to get the sr
+ * register value from both of the parallel chips.
* Returns negative if error occurred.
*/
static int read_sr(struct spi_nor *nor)
@@ -500,18 +501,29 @@ static int read_sr(struct spi_nor *nor)
if (spi_nor_protocol_is_dtr(nor->reg_proto))
op.data.nbytes = 2;
- ret = spi_nor_read_write_reg(nor, &op, val);
- if (ret < 0) {
- pr_debug("error %d reading SR\n", (int)ret);
- return ret;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ op.data.nbytes = 2;
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
+ val[0] |= val[1];
+ } else {
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
}
- return *val;
+ return val[0];
}
/*
- * Read the flag status register, returning its value in the location
- * Return the status register value.
+ * Return the flag status register value. If the chip is parallel, then
+ * the read will be striped, so we should read 2 bytes to get the fsr
+ * register value from both of the parallel chips.
* Returns negative if error occurred.
*/
static int read_fsr(struct spi_nor *nor)
@@ -543,13 +555,23 @@ static int read_fsr(struct spi_nor *nor)
if (spi_nor_protocol_is_dtr(nor->reg_proto))
op.data.nbytes = 2;
- ret = spi_nor_read_write_reg(nor, &op, val);
- if (ret < 0) {
- pr_debug("error %d reading FSR\n", ret);
- return ret;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ op.data.nbytes = 2;
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading SR\n", (int)ret);
+ return ret;
+ }
+ val[0] &= val[1];
+ } else {
+ ret = spi_nor_read_write_reg(nor, &op, &val[0]);
+ if (ret < 0) {
+ pr_debug("error %d reading FSR\n", ret);
+ return ret;
+ }
}
- return *val;
+ return val[0];
}
/*
@@ -573,6 +595,24 @@ static int read_cr(struct spi_nor *nor)
}
#endif
+/**
+ * read_sr3() - Read status register 3 unique to newer Winbond flashes
+ * @nor: pointer to a 'struct spi_nor'
+ */
+static int read_sr3(struct spi_nor *nor)
+{
+ int ret;
+ u8 val;
+
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR3, &val, 1);
+ if (ret < 0) {
+ dev_dbg(nor->dev, "error %d reading SR3\n", ret);
+ return ret;
+ }
+
+ return val;
+}
+
/*
* Write status register 1 byte
* Returns negative if error occurred.
@@ -583,6 +623,17 @@ static int write_sr(struct spi_nor *nor, u8 val)
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
}
+/**
+ * write_sr3() - Write status register 3 unique to newer Winbond flashes
+ * @nor: pointer to a 'struct spi_nor'
+ * @val: value to be written into SR3
+ */
+static int write_sr3(struct spi_nor *nor, u8 val)
+{
+ nor->cmd_buf[0] = val;
+ return nor->write_reg(nor, SPINOR_OP_WRSR3, nor->cmd_buf, 1);
+}
+
/*
* Set write enable latch with Write Enable command.
* Returns negative if error occurred.
@@ -668,12 +719,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->flags & SNOR_F_HAS_PARALLEL)
+ 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:
@@ -901,12 +957,32 @@ 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;
+ struct mtd_info *mtd = &nor->mtd;
+
+ /* Wait until previous write command is finished */
+ if (spi_nor_wait_till_ready(nor))
+ return 1;
+
+ if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED) &&
+ mtd->size <= SZ_32M)
+ return 0;
+
+ if (mtd->size <= SZ_16M)
+ return 0;
- bank_sel = offset / SZ_16M;
- if (bank_sel == nor->bank_curr)
- goto bar_end;
+ offset = offset % (u32)mtd->size;
+ bank_sel = offset >> 24;
+
+ upage_curr = nor->spi->flags & SPI_XFER_U_PAGE;
+
+ if (!(nor->flags & SNOR_F_HAS_STACKED) && bank_sel == nor->bank_curr)
+ return 0;
+ else if (upage_curr == nor->upage_prev && bank_sel == nor->bank_curr)
+ return 0;
+
+ nor->upage_prev = upage_curr;
cmd = nor->bank_write_cmd;
write_enable(nor);
@@ -916,15 +992,19 @@ static int write_bar(struct spi_nor *nor, u32 offset)
return ret;
}
-bar_end:
nor->bank_curr = bank_sel;
- return nor->bank_curr;
+
+ return write_disable(nor);
}
static int read_bar(struct spi_nor *nor, const struct flash_info *info)
{
u8 curr_bank = 0;
int ret;
+ struct mtd_info *mtd = &nor->mtd;
+
+ if (mtd->size <= SZ_16M)
+ return 0;
switch (JEDEC_MFR(info)) {
case SNOR_MFR_SPANSION:
@@ -936,15 +1016,30 @@ static int read_bar(struct spi_nor *nor, const struct flash_info *info)
nor->bank_write_cmd = SPINOR_OP_WREAR;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_XFER_LOWER;
+
ret = nor->read_reg(nor, nor->bank_read_cmd,
- &curr_bank, 1);
+ &curr_bank, 1);
if (ret) {
debug("SF: fail to read bank addr register\n");
return ret;
}
nor->bank_curr = curr_bank;
- return 0;
+ // Make sure both chips use the same BAR
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ write_enable(nor);
+ ret = nor->write_reg(nor, nor->bank_write_cmd, &curr_bank, 1);
+ if (ret)
+ return ret;
+
+ ret = write_disable(nor);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
}
#endif
@@ -1008,8 +1103,8 @@ 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, offset, max_size;
bool addr_known = false;
- u32 addr, len, rem, max_size;
int ret, err;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
@@ -1030,11 +1125,23 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) {
schedule();
- if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc()) {
+ if (!IS_ENABLED(CONFIG_XPL_BUILD) && ctrlc()) {
addr_known = false;
ret = -EINTR;
goto erase_err;
}
+ offset = addr;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
+
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
#ifdef CONFIG_SPI_FLASH_BAR
ret = write_bar(nor, addr);
if (ret < 0)
@@ -1446,6 +1553,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->flags & SNOR_F_HAS_PARALLEL)
+ 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);
@@ -1470,28 +1580,67 @@ 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;
+ loff_t offset = from;
+ u32 read_len = 0;
+ u32 rem_bank_len = 0;
+ u8 bank;
+ bool is_ofst_odd = false;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (offset & 1)) {
+ /* We can hit this case when we use file system like ubifs */
+ from--;
+ len++;
+ is_ofst_odd = true;
+ }
+
while (len) {
- loff_t addr = from;
- size_t read_len = len;
+ if (nor->addr_width == 3) {
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ bank = (u32)from / (SZ_16M << 0x01);
+ rem_bank_len = ((SZ_16M << 0x01) *
+ (bank + 1)) - from;
+ } else {
+ bank = (u32)from / SZ_16M;
+ rem_bank_len = (SZ_16M * (bank + 1)) - from;
+ }
+ }
+ offset = from;
-#ifdef CONFIG_SPI_FLASH_BAR
- u32 remain_len;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
- ret = write_bar(nor, addr);
- if (ret < 0)
- return log_ret(ret);
- remain_len = (SZ_16M * (nor->bank_curr + 1)) - addr;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
+
+ if (nor->addr_width == 3) {
+#ifdef CONFIG_SPI_FLASH_BAR
+ ret = write_bar(nor, offset);
+ if (ret < 0)
+ return log_ret(ret);
+#endif
+ }
- if (len < remain_len)
+ if (len < rem_bank_len)
read_len = len;
else
- read_len = remain_len;
-#endif
+ read_len = rem_bank_len;
+
+ if (read_len == 0)
+ return -EIO;
- 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;
@@ -1500,8 +1649,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (ret < 0)
goto read_err;
- *retlen += ret;
- buf += ret;
+ if (is_ofst_odd == true) {
+ memmove(buf, (buf + 1), (len - 1));
+ *retlen += (ret - 1);
+ buf += ret - 1;
+ is_ofst_odd = false;
+ } else {
+ *retlen += ret;
+ buf += ret;
+ }
from += ret;
len -= ret;
}
@@ -1796,6 +1952,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;
#ifdef CONFIG_SPI_FLASH_SST
/* sst nor chips use AAI word program */
@@ -1805,6 +1962,27 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+ if (!len)
+ return 0;
+
+ /*
+ * Cannot write to odd offset in parallel mode,
+ * so write 2 bytes first
+ */
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (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;
@@ -1822,18 +2000,35 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
page_offset = do_div(aux, nor->page_size);
}
+ offset = (to + i);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ offset /= 2;
+
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ if (offset >= (mtd->size / 2)) {
+ offset = offset - (mtd->size / 2);
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ } else {
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+ }
+
+ if (nor->addr_width == 3) {
+#ifdef CONFIG_SPI_FLASH_BAR
+ ret = write_bar(nor, offset);
+ if (ret < 0)
+ return ret;
+#endif
+ }
/* the size of data remaining on the first page */
page_remain = min_t(size_t,
nor->page_size - page_offset, len - i);
-#ifdef CONFIG_SPI_FLASH_BAR
- ret = write_bar(nor, addr);
- if (ret < 0)
- return ret;
-#endif
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto write_err;
write_enable(nor);
-
/*
* On DTR capable flashes like Micron Xcella the writes cannot
* start or end at an odd address in DTR mode. So we need to
@@ -1841,7 +2036,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
* address and end address are even.
*/
if (spi_nor_protocol_is_dtr(nor->write_proto) &&
- ((addr | page_remain) & 1)) {
+ ((offset | page_remain) & 1)) {
u_char *tmp;
size_t extra_bytes = 0;
@@ -1852,10 +2047,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
}
/* Prepend a 0xff byte if the start address is odd. */
- if (addr & 1) {
+ if (offset & 1) {
tmp[0] = 0xff;
memcpy(tmp + 1, buf + i, page_remain);
- addr--;
+ offset--;
page_remain++;
extra_bytes++;
} else {
@@ -1863,13 +2058,13 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
}
/* Append a 0xff byte if the end address is odd. */
- if ((addr + page_remain) & 1) {
+ if ((offset + page_remain) & 1) {
tmp[page_remain + extra_bytes] = 0xff;
extra_bytes++;
page_remain++;
}
- ret = nor->write(nor, addr, page_remain, tmp);
+ ret = nor->write(nor, offset, page_remain, tmp);
kfree(tmp);
@@ -1882,7 +2077,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
*/
written = ret - extra_bytes;
} else {
- 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;
@@ -1891,6 +2086,11 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
ret = spi_nor_wait_till_ready(nor);
if (ret)
goto write_err;
+
+ ret = write_disable(nor);
+ if (ret)
+ goto write_err;
+
*retlen += written;
i += written;
}
@@ -1931,6 +2131,10 @@ static int macronix_quad_enable(struct spi_nor *nor)
if (ret)
return ret;
+ ret = write_disable(nor);
+ if (ret)
+ return ret;
+
ret = read_sr(nor);
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
dev_err(nor->dev, "Macronix Quad bit not set\n");
@@ -1992,7 +2196,7 @@ static int spansion_quad_enable_volatile(struct spi_nor *nor, u32 addr_base,
return -EINVAL;
}
- return 0;
+ return write_disable(nor);
}
#endif
@@ -2168,6 +2372,10 @@ static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
nor->read_dummy = 8;
while (len) {
+ /* Both chips are identical, so should be the SFDP data */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_XFER_LOWER;
+
ret = nor->read(nor, addr, len, (u8 *)buf);
if (!ret || ret > len) {
ret = -EIO;
@@ -2862,6 +3070,13 @@ static int spi_nor_init_params(struct spi_nor *nor,
const struct flash_info *info,
struct spi_nor_flash_parameter *params)
{
+#if CONFIG_IS_ENABLED(DM_SPI) && CONFIG_IS_ENABLED(SPI_ADVANCE)
+ struct udevice *dev = nor->spi->dev;
+ u64 flash_size[SNOR_FLASH_CNT_MAX] = {0};
+ u32 idx = 0, i = 0;
+ int rc;
+#endif
+
/* Set legacy flash parameters as default. */
memset(params, 0, sizeof(*params));
@@ -2979,7 +3194,62 @@ static int spi_nor_init_params(struct spi_nor *nor,
memcpy(params, &sfdp_params, sizeof(*params));
}
}
+#if CONFIG_IS_ENABLED(DM_SPI) && CONFIG_IS_ENABLED(SPI_ADVANCE)
+ /*
+ * The flashes that are connected in stacked mode should be of same make.
+ * Except the flash size all other properties are identical for all the
+ * flashes connected in stacked mode.
+ * The flashes that are connected in parallel mode should be identical.
+ */
+ while (i < SNOR_FLASH_CNT_MAX) {
+ rc = ofnode_read_u64_index(dev_ofnode(dev), "stacked-memories",
+ idx, &flash_size[i]);
+ if (rc == -EINVAL) {
+ break;
+ } else if (rc == -EOVERFLOW) {
+ idx++;
+ } else {
+ idx++;
+ i++;
+ if (!(nor->flags & SNOR_F_HAS_STACKED))
+ nor->flags |= SNOR_F_HAS_STACKED;
+ if (!(nor->spi->flags & SPI_XFER_STACKED))
+ nor->spi->flags |= SPI_XFER_STACKED;
+ }
+ }
+
+ i = 0;
+ idx = 0;
+ while (i < SNOR_FLASH_CNT_MAX) {
+ rc = ofnode_read_u64_index(dev_ofnode(dev), "parallel-memories",
+ idx, &flash_size[i]);
+ if (rc == -EINVAL) {
+ break;
+ } else if (rc == -EOVERFLOW) {
+ idx++;
+ } else {
+ idx++;
+ i++;
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL))
+ nor->flags |= SNOR_F_HAS_PARALLEL;
+ }
+ }
+ if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) {
+ params->size = 0;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++)
+ params->size += flash_size[idx];
+ }
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ nor->mtd.erasesize <<= 1;
+ params->page_size <<= 1;
+ }
+#endif
spi_nor_post_sfdp_fixups(nor, params);
return 0;
@@ -3294,16 +3564,54 @@ static int spi_nor_select_erase(struct spi_nor *nor,
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
nor->erase_opcode = SPINOR_OP_BE_4K;
- mtd->erasesize = 4096;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
} else if (info->flags & SECT_4K_PMC) {
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
- mtd->erasesize = 4096;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
} else
#endif
{
nor->erase_opcode = SPINOR_OP_SE;
- mtd->erasesize = info->sector_size;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = info->sector_size * 2;
+ else
+ mtd->erasesize = info->sector_size;
+ }
+
+ if ((JEDEC_MFR(info) == SNOR_MFR_SST) && info->flags & SECT_4K) {
+ nor->erase_opcode = SPINOR_OP_BE_4K;
+ /*
+ * In parallel-memories the erase operation is
+ * performed on both the flashes simultaneously
+ * so, double the erasesize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->erasesize = 4096 * 2;
+ else
+ mtd->erasesize = 4096;
}
+
return 0;
}
@@ -3376,8 +3684,10 @@ static int s25fs_s_quad_enable(struct spi_nor *nor)
static int s25fs_s_erase_non_uniform(struct spi_nor *nor, loff_t addr)
{
+ u8 opcode = nor->addr_width == 4 ? SPINOR_OP_BE_4K_4B : SPINOR_OP_BE_4K;
+
/* Support 8 x 4KB sectors at bottom */
- return spansion_erase_non_uniform(nor, addr, SPINOR_OP_BE_4K_4B, 0, SZ_32K);
+ return spansion_erase_non_uniform(nor, addr, opcode, 0, SZ_32K);
}
static int s25fs_s_setup(struct spi_nor *nor, const struct flash_info *info,
@@ -3431,12 +3741,24 @@ static int s25fs_s_post_bfpt_fixup(struct spi_nor *nor,
static void s25fs_s_post_sfdp_fixup(struct spi_nor *nor,
struct spi_nor_flash_parameter *params)
{
- /* READ_1_1_2 is not supported */
- params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_2;
- /* READ_1_1_4 is not supported */
- params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_4;
- /* PP_1_1_4 is not supported */
- params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
+ /*
+ * The S25FS064S(8MB) supports 1-1-2 and 1-1-4 commands, but params for
+ * read ops in SFDP are wrong. The other density parts do not support
+ * 1-1-2 and 1-1-4 commands.
+ */
+ if (params->size == SZ_8M) {
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2,
+ SNOR_PROTO_1_1_2);
+ spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4,
+ SNOR_PROTO_1_1_4);
+ } else {
+ params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_2;
+ params->hwcaps.mask &= ~SNOR_HWCAPS_READ_1_1_4;
+ params->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
+ }
+
/* Use volatile register to enable quad */
params->quad_enable = s25fs_s_quad_enable;
}
@@ -3925,6 +4247,9 @@ static int spi_nor_init(struct spi_nor *nor)
{
int err;
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ nor->spi->flags |= SPI_NOR_ENABLE_MULTI_CS;
+
err = spi_nor_octal_dtr_enable(nor);
if (err) {
dev_dbg(nor->dev, "Octal DTR mode not supported\n");
@@ -3943,6 +4268,24 @@ static int spi_nor_init(struct spi_nor *nor)
write_enable(nor);
write_sr(nor, 0);
spi_nor_wait_till_ready(nor);
+
+ /*
+ * Some Winbond SPI NORs have special SR3 register which is
+ * used among other things to control whether non-standard
+ * "Individual Block/Sector Write Protection" (WPS bit)
+ * locking scheme is activated. This non-standard locking
+ * scheme is not supported by either U-Boot or Linux SPI
+ * NOR stack so make sure it is disabled, otherwise the
+ * SPI NOR may appear locked for no obvious reason.
+ */
+ if (JEDEC_MFR(nor->info) == SNOR_MFR_WINBOND) {
+ err = read_sr3(nor);
+ if (err > 0 && err & SR3_WPS) {
+ write_enable(nor);
+ write_sr3(nor, err & ~SR3_WPS);
+ write_disable(nor);
+ }
+ }
}
if (nor->quad_enable) {
@@ -4091,6 +4434,7 @@ int spi_nor_scan(struct spi_nor *nor)
struct spi_slave *spi = nor->spi;
int ret;
int cfi_mtd_nb = 0;
+ bool shift = 0;
#ifdef CONFIG_FLASH_CFI_MTD
cfi_mtd_nb = CFI_FLASH_BANKS;
@@ -4228,7 +4572,9 @@ int spi_nor_scan(struct spi_nor *nor)
nor->addr_width = 3;
}
- if (nor->addr_width == 3 && mtd->size > SZ_16M) {
+ if (nor->flags & (SNOR_F_HAS_PARALLEL | SNOR_F_HAS_STACKED))
+ shift = 1;
+ if (nor->addr_width == 3 && (mtd->size >> shift) > SZ_16M) {
#ifndef CONFIG_SPI_FLASH_BAR
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
@@ -4238,6 +4584,7 @@ int spi_nor_scan(struct spi_nor *nor)
#else
/* Configure the BAR - discover bank cmds and read current bank */
nor->addr_width = 3;
+ set_4byte(nor, info, 0);
ret = read_bar(nor, info);
if (ret < 0)
return ret;
@@ -4255,6 +4602,14 @@ int spi_nor_scan(struct spi_nor *nor)
if (ret)
return ret;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ nor->spi->flags |= SPI_XFER_U_PAGE;
+ ret = spi_nor_init(nor);
+ if (ret)
+ return ret;
+ nor->spi->flags &= ~SPI_XFER_U_PAGE;
+ }
+
nor->rdsr_dummy = params.rdsr_dummy;
nor->rdsr_addr_nbytes = params.rdsr_addr_nbytes;
nor->name = info->name;
@@ -4262,7 +4617,7 @@ int spi_nor_scan(struct spi_nor *nor)
nor->erase_size = mtd->erasesize;
nor->sector_size = mtd->erasesize;
-#ifndef CONFIG_SPL_BUILD
+#ifndef CONFIG_XPL_BUILD
printf("SF: Detected %s with page size ", nor->name);
print_size(nor->page_size, ", erase size ");
print_size(nor->erase_size, ", total ");
diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c
index 88709a52b3a..dfe92c3986e 100644
--- a/drivers/mtd/spi/spi-nor-ids.c
+++ b/drivers/mtd/spi/spi-nor-ids.c
@@ -258,7 +258,6 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("mx25u6435f", 0xc22537, 0, 64 * 1024, 128, SECT_4K) },
{ INFO("mx25l12805d", 0xc22018, 0, 64 * 1024, 256, SECT_4K) },
{ INFO("mx25u12835f", 0xc22538, 0, 64 * 1024, 256, SECT_4K) },
- { INFO("mx25u25635f", 0xc22539, 0, 64 * 1024, 512, SECT_4K) },
{ INFO("mx25u51245g", 0xc2253a, 0, 64 * 1024, 1024, SECT_4K |
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ INFO("mx25l12855e", 0xc22618, 0, 64 * 1024, 256, 0) },
@@ -339,9 +338,12 @@ const struct flash_info spi_nor_ids[] = {
*/
{ INFO("s25sl032p", 0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("s25sl064p", 0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { INFO("s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
- { INFO("s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { INFO6("s25fl256s0", 0x010219, 0x4d0080, 256 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { INFO6("s25fl256s1", 0x010219, 0x4d0180, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO6("s25fl512s", 0x010220, 0x4d0080, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { INFO6("s25fs064s", 0x010217, 0x4d0181, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { INFO6("s25fs128s", 0x012018, 0x4d0181, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+ { INFO6("s25fs256s", 0x010219, 0x4d0181, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO6("s25fs512s", 0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl512s_256k", 0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
{ INFO("s25fl512s_64k", 0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
@@ -369,7 +371,7 @@ const struct flash_info spi_nor_ids[] = {
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
USE_CLSR) },
{ INFO6("s25hl02gt", 0x342a1c, 0x0f0090, 256 * 1024, 1024,
- SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | NO_CHIP_ERASE) },
{ INFO6("s25hs512t", 0x342b1a, 0x0f0390, 256 * 1024, 256,
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
USE_CLSR) },
@@ -377,15 +379,16 @@ const struct flash_info spi_nor_ids[] = {
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES |
USE_CLSR) },
{ INFO6("s25hs02gt", 0x342b1c, 0x0f0090, 256 * 1024, 1024,
- SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+ SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | NO_CHIP_ERASE) },
{ INFO6("s25fs256t", 0x342b19, 0x0f0890, 128 * 1024, 256,
SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
#ifdef CONFIG_SPI_FLASH_S28HX_T
{ INFO("s28hl512t", 0x345a1a, 0, 256 * 1024, 256, SPI_NOR_OCTAL_DTR_READ) },
{ INFO("s28hl01gt", 0x345a1b, 0, 256 * 1024, 512, SPI_NOR_OCTAL_DTR_READ) },
+ { INFO("s28hs256t", 0x345b19, 0, 256 * 1024, 128, SPI_NOR_OCTAL_DTR_READ) },
{ INFO("s28hs512t", 0x345b1a, 0, 256 * 1024, 256, SPI_NOR_OCTAL_DTR_READ) },
{ INFO("s28hs01gt", 0x345b1b, 0, 256 * 1024, 512, SPI_NOR_OCTAL_DTR_READ) },
- { INFO("s28hs02gt", 0x345b1c, 0, 256 * 1024, 1024, SPI_NOR_OCTAL_DTR_READ) },
+ { INFO("s28hs02gt", 0x345b1c, 0, 256 * 1024, 1024, SPI_NOR_OCTAL_DTR_READ | NO_CHIP_ERASE) },
#endif
#endif
#ifdef CONFIG_SPI_FLASH_SST /* SST */
@@ -430,11 +433,6 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("w25x05", 0xef3010, 0, 64 * 1024, 1, SECT_4K) },
{ INFO("w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K) },
{ INFO("w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K) },
- {
- INFO("w25q16dw", 0xef6015, 0, 64 * 1024, 32,
- SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
- SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
- },
{ INFO("w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ INFO("w25q20cl", 0xef4012, 0, 64 * 1024, 4, SECT_4K) },
{ INFO("w25q20bw", 0xef5012, 0, 64 * 1024, 4, SECT_4K) },
@@ -442,7 +440,8 @@ const struct flash_info spi_nor_ids[] = {
{ INFO("w25q32", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{
INFO("w25q16dw", 0xef6015, 0, 64 * 1024, 32,
- SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q32dw", 0xef6016, 0, 64 * 1024, 64,
@@ -541,7 +540,11 @@ const struct flash_info spi_nor_ids[] = {
},
{ INFO("w25q80", 0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ INFO("w25q80bl", 0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- { INFO("w25q16cl", 0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ {
+ INFO("w25q16cl", 0xef4015, 0, 64 * 1024, 32,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ },
{ INFO("w25q32bv", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("w25q64cv", 0xef4017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ INFO("w25q128", 0xef4018, 0, 64 * 1024, 256,