diff options
Diffstat (limited to 'drivers/mtd/spi-nor/winbond.c')
-rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 8d0a00d69e12..63a93c9eb917 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -10,6 +10,7 @@ #define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ #define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */ +#define WINBOND_NOR_OP_SELDIE 0xc2 /* Select active die */ #define WINBOND_NOR_WREAR_OP(buf) \ SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \ @@ -17,6 +18,12 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) +#define WINBOND_NOR_SELDIE_OP(buf) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 0), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, buf, 0)) + static int w25q128_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -66,6 +73,79 @@ static const struct spi_nor_fixups w25q256_fixups = { .post_bfpt = w25q256_post_bfpt_fixups, }; +/** + * winbond_nor_select_die() - Set active die. + * @nor: pointer to 'struct spi_nor'. + * @die: die to set active. + * + * Certain Winbond chips feature more than a single die. This is mostly hidden + * to the user, except that some chips may experience time deviation when + * modifying the status bits between dies, which in some corner cases may + * produce problematic races. Being able to explicitly select a die to check its + * state in this case may be useful. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_nor_select_die(struct spi_nor *nor, u8 die) +{ + int ret; + + nor->bouncebuf[0] = die; + + if (nor->spimem) { + struct spi_mem_op op = WINBOND_NOR_SELDIE_OP(nor->bouncebuf); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + } else { + ret = spi_nor_controller_ops_write_reg(nor, + WINBOND_NOR_OP_SELDIE, + nor->bouncebuf, 1); + } + + if (ret) + dev_dbg(nor->dev, "error %d selecting die %d\n", ret, die); + + return ret; +} + +static int winbond_nor_multi_die_ready(struct spi_nor *nor) +{ + int ret, i; + + for (i = 0; i < nor->params->n_dice; i++) { + ret = winbond_nor_select_die(nor, i); + if (ret) + return ret; + + ret = spi_nor_sr_ready(nor); + if (ret <= 0) + return ret; + } + + return 1; +} + +static int +winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * SFDP supports dice numbers, but this information is only available in + * optional additional tables which are not provided by these chips. + * Dice number has an impact though, because these devices need extra + * care when reading the busy bit. + */ + nor->params->n_dice = nor->params->size / SZ_64M; + nor->params->ready = winbond_nor_multi_die_ready; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_multi_die_fixups = { + .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, +}; + static const struct flash_info winbond_nor_parts[] = { { .id = SNOR_ID(0xef, 0x30, 0x10), @@ -147,6 +227,10 @@ static const struct flash_info winbond_nor_parts[] = { .size = SZ_64M, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, }, { + /* W25Q01JV */ + .id = SNOR_ID(0xef, 0x40, 0x21), + .fixups = &winbond_nor_multi_die_fixups, + }, { .id = SNOR_ID(0xef, 0x50, 0x12), .name = "w25q20bw", .size = SZ_256K, @@ -222,6 +306,10 @@ static const struct flash_info winbond_nor_parts[] = { .id = SNOR_ID(0xef, 0x70, 0x19), .name = "w25q256jvm", }, { + /* W25Q02JV */ + .id = SNOR_ID(0xef, 0x70, 0x22), + .fixups = &winbond_nor_multi_die_fixups, + }, { .id = SNOR_ID(0xef, 0x71, 0x19), .name = "w25m512jv", .size = SZ_64M, |