On Sat, 1 Jan 2022 13:12:44 +0530 Apurva Nandan <a-nandan@xxxxxx> wrote: > Enable Octal DTR SPI mode, i.e. 8D-8D-8D mode, if the SPI NAND flash > device supports it. Mixed OSPI (1S-1S-8S & 1S-8S-8S), mixed DTR modes > (1S-1D-8D), etc. aren't supported yet. > > The method to switch to Octal DTR SPI mode may vary across > manufacturers. For example, for Winbond, it is enabled by writing > values to the volatile configuration register. So, let the > manufacturer's code have their own implementation for switching to > Octal DTR SPI mode. > > Check for the SPI NAND device's support for Octal DTR mode using > spinand flags, and if the data_ops and ctrl_ops are 8D-8D-8D, call > change_mode() manufacturer op. If the SPI controller doesn't > supports these modes, the selected data_ops and ctrl_ops will > prevent switching to the Octal DTR mode. And finally update the > spinand protocol and ctrl_ops on success. Similarly, for disabling > Octal DTR mode, call change_mode(), and update protocol and ctrl_ops. > > Signed-off-by: Apurva Nandan <a-nandan@xxxxxx> > --- > drivers/mtd/nand/spi/core.c | 79 +++++++++++++++++++++++++++++++++++++ > include/linux/mtd/spinand.h | 1 + > 2 files changed, 80 insertions(+) > > diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c > index 1a602e4dd6bd..2fd08085db6f 100644 > --- a/drivers/mtd/nand/spi/core.c > +++ b/drivers/mtd/nand/spi/core.c > @@ -1067,6 +1067,81 @@ spinand_select_ctrl_ops_variant(struct spinand_device *spinand, > return NULL; > } > > +static bool spinand_op_is_octal_dtr(const struct spi_mem_op *op) > +{ > + return op->cmd.buswidth == 8 && op->cmd.dtr && > + op->addr.buswidth == 8 && op->addr.dtr && > + op->data.buswidth == 8 && op->data.dtr; > +} > + > +static int spinand_init_octal_dtr_enable(struct spinand_device *spinand) > +{ > + struct device *dev = &spinand->spimem->spi->dev; > + const struct spinand_ctrl_ops *octal_dtr_ctrl_ops; > + int ret; > + > + if (!(spinand->flags & SPINAND_HAS_OCTAL_DTR_BIT)) > + return 0; > + > + if (!(spinand_op_is_octal_dtr(spinand->data_ops.read_cache) && > + spinand_op_is_octal_dtr(spinand->data_ops.write_cache) && > + spinand_op_is_octal_dtr(spinand->data_ops.update_cache))) > + return 0; > + > + octal_dtr_ctrl_ops = spinand_select_ctrl_ops_variant(spinand, > + spinand->desc_entry->ctrl_ops_variants, > + SPINAND_8D_8D_8D); > + > + if (!octal_dtr_ctrl_ops) > + return 0; > + > + if (!spinand->manufacturer->ops->change_mode) { > + dev_dbg(dev, > + "Missing ->change_mode(), unable to switch mode\n"); > + return -EINVAL; Looks like something that's worth printing at the info level, and I think returning EOPNOTSUPP would be more appropriate. > + } > + > + ret = spinand->manufacturer->ops->change_mode(spinand, > + SPINAND_8D_8D_8D); > + if (ret) { > + dev_err(dev, > + "Failed to enable Octal DTR SPI mode (err = %d)\n", > + ret); > + return ret; > + } > + > + spinand->protocol = SPINAND_8D_8D_8D; > + spinand->ctrl_ops = octal_dtr_ctrl_ops; > + > + dev_dbg(dev, > + "%s SPI NAND switched to Octal DTR SPI (8D-8D-8D) mode\n", > + spinand->manufacturer->name); > + return 0; > +} > + > +static int spinand_init_octal_dtr_disable(struct spinand_device *spinand) This function is never used. I guess it should be called in the suspend/shutdown path, at least. > +{ > + struct device *dev = &spinand->spimem->spi->dev; > + int ret; > + > + if (!spinand->manufacturer->ops->change_mode) > + return -EINVAL; > + > + ret = spinand->manufacturer->ops->change_mode(spinand, > + SPINAND_1S_1S_1S); > + > + if (ret) { > + dev_err(dev, > + "Failed to disable Octal DTR SPI mode (err = %d)\n", > + ret); > + return ret; > + } > + > + spinand->protocol = SPINAND_1S_1S_1S; > + spinand->ctrl_ops = &spinand_default_ctrl_ops; > + return 0; > +} > + > /** > * spinand_match_and_init() - Try to find a match between a device ID and an > * entry in a spinand_info table > @@ -1203,6 +1278,10 @@ static int spinand_init_flash(struct spinand_device *spinand) > break; > } > > + ret = spinand_init_octal_dtr_enable(spinand); > + if (ret) > + return ret; > + > if (ret) > spinand_manufacturer_cleanup(spinand); > > diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h > index a8c071983a27..f12aa4516fab 100644 > --- a/include/linux/mtd/spinand.h > +++ b/include/linux/mtd/spinand.h > @@ -417,6 +417,7 @@ struct spinand_ecc_info { > > #define SPINAND_HAS_QE_BIT BIT(0) > #define SPINAND_HAS_CR_FEAT_BIT BIT(1) > +#define SPINAND_HAS_OCTAL_DTR_BIT BIT(2) Do we really need a new flag for this? Isn't the template op initialization enough to reflect whether the NAND and controller can do 8DTR or not? > > /** > * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure