@@ -1441,13 +1441,18 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
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;
+ struct spi_nor_flash_parameter *params;
+ u32 addr, len, offset, cur_cs_num = 0;
uint32_t rem;
int ret;
+ u64 sz;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
(long long)instr->len);
+ params = spi_nor_get_params(nor, 0);
+ sz = params->size;
+
if (spi_nor_has_uniform_erase(nor)) {
div_u64_rem(instr->len, mtd->erasesize, &rem);
if (rem)
@@ -1465,26 +1470,30 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
unsigned long timeout;
- ret = spi_nor_write_enable(nor);
- if (ret)
- goto erase_err;
+ while (cur_cs_num < SNOR_FLASH_CNT_MAX && params) {
+ nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ goto erase_err;
- ret = spi_nor_erase_chip(nor);
- if (ret)
- goto erase_err;
+ ret = spi_nor_erase_chip(nor);
+ if (ret)
+ goto erase_err;
- /*
- * Scale the timeout linearly with the size of the flash, with
- * a minimum calibrated to an old 2MB flash. We could try to
- * pull these from CFI/SFDP, but these values should be good
- * enough for now.
- */
- timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
- CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
- (unsigned long)(mtd->size / SZ_2M));
- ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
- if (ret)
- goto erase_err;
+ /*
+ * Scale the timeout linearly with the size of the flash, with
+ * a minimum calibrated to an old 2MB flash. We could try to
+ * pull these from CFI/SFDP, but these values should be good
+ * enough for now.
+ */
+ timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
+ CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
+ (unsigned long)(params->size / SZ_2M));
+ ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
+ if (ret)
+ goto erase_err;
+ cur_cs_num++;
+ }
/* REVISIT in some cases we could speed up erasing large regions
* by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K. We may have set up
@@ -1493,12 +1502,26 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
/* "sector"-at-a-time erase */
} else if (spi_nor_has_uniform_erase(nor)) {
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+
while (len) {
+ nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
ret = spi_nor_write_enable(nor);
if (ret)
goto erase_err;
- ret = spi_nor_erase_sector(nor, addr);
+ offset = addr;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ params = spi_nor_get_params(nor, cur_cs_num);
+ offset -= (sz - params->size);
+ }
+
+ ret = spi_nor_erase_sector(nor, offset);
if (ret)
goto erase_err;
@@ -1508,13 +1531,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
addr += mtd->erasesize;
len -= mtd->erasesize;
+
+ /*
+ * Flash cross over condition in stacked mode.
+ */
+ if ((nor->flags & SNOR_F_HAS_STACKED) && (addr > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
/* erase multiple sectors */
} else {
- ret = spi_nor_erase_multi_sectors(nor, addr, len);
- if (ret)
- goto erase_err;
+ u64 erase_len = 0;
+
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (addr > sz - 1) && params) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+ /* perform multi sector erase onec per Flash*/
+ while (len) {
+ erase_len = (len > (sz - addr)) ? (sz - addr) : len;
+ offset = addr;
+ nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
+ if (nor->flags & SNOR_F_HAS_STACKED) {
+ params = spi_nor_get_params(nor, cur_cs_num);
+ offset -= (sz - params->size);
+ }
+ ret = spi_nor_erase_multi_sectors(nor, offset, erase_len);
+ if (ret)
+ goto erase_err;
+ len -= erase_len;
+ addr += erase_len;
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
ret = spi_nor_write_disable(nor);
@@ -1713,7 +1768,10 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
- ssize_t ret;
+ struct spi_nor_flash_parameter *params;
+ ssize_t ret, read_len;
+ u32 cur_cs_num = 0;
+ u64 sz;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
@@ -1721,9 +1779,23 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (ret)
return ret;
+ params = spi_nor_get_params(nor, 0);
+ sz = params->size;
+
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (from > sz - 1) && params) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
while (len) {
loff_t addr = from;
+ nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
+ read_len = (len > (sz - addr)) ? (sz - addr) : len;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ addr -= (sz - params->size);
+
addr = spi_nor_convert_addr(nor, addr);
ret = spi_nor_read_data(nor, addr, len, buf);
@@ -1735,11 +1807,22 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
if (ret < 0)
goto read_err;
- WARN_ON(ret > len);
+ WARN_ON(ret > read_len);
*retlen += ret;
buf += ret;
from += ret;
len -= ret;
+
+ /*
+ * Flash cross over condition in stacked mode.
+ *
+ */
+ if ((nor->flags & SNOR_F_HAS_STACKED) && (from > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+
}
ret = 0;
@@ -1759,13 +1842,22 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
struct spi_nor *nor = mtd_to_spi_nor(mtd);
struct spi_nor_flash_parameter *params;
size_t page_offset, page_remain, i;
+ u32 page_size, cur_cs_num = 0;
ssize_t ret;
- u32 page_size;
+ u64 sz;
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
params = spi_nor_get_params(nor, 0);
page_size = params->page_size;
+ sz = params->size;
+
+ /* Determine the flash from which the operation need to start */
+ while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && (to > sz - 1) && params) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
ret = spi_nor_lock_and_prep(nor);
if (ret)
@@ -1790,6 +1882,10 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
/* the size of data remaining on the first page */
page_remain = min_t(size_t, page_size - page_offset, len - i);
+ nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ addr -= (sz - params->size);
+
addr = spi_nor_convert_addr(nor, addr);
ret = spi_nor_write_enable(nor);
@@ -1806,6 +1902,15 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
goto write_err;
*retlen += written;
i += written;
+
+ /*
+ * Flash cross over condition in stacked mode.
+ */
+ if ((nor->flags & SNOR_F_HAS_STACKED) && ((to + i) > sz - 1)) {
+ cur_cs_num++;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
}
write_err:
@@ -1918,8 +2023,6 @@ int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
static int spi_nor_spimem_check_op(struct spi_nor *nor,
struct spi_mem_op *op)
{
- struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
-
/*
* First test with 4 address bytes. The opcode itself might
* be a 3B addressing opcode but we don't care, because
@@ -1928,7 +2031,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor,
*/
op->addr.nbytes = 4;
if (!spi_mem_supports_op(nor->spimem, op)) {
- if (params->size > SZ_16M)
+ if (nor->mtd.size > SZ_16M)
return -EOPNOTSUPP;
/* If flash size <= 16MB, 3 address bytes are sufficient */
@@ -2516,6 +2619,10 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
static void spi_nor_late_init_params(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+ struct device_node *np = spi_nor_get_flash_node(nor);
+ u64 flash_size[SNOR_FLASH_CNT_MAX];
+ u32 idx = 0, i = 0;
+ int rc;
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->late_init)
@@ -2533,6 +2640,36 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
*/
if (nor->flags & SNOR_F_HAS_LOCK && !params->locking_ops)
spi_nor_init_default_locking_ops(nor);
+ /*
+ * 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 = of_property_read_u64_index(np, "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->flags & SNOR_F_HAS_STACKED) {
+ for (idx = 1; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ params = devm_kzalloc(nor->dev, sizeof(*params), GFP_KERNEL);
+ if (params) {
+ memcpy(params, spi_nor_get_params(nor, 0), sizeof(*params));
+ params->size = flash_size[idx];
+ spi_nor_set_params(nor, idx, params);
+ }
+ }
+ }
}
/**
@@ -2741,22 +2878,36 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
*/
static int spi_nor_quad_enable(struct spi_nor *nor)
{
- struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
+ struct spi_nor_flash_parameter *params;
+ int err, idx;
- if (!params->quad_enable)
- return 0;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (params) {
+ if (!params->quad_enable)
+ return 0;
- if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
- spi_nor_get_protocol_width(nor->write_proto) == 4))
- return 0;
+ if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4))
+ return 0;
+ /*
+ * Set the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 0x01 << idx;
- return params->quad_enable(nor);
+ err = params->quad_enable(nor);
+ if (err)
+ return err;
+ }
+ }
+ return err;
}
static int spi_nor_init(struct spi_nor *nor)
{
- struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
- int err;
+ struct spi_nor_flash_parameter *params;
+ int err, idx;
err = spi_nor_octal_dtr_enable(nor, true);
if (err) {
@@ -2797,9 +2948,19 @@ static int spi_nor_init(struct spi_nor *nor)
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
- err = params->set_4byte_addr_mode(nor, true);
- if (err && err != -ENOTSUPP)
- return err;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (params) {
+ /*
+ * Select the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ err = params->set_4byte_addr_mode(nor, true);
+ if (err && err != -ENOTSUPP)
+ return err;
+ }
+ }
}
return 0;
@@ -2915,19 +3076,31 @@ void spi_nor_restore(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params;
int ret;
+ int idx;
/* restore the addressing mode */
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET) {
- params = spi_nor_get_params(nor, 0);
- ret = params->set_4byte_addr_mode(nor, false);
- if (ret)
- /*
- * Do not stop the execution in the hope that the flash
- * will default to the 3-byte address mode after the
- * software reset.
- */
- dev_err(nor->dev, "Failed to exit 4-byte address mode, err = %d\n", ret);
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (params) {
+ /*
+ * Select the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ ret = params->set_4byte_addr_mode(nor, false);
+ if (ret)
+ /*
+ * Do not stop the execution in the hope that the flash
+ * will default to the 3-byte address mode after the
+ * software reset.
+ */
+ dev_err(nor->dev,
+ "Failed to exit 4-byte address mode, err = %d\n",
+ ret);
+ }
+ }
}
if (nor->flags & SNOR_F_SOFT_RESET)
@@ -2995,6 +3168,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
struct spi_nor_flash_parameter *params = spi_nor_get_params(nor, 0);
struct mtd_info *mtd = &nor->mtd;
struct device *dev = nor->dev;
+ u64 total_sz = 0;
+ int idx;
spi_nor_set_mtd_locking_ops(nor);
spi_nor_set_mtd_otp_ops(nor);
@@ -3010,7 +3185,12 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->_erase = spi_nor_erase;
mtd->writesize = params->writesize;
mtd->writebufsize = params->page_size;
- mtd->size = params->size;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (params)
+ total_sz += params->size;
+ }
+ mtd->size = total_sz;
mtd->_read = spi_nor_read;
/* Might be already set by some SST flashes. */
if (!mtd->_write)
@@ -11,6 +11,9 @@
#define SPI_NOR_MAX_ID_LEN 6
+/* In single configuration enable CS0 */
+#define SPI_NOR_ENABLE_CS0 BIT(0)
+
/* Standard SPI NOR flash operations. */
#define SPI_NOR_READID_OP(naddr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 0), \
@@ -130,6 +133,7 @@ enum spi_nor_option_flags {
SNOR_F_IO_MODE_EN_VOLATILE = BIT(11),
SNOR_F_SOFT_RESET = BIT(12),
SNOR_F_SWP_IS_VOLATILE = BIT(13),
+ SNOR_F_HAS_STACKED = BIT(14),
};
struct spi_nor_read_command {
@@ -128,6 +128,12 @@
#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
#define SR2_QUAD_EN_BIT7 BIT(7)
+/*
+ * Maximum number of flashes that can be connected
+ * in stacked/parallel configuration
+ */
+#define SNOR_FLASH_CNT_MAX 2
+
/* Supported SPI protocols */
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
#define SNOR_PROTO_INST_SHIFT 16
@@ -400,7 +406,7 @@ struct spi_nor {
const struct spi_nor_controller_ops *controller_ops;
- struct spi_nor_flash_parameter *params;
+ struct spi_nor_flash_parameter *params[SNOR_FLASH_CNT_MAX];
struct {
struct spi_mem_dirmap_desc *rdesc;
@@ -423,13 +429,13 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
static inline struct spi_nor_flash_parameter *spi_nor_get_params(const struct spi_nor *nor, u8 idx)
{
- return nor->params;
+ return nor->params[idx];
}
static inline void spi_nor_set_params(struct spi_nor *nor, u8 idx,
struct spi_nor_flash_parameter *params)
{
- nor->params = params;
+ nor->params[idx] = params;
}
/**
* spi_nor_scan() - scan the SPI NOR