123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- From 0cd0df6b3583920ab9231035e533560b58e71a50 Mon Sep 17 00:00:00 2001
- From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
- Date: Fri, 8 Jan 2016 17:02:18 +0100
- Subject: [PATCH 21/33] mtd: spi-nor: fix support of Micron memories
- This patch adds missing mode transitions. Indeed depending on both the
- current memory mode and the new protocol wanted by the user, we may need
- to perform a switch back to the Extended SPI mode.
- However when the current mode is the Quad mode and the user has asked for
- a Quad SPI protocol, we'd rather stay in Quad mode and use the Fast Read
- 4-4-4 command than switch to the Extended SPI mode and use the Fast Read
- 1-1-4 command.
- Also we'd rather stay in Dual mode than swith to the Extended SPI mode
- whenever the user has asked for Dual SPI protocol.
- Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
- ---
- drivers/mtd/spi-nor/spi-nor.c | 154 +++++++++++++++++++++++++++++++++++++++---
- include/linux/mtd/spi-nor.h | 1 +
- 2 files changed, 147 insertions(+), 8 deletions(-)
- diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
- index aa7d26d..ae2cbac 100644
- --- a/drivers/mtd/spi-nor/spi-nor.c
- +++ b/drivers/mtd/spi-nor/spi-nor.c
- @@ -1317,6 +1317,147 @@ static int winbond_set_single_mode(struct spi_nor *nor)
- return 0;
- }
-
- +static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val,
- + enum spi_nor_protocol proto)
- +{
- + u8 evcr;
- + int ret;
- +
- + /* Read the Enhanced Volatile Configuration Register (EVCR). */
- + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
- + if (ret < 0) {
- + dev_err(nor->dev, "error while reading EVCR register\n");
- + return ret;
- + }
- +
- + /* Check whether we need to update the protocol bits. */
- + if ((evcr & mask) == val)
- + return 0;
- +
- + /* Set EVCR protocol bits. */
- + write_enable(nor);
- + evcr = (evcr & ~mask) | val;
- + ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, &evcr, 1);
- + if (ret < 0) {
- + dev_err(nor->dev, "error while writing EVCR register\n");
- + return ret;
- + }
- +
- + /* Switch reg protocol now before accessing any other registers. */
- + nor->reg_proto = proto;
- +
- + ret = spi_nor_wait_till_ready(nor);
- + if (ret)
- + return ret;
- +
- + /* Read EVCR and check it. */
- + ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
- + if (ret < 0 || (evcr & mask) != val) {
- + dev_err(nor->dev, "Micron EVCR protocol bits not updated\n");
- + return -EINVAL;
- + }
- +
- + return 0;
- +}
- +
- +static int micron_set_extended_spi_protocol(struct spi_nor *nor)
- +{
- + int ret;
- +
- + /* Set Quad/Dual bits to 11 to select the Extended SPI mode */
- + ret = micron_set_protocol(nor,
- + EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
- + EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
- + SNOR_PROTO_1_1_1);
- + if (ret) {
- + dev_err(nor->dev, "Failed to set Micron Extended SPI mode\n");
- + return ret;
- + }
- +
- + nor->write_proto = SNOR_PROTO_1_1_1;
- + nor->erase_proto = SNOR_PROTO_1_1_1;
- + return 0;
- +}
- +
- +static int micron_set_quad_mode(struct spi_nor *nor)
- +{
- + /* Check whether the Dual SPI mode is enabled. */
- + if (unlikely(nor->read_proto == SNOR_PROTO_2_2_2)) {
- + int ret;
- +
- + /*
- + * Exit Micron Dual mode and switch to the Extended SPI mode:
- + * we can change the mode safely as we write into a volatile
- + * register.
- + * Also the Quad mode is not worth it for MTD usages: it
- + * should only be relevant for eXecution In Place (XIP) usages,
- + * which are out of the scope of the spi-nor framework.
- + */
- + ret = micron_set_extended_spi_protocol(nor);
- + if (ret)
- + return ret;
- + }
- +
- + /*
- + * Whatever the Quad mode is enabled or not, the
- + * Fast Read Quad Output 1-1-4 (0x6b) op code is supported.
- + */
- + if (nor->read_proto != SNOR_PROTO_4_4_4)
- + nor->read_proto = SNOR_PROTO_1_1_4;
- + nor->read_opcode = SPINOR_OP_READ_1_1_4;
- + return 0;
- +}
- +
- +static int micron_set_dual_mode(struct spi_nor *nor)
- +{
- + /* Check whether Quad mode is enabled. */
- + if (unlikely(nor->read_proto == SNOR_PROTO_4_4_4)) {
- + int ret;
- +
- + /*
- + * Exit Micron Quad mode and switch to the Extended SPI mode:
- + * we can change the mode safely as we write into a volatile
- + * register.
- + * Also the Dual mode is not worth it for MTD usages: it
- + * should only be relevant for eXecution In Place (XIP) usages,
- + * which are out of the scope of the spi-nor framework.
- + */
- + ret = micron_set_extended_spi_protocol(nor);
- + if (ret)
- + return ret;
- + }
- +
- + /*
- + * Whatever the Dual mode is enabled or not, the
- + * Fast Read Dual Output 1-1-2 (0x3b) op code is supported.
- + */
- + if (nor->read_proto != SNOR_PROTO_2_2_2)
- + nor->read_proto = SNOR_PROTO_1_1_2;
- + nor->read_opcode = SPINOR_OP_READ_1_1_2;
- + return 0;
- +}
- +
- +static int micron_set_single_mode(struct spi_nor *nor)
- +{
- + /* Check whether either the Dual or Quad mode is enabled. */
- + if (unlikely(nor->read_proto != SNOR_PROTO_1_1_1)) {
- + int ret;
- +
- + /*
- + * Exit Micron Dual or Quad mode and switch to the Extended SPI
- + * mode: we can change the mode safely as we write into a
- + * volatile register.
- + */
- + ret = micron_set_extended_spi_protocol(nor);
- + if (ret)
- + return ret;
- +
- + nor->read_proto = SNOR_PROTO_1_1_1;
- + }
- +
- + return 0;
- +}
- +
- static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
- {
- int status;
- @@ -1329,10 +1470,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
- return winbond_set_quad_mode(nor);
-
- case SNOR_MFR_MICRON:
- - /* Check whether Micron Quad mode is enabled. */
- - if (nor->read_proto != SNOR_PROTO_4_4_4)
- - nor->read_proto = SNOR_PROTO_1_1_4;
- - break;
- + return micron_set_quad_mode(nor);
-
- case SNOR_MFR_SPANSION:
- status = spansion_quad_enable(nor);
- @@ -1361,10 +1499,7 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
- return winbond_set_dual_mode(nor);
-
- case SNOR_MFR_MICRON:
- - /* Check whether Micron Dual mode is enabled. */
- - if (nor->read_proto != SNOR_PROTO_2_2_2)
- - nor->read_proto = SNOR_PROTO_1_1_2;
- - break;
- + return micron_set_dual_mode(nor);
-
- default:
- nor->read_proto = SNOR_PROTO_1_1_2;
- @@ -1384,6 +1519,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info)
- case SNOR_MFR_WINBOND:
- return winbond_set_single_mode(nor);
-
- + case SNOR_MFR_MICRON:
- + return micron_set_single_mode(nor);
- +
- default:
- nor->read_proto = SNOR_PROTO_1_1_1;
- break;
- diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
- index 46343f5..d0a6f34 100644
- --- a/include/linux/mtd/spi-nor.h
- +++ b/include/linux/mtd/spi-nor.h
- @@ -102,6 +102,7 @@
-
- /* Enhanced Volatile Configuration Register bits */
- #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */
- +#define EVCR_DUAL_EN_MICRON BIT(6) /* Micron Dual I/O */
-
- /* Flag Status Register bits */
- #define FSR_READY BIT(7)
- --
- 2.8.1
|