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