[RFC PATCH 2/2] mtd: spi-nor: Add support for stacked/parallel memories

From: Amit Kumar Mahapatra
Date: Mon Jun 06 2022 - 07:26:41 EST


While initializing the flash parameter structure, size of each flash is
updated with the values coming from "stacked-memories" and
"parallel-memories" DT properties.

Two new nor->flags (SNOR_F_HAS_STACKED and SNOR_F_HAS_PARALLEL) are added
to distinguish between the stacked and parallel configuration.

In parallel configuration all the operations need to be performed on both
the flashes simultaneously, So during each operation SPI-NOR needs to set
0th bit(CS0) & 1st bit(CS1) in nor->spimem->spi->cs_index_mask. The GQSPI
driver will then assert CS0 & CS1.

In stacked configuration the SPI-NOR, with individual flash size information,
will determining the flash on which the operation need to be performed and
it will set the appropriate CS bit in nor->spimem->spi->cs_index_mask.

Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@xxxxxxxxxx>
---
drivers/mtd/spi-nor/core.c | 104 +++++++++++++++++++++++++++++++-----
drivers/mtd/spi-nor/core.h | 5 ++
include/linux/mtd/spi-nor.h | 8 ++-
3 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 502967c76c5f..5d9bbb28659a 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2463,6 +2463,9 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
*/
static void spi_nor_late_init_params(struct spi_nor *nor)
{
+ struct device_node *np = spi_nor_get_flash_node(nor);
+ u64 flash_size[SNOR_FLASH_CNT_MAX];
+
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->late_init)
nor->manufacturer->fixups->late_init(nor);
@@ -2479,6 +2482,27 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
*/
if (nor->flags & SNOR_F_HAS_LOCK && !nor->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.
+ */
+ if (!of_property_read_u64_array(np, "stacked-memories", &flash_size[0], SNOR_FLASH_CNT_MAX)) {
+ nor->flags |= SNOR_F_HAS_STACKED;
+ } else if (!of_property_read_u64_array(np, "parallel-memories", &flash_size[0], SNOR_FLASH_CNT_MAX)){
+ nor->flags |= SNOR_F_HAS_PARALLEL;
+ }
+
+ if (nor->flags & (SNOR_F_HAS_STACKED | SNOR_F_HAS_PARALLEL)) {
+ nor->params[1] = devm_kzalloc(nor->dev, sizeof(*nor->params[0]), GFP_KERNEL);
+ if (!nor->params[1])
+ return -ENOMEM;
+ memcpy(nor->params[1], nor->params[0], sizeof(*nor->params[0]));
+ nor->params[1]->size = flash_size[1];
+ }
+ return 0;
}

/**
@@ -2614,8 +2638,8 @@ static int spi_nor_init_params(struct spi_nor *nor)
{
int ret;

- nor->params = devm_kzalloc(nor->dev, sizeof(*nor->params), GFP_KERNEL);
- if (!nor->params)
+ nor->params[0] = devm_kzalloc(nor->dev, sizeof(*nor->params[0]), GFP_KERNEL);
+ if (!nor->params[0])
return -ENOMEM;

spi_nor_init_default_params(nor);
@@ -2677,19 +2701,51 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable)
*/
static int spi_nor_quad_enable(struct spi_nor *nor)
{
- if (!nor->params->quad_enable)
- return 0;
+ u8 idx;
+ int err;

- if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 ||
- spi_nor_get_protocol_width(nor->write_proto) == 4))
- return 0;
+ if (nor->flags & SNOR_F_HAS_PARALLEL) {
+ if (!nor->params[0]->quad_enable)
+ return 0;

- return nor->params->quad_enable(nor);
+ 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_BOTH_CS;
+ err = nor->params[0]->quad_enable(nor);
+ } else {
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ if (nor->params[idx] != NULL) {
+
+ if (!nor->params[idx]->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;
+ err = nor->params[idx]->quad_enable(nor);
+ if (err)
+ return err;
+ }
+ }
+ }
+ return err;
}

static int spi_nor_init(struct spi_nor *nor)
{
int err;
+ int idx;

err = spi_nor_octal_dtr_enable(nor, true);
if (err) {
@@ -2730,7 +2786,25 @@ 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");
- nor->params->set_4byte_addr_mode(nor, true);
+ 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_BOTH_CS;
+ nor->params[0]->set_4byte_addr_mode(nor, true);
+ } else {
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ if (nor->params[idx] != NULL) {
+ /*
+ * Select the appropriate CS index before
+ * issuing the command.
+ */
+ nor->spimem->spi->cs_index_mask = 0x01 << idx;
+ nor->params[idx]->set_4byte_addr_mode(nor, true);
+ }
+ }
+ }
}

return 0;
@@ -2913,6 +2987,8 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
{
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);
@@ -2926,9 +3002,13 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
mtd->flags |= MTD_NO_ERASE;
else
mtd->_erase = spi_nor_erase;
- mtd->writesize = nor->params->writesize;
- mtd->writebufsize = nor->params->page_size;
- mtd->size = nor->params->size;
+ mtd->writesize = nor->params[0]->writesize;
+ mtd->writebufsize = nor->params[0]->page_size;
+ for (idx = 0; idx < SNOR_FLASH_CNT_MAX; idx++) {
+ if (nor->params[idx] != NULL)
+ total_sz += nor->params[idx]->size;
+ }
+ mtd->size = total_sz;
mtd->_read = spi_nor_read;
/* Might be already set by some SST flashes. */
if (!mtd->_write)
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index 3f841ec36e56..4e8bf3cbe331 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -11,6 +11,9 @@

#define SPI_NOR_MAX_ID_LEN 6

+/* In parallel configuration enable both CS */
+#define SPI_NOR_ENABLE_BOTH_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), \
@@ -130,6 +133,8 @@ 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),
+ SNOR_F_HAS_PARALLEL = BIT(15),
};

struct spi_nor_read_command {
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 1ede4c89805a..40800e7235ee 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -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
@@ -397,7 +403,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;
--
2.17.1