@@ -464,17 +464,29 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr)
op.data.nbytes = 2;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ op.data.nbytes = 2;
+
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
} else {
- ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr,
- 1);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ ret = spi_nor_controller_ops_read_reg(nor,
+ SPINOR_OP_RDSR,
+ sr, 2);
+ else
+ ret = spi_nor_controller_ops_read_reg(nor,
+ SPINOR_OP_RDSR,
+ sr, 1);
}
if (ret)
dev_dbg(nor->dev, "error %d reading SR\n", ret);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ sr[0] |= sr[1];
+
return ret;
}
@@ -1466,12 +1478,122 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
if (ret)
return ret;
- /* whole-chip erase? */
- if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
- unsigned long timeout;
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL)) {
+ /* whole-chip erase? */
+ if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
+ unsigned long timeout;
+
+ while ((cur_cs_num < SNOR_FLASH_CNT_MAX) && params) {
+ nor->spimem->spi->cs_index_mask = 1 << 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;
+
+ /*
+ * 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++;
+ params = spi_nor_get_params(nor, 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
+ * to use "small sector erase", but that's not always optimal.
+ */
+
+ /* "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 = 1 << cur_cs_num;
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ goto erase_err;
+
+ 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;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto erase_err;
+
+ 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 {
+ 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 = 1 << 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;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ sz += params->size;
+ }
+ }
+ } else {
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+
+ /* whole-chip erase? */
+ if (len == mtd->size && !(nor->flags &
+ SNOR_F_NO_OP_CHIP_ERASE)) {
+ unsigned long timeout;
- 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;
@@ -1488,90 +1610,45 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
*/
timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
- (unsigned long)(params->size / SZ_2M));
+ (unsigned long)(mtd->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
- * to use "small sector erase", but that's not always optimal.
- */
- /* "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;
- }
+ /* 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
+ * to use "small sector erase", but that's not always optimal.
+ */
- while (len) {
- nor->spimem->spi->cs_index_mask = 0x01 << cur_cs_num;
- ret = spi_nor_write_enable(nor);
- if (ret)
- goto erase_err;
+ /* "sector"-at-a-time erase */
+ } else if (spi_nor_has_uniform_erase(nor)) {
+ while (len) {
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ goto erase_err;
- offset = addr;
- if (nor->flags & SNOR_F_HAS_STACKED) {
- params = spi_nor_get_params(nor, cur_cs_num);
- offset -= (sz - params->size);
- }
+ offset = addr / 2;
- ret = spi_nor_erase_sector(nor, offset);
- if (ret)
- goto erase_err;
-
- ret = spi_nor_wait_till_ready(nor);
- if (ret)
- goto erase_err;
+ ret = spi_nor_erase_sector(nor, offset);
+ if (ret)
+ goto erase_err;
- addr += mtd->erasesize;
- len -= mtd->erasesize;
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto erase_err;
- /*
- * 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;
+ addr += mtd->erasesize;
+ len -= mtd->erasesize;
}
- }
-
- /* erase multiple sectors */
- } else {
- 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);
+ /* erase multiple sectors */
+ } else {
+ offset = addr / 2;
+ ret = spi_nor_erase_multi_sectors(nor, offset, 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);
erase_err:
@@ -1771,34 +1848,59 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
struct spi_nor_flash_parameter *params;
ssize_t ret, read_len;
u32 cur_cs_num = 0;
- u64 sz;
+ u_char *readbuf;
+ bool is_ofst_odd = false;
+ u64 sz = 0;
dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
- ret = spi_nor_lock_and_prep(nor);
- 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;
+ /*
+ * Cannot read from odd offset in parallel mode, so read
+ * len + 1 from offset + 1 and ignore offset[0] data.
+ */
+ if ((nor->flags & SNOR_F_HAS_PARALLEL) && (from & 0x01)) {
+ from = (loff_t)(from - 1);
+ len = (size_t)(len + 1);
+ is_ofst_odd = true;
+ readbuf = kmalloc(len, GFP_KERNEL);
+ if (!readbuf)
+ return -ENOMEM;
+ } else {
+ readbuf = buf;
+ }
+
+ if (!(nor->flags & SNOR_F_HAS_PARALLEL)) {
+ /* 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;
+ }
}
+ ret = spi_nor_lock_and_prep(nor);
+ if (ret)
+ return ret;
+
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);
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ read_len = len;
+ addr /= 2;
+ } else {
+ nor->spimem->spi->cs_index_mask = 1 << 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);
+ ret = spi_nor_read_data(nor, addr, read_len, readbuf);
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
@@ -1808,8 +1910,20 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
goto read_err;
WARN_ON(ret > read_len);
- *retlen += ret;
+ if (is_ofst_odd) {
+ /*
+ * Cannot read from odd offset in parallel mode.
+ * So read len + 1 from offset + 1 from the flash
+ * and copy len data from readbuf[1].
+ */
+ memcpy(buf, (readbuf + 1), (len - 1));
+ *retlen += (ret - 1);
+ } else {
+ *retlen += ret;
+ }
buf += ret;
+ if (!is_ofst_odd)
+ readbuf += ret;
from += ret;
len -= ret;
@@ -1827,6 +1941,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
ret = 0;
read_err:
+ if (is_ofst_odd)
+ kfree(readbuf);
+
spi_nor_unlock_and_unprep(nor);
return ret;
}
@@ -1852,13 +1969,38 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
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;
- }
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ /*
+ * Cannot write to odd offset in parallel mode,
+ * so write 2 byte first.
+ */
+ if (to & 0x01) {
+ u8 two[2] = {0xff, buf[0]};
+ size_t written_len;
+
+ ret = spi_nor_write(mtd, to & ~1, 2, &written_len, two);
+ if (ret < 0)
+ return ret;
+ *retlen += 1; /* We've written only one actual byte */
+ ++buf;
+ --len;
+ ++to;
+ }
+ /*
+ * Write operation are performed in page size chunks and in
+ * parallel memories both the flashes are written simultaneously,
+ * hence doubled the page_size.
+ */
+ page_size <<= 1;
+ } else {
+ /* 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)
return ret;
@@ -1882,9 +2024,14 @@ 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);
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ addr /= 2;
+ } else {
+ nor->spimem->spi->cs_index_mask = 1 << cur_cs_num;
+ params = spi_nor_get_params(nor, cur_cs_num);
+ addr -= (sz - params->size);
+ }
addr = spi_nor_convert_addr(nor, addr);
@@ -2323,7 +2470,15 @@ static int spi_nor_select_erase(struct spi_nor *nor)
if (!erase)
return -EINVAL;
nor->erase_opcode = erase->opcode;
- mtd->erasesize = erase->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 = erase->size * 2;
+ else
+ mtd->erasesize = erase->size;
return 0;
}
@@ -2341,7 +2496,15 @@ static int spi_nor_select_erase(struct spi_nor *nor)
if (!erase)
return -EINVAL;
- mtd->erasesize = erase->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 = erase->size * 2;
+ else
+ mtd->erasesize = erase->size;
return 0;
}
@@ -2659,7 +2822,22 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
nor->flags |= SNOR_F_HAS_STACKED;
}
}
- if (nor->flags & SNOR_F_HAS_STACKED) {
+ i = 0;
+ idx = 0;
+ while (i < SNOR_FLASH_CNT_MAX) {
+ rc = of_property_read_u64_index(np, "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)) {
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);
@@ -2881,24 +3059,42 @@ static int spi_nor_quad_enable(struct spi_nor *nor)
struct spi_nor_flash_parameter *params;
int err, idx;
- 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 (nor->flags & SNOR_F_HAS_PARALLEL) {
+ params = spi_nor_get_params(nor, 0);
+ 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;
- /*
- * Set the appropriate CS index before
- * issuing the command.
- */
- nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+ spi_nor_get_protocol_width(nor->write_proto) == 4))
+ return 0;
+ /*
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
+ */
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ err = params->quad_enable(nor);
+ if (err)
+ return err;
+ } else {
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (params) {
+ if (!params->quad_enable)
+ return 0;
- err = params->quad_enable(nor);
- if (err)
- return err;
+ 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 = 1 << idx;
+
+ err = params->quad_enable(nor);
+ if (err)
+ return err;
+ }
}
}
return err;
@@ -2948,17 +3144,29 @@ 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");
- 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;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ /*
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
+ */
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ params = spi_nor_get_params(nor, 0);
+ err = params->set_4byte_addr_mode(nor, true);
+ if (err && err != -ENOTSUPP)
+ return err;
+ } else {
+ 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 = 1 << idx;
+ err = params->set_4byte_addr_mode(nor, true);
+ if (err && err != -ENOTSUPP)
+ return err;
+ }
}
}
}
@@ -3081,20 +3289,39 @@ void spi_nor_restore(struct spi_nor *nor)
/* restore the addressing mode */
if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) &&
nor->flags & SNOR_F_BROKEN_RESET) {
- for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
- params = spi_nor_get_params(nor, idx);
- if (params) {
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ /*
+ * In parallel mode both chip selects i.e., CS0 &
+ * CS1 need to be asserted simulatneously.
+ */
+ nor->spimem->spi->cs_index_mask = SPI_NOR_ENABLE_MULTI_CS;
+ 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);
+ } else {
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ params = spi_nor_get_params(nor, idx);
+ if (!params)
+ break;
/*
* Select the appropriate CS index before
* issuing the command.
*/
- nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ nor->spimem->spi->cs_index_mask = 1 << 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.
+ * 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",
@@ -3184,7 +3411,16 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
else
mtd->_erase = spi_nor_erase;
mtd->writesize = params->writesize;
- mtd->writebufsize = params->page_size;
+ /*
+ * In parallel-memories the write operation is
+ * performed on both the flashes simultaneously
+ * one page per flash, so double the writebufsize.
+ */
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ mtd->writebufsize = params->page_size << 1;
+ else
+ mtd->writebufsize = params->page_size;
+
for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
params = spi_nor_get_params(nor, idx);
if (params)
@@ -14,6 +14,9 @@
/* In single configuration enable CS0 */
#define SPI_NOR_ENABLE_CS0 BIT(0)
+/* In parallel configuration enable multiple CS */
+#define SPI_NOR_ENABLE_MULTI_CS (BIT(0) | BIT(1))
+
/* 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), \
@@ -134,6 +137,7 @@ enum spi_nor_option_flags {
SNOR_F_SOFT_RESET = BIT(12),
SNOR_F_SWP_IS_VOLATILE = BIT(13),
SNOR_F_HAS_STACKED = BIT(14),
+ SNOR_F_HAS_PARALLEL = BIT(15),
};
struct spi_nor_read_command {
@@ -357,6 +357,9 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
op.data.nbytes = 2;
}
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ op.data.nbytes = 2;
+
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op);
@@ -368,6 +371,8 @@ static int micron_st_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
if (ret)
dev_dbg(nor->dev, "error %d reading FSR\n", ret);
+ if (nor->flags & SNOR_F_HAS_PARALLEL)
+ fsr[0] &= fsr[1];
return ret;
}