diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8963018e1b..b8cffb0ce7 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -226,7 +226,7 @@ static int spinand_ecc_enable(struct spinand_device *spinand, enable ? CFG_ECC_ENABLE : 0); } -static int spinand_write_enable_op(struct spinand_device *spinand) +int spinand_write_enable_op(struct spinand_device *spinand) { struct spi_mem_op op = spinand->ctrl_ops->ops.write_enable; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 4f8b8de3a9..acb197e668 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -13,6 +13,7 @@ #include #endif #include +#include #include #define SPINAND_MFR_WINBOND 0xEF @@ -189,6 +190,48 @@ static int winbond_spinand_init(struct spinand_device *spinand) return 0; } +/** + * winbond_write_vcr_op() - write values onto the volatile configuration + * registers (VCR) + * @spinand: the spinand device + * @reg: the address of the particular reg in the VCR to be written on + * @val: the value to be written on the reg in the VCR + * + * Volatile configuration registers are a separate set of configuration + * registers, i.e. they differ from the status registers SR-1/2/3. A different + * SPI instruction is required to write to these registers. Any changes + * to the Volatile Configuration Register get transferred directly to + * the Internal Configuration Register and instantly reflect on the + * device operation. + */ +static int winbond_write_vcr_op(struct spinand_device *spinand, u8 reg, u8 val) +{ + int ret; + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(0x81, 1), + SPI_MEM_OP_ADDR(3, reg, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, spinand->scratchbuf, 1)); + + *spinand->scratchbuf = val; + + ret = spinand_write_enable_op(spinand); + if (ret) + return ret; + + ret = spi_mem_exec_op(spinand->slave, &op); + if (ret) + return ret; + + /* + * Write VCR operation doesn't set the busy bit in SR, so can't perform + * a status poll. Minimum time of 50ns is needed to complete the write. + * So, give thrice the minimum required delay. + */ + ndelay(150); + return 0; +} + static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { .detect = winbond_spinand_detect, .init = winbond_spinand_init, diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 6902c4cd43..f90bcb66aa 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -574,5 +574,6 @@ int spinand_match_and_init(struct spinand_device *dev, int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); +int spinand_write_enable_op(struct spinand_device *spinand); #endif /* __LINUX_MTD_SPINAND_H */