
This patch provides support of Micron QSPI memories.
Signed-off-by: Cyrille Pitchen cyrille.pitchen@atmel.com --- drivers/mtd/spi/Makefile | 1 + drivers/mtd/spi/sf_internal.h | 24 +++++ drivers/mtd/spi/sf_micron.c | 222 ++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi_flash.c | 13 +-- 4 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 drivers/mtd/spi/sf_micron.c
diff --git a/drivers/mtd/spi/Makefile b/drivers/mtd/spi/Makefile index c665836f9560..90266d619ddf 100644 --- a/drivers/mtd/spi/Makefile +++ b/drivers/mtd/spi/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_spl.o endif
obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o sf_params.o sf.o +obj-$(CONFIG_SPI_FLASH_STMICRO) += sf_micron.o obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h index a98c011218ce..afee8b69ca6b 100644 --- a/drivers/mtd/spi/sf_internal.h +++ b/drivers/mtd/spi/sf_internal.h @@ -142,6 +142,24 @@ enum spi_nor_option_flags { # define CMD_SST_AAI_WP 0xAD /* Auto Address Incr Word Program */ #endif
+/* Micron specific */ +#ifdef CONFIG_SPI_FLASH_STMICRO + +/* Volatile Configuration Register (VCR) */ +#define CMD_MICRON_WR_VCR 0x81 +#define CMD_MICRON_RD_VCR 0x85 +#define MICRON_VCR_XIP BIT(3) +#define MICRON_VCR_DUMMIES 0xf0 +#define MICRON_VCR_DUMMIES_(x) (((x) << 4) & MICRON_VCR_DUMMIES) + +/* Enhanced Volatile Configuraiton Register (EVCR) */ +#define CMD_MICRON_WR_EVCR 0x61 +#define CMD_MICRON_RD_EVCR 0x65 +#define MICRON_EVCR_QUAD_DIS BIT(7) +#define MICRON_EVCR_DUAL_DIS BIT(6) +#endif + + /** * struct spi_flash_params - SPI/QSPI flash device params structure * @@ -264,6 +282,12 @@ int spi_flash_mtd_register(struct spi_flash *flash); void spi_flash_mtd_unregister(void); #endif
+#ifdef CONFIG_SPI_FLASH_STMICRO +int spi_flash_setup_micron(struct spi_flash *flash, + const struct spi_flash_params *params, + int best_match); +#endif + /** * spi_flash_scan - scan the SPI FLASH * @flash: the spi flash structure diff --git a/drivers/mtd/spi/sf_micron.c b/drivers/mtd/spi/sf_micron.c new file mode 100644 index 000000000000..b399b6ff52fe --- /dev/null +++ b/drivers/mtd/spi/sf_micron.c @@ -0,0 +1,222 @@ +/* + * Micron SPI NOR flash support + * + * Copyright (C) 2016 Cyrille Pitchen cyrille.pitchen@atmel.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <spi.h> +#include <spi_flash.h> + +#include "sf_internal.h" + +static int spi_flash_micron_set_dummy_cycles(struct spi_flash *flash, + u8 num_dummy_cycles) +{ + u8 vcr, mask, value; + int ret; + + /* Set the number of dummy cycles and disable XIP */ + mask = (MICRON_VCR_DUMMIES | MICRON_VCR_XIP); + value = MICRON_VCR_DUMMIES_(num_dummy_cycles) | MICRON_VCR_XIP; + + /* Check current VCR value */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_VCR, 1, &vcr); + if (ret) + goto end; + + if ((vcr & mask) == value) + goto end; + + /* VCR update is needed */ + vcr = (vcr & ~mask) | value; + ret = spi_flash_update_reg(flash, CMD_MICRON_WR_VCR, 1, &vcr); + if (ret) + goto end; + + /* Verify update */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_VCR, 1, &vcr); + if (ret) + goto end; + + if ((vcr & mask) != value) + ret = -EIO; + +end: + if (ret) + printf("SF: failed to set the dummy cycles on Micron memory!\n"); + + return ret; +} + +static int spi_flash_micron_set_protocol(struct spi_flash *flash, + enum spi_flash_protocol proto, + u8 mask, u8 value) +{ + u8 evcr; + int ret; + + /* Check current EVCR value */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_EVCR, 1, &evcr); + if (ret) + goto end; + + if ((evcr & mask) == value) + goto end; + + /* EVCR update is needed */ + ret = spi_flash_cmd_write_enable(flash); + if (ret) + goto end; + + evcr = (evcr & ~mask) | value; + ret = spi_flash_write_reg(flash, CMD_MICRON_WR_EVCR, 1, &evcr); + if (ret) + goto end; + + /* + * Don't forget to update the protocol HERE otherwise next commands + * will fail. + */ + flash->reg_proto = proto; + + /* Wait for ready status */ + ret = spi_flash_wait_ready(flash); + if (ret) + goto end; + + /* Verify update */ + ret = spi_flash_read_reg(flash, CMD_MICRON_RD_EVCR, 1, &evcr); + if (ret) + goto end; + + if ((evcr & mask) != value) + ret = -EIO; + +end: + if (ret) { + printf("SF: failed to set I/O mode on Micron memory!\n"); + return ret; + } + + flash->read_proto = proto; + flash->write_proto = proto; + flash->erase_proto = proto; + return 0; +} + +static inline int spi_flash_micron_set_ext_spi_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_1_1_1; + u8 mask = (MICRON_EVCR_QUAD_DIS | MICRON_EVCR_DUAL_DIS); + u8 value = mask; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +static inline int spi_flash_micron_set_dual_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_2_2_2; + u8 mask = (MICRON_EVCR_QUAD_DIS | MICRON_EVCR_DUAL_DIS); + u8 value = MICRON_EVCR_QUAD_DIS; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +static inline int spi_flash_micron_set_quad_mode(struct spi_flash *flash) +{ + enum spi_flash_protocol proto = SPI_FLASH_PROTO_4_4_4; + u8 mask = MICRON_EVCR_QUAD_DIS; + u8 value = 0; + + return spi_flash_micron_set_protocol(flash, proto, mask, value); +} + +int spi_flash_setup_micron(struct spi_flash *flash, + const struct spi_flash_params *params, + int best_match) +{ + u16 read_cmd = best_match ? (1 << (best_match - 1)) : ARRAY_FAST; + u8 dummy_cycles = (read_cmd == ARRAY_SLOW) ? 0 : 8; + int ret = 0; + + /* Configure (Fast) Read operations */ + switch (read_cmd) { + case QUAD_CMD_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_4_4_4) + ret = spi_flash_micron_set_quad_mode(flash); + flash->read_cmd = CMD_READ_QUAD_IO_FAST; + break; + + case QUAD_IO_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_4_4; + flash->read_cmd = CMD_READ_QUAD_IO_FAST; + break; + + case QUAD_OUTPUT_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_1_4; + flash->read_cmd = CMD_READ_QUAD_OUTPUT_FAST; + break; + + case DUAL_CMD_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_2_2_2) + ret = spi_flash_micron_set_dual_mode(flash); + flash->read_cmd = CMD_READ_DUAL_IO_FAST; + break; + + case DUAL_IO_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_2_2; + flash->read_cmd = CMD_READ_DUAL_IO_FAST; + break; + + case DUAL_OUTPUT_FAST: + if (flash->reg_proto != SPI_FLASH_PROTO_1_1_1) + ret = spi_flash_micron_set_ext_spi_mode(flash); + flash->read_proto = SPI_FLASH_PROTO_1_1_2; + flash->read_cmd = CMD_READ_DUAL_OUTPUT_FAST; + break; + + case ARRAY_FAST: + flash->read_proto = SPI_FLASH_PROTO_1_1_1; + flash->read_cmd = CMD_READ_ARRAY_FAST; + break; + + case ARRAY_SLOW: + flash->read_proto = SPI_FLASH_PROTO_1_1_1; + flash->read_cmd = CMD_READ_ARRAY_SLOW; + break; + + default: + return -EINVAL; + } + + if (ret) + return ret; + + /* Configure Page Program operations */ + if (flash->write_proto == SPI_FLASH_PROTO_4_4_4 || + flash->write_proto == SPI_FLASH_PROTO_2_2_2) { + flash->write_cmd = CMD_PAGE_PROGRAM; + } else if ((flash->spi->mode & SPI_TX_QUAD) && + (params->flags & WR_QPP)) { + flash->write_cmd = CMD_QUAD_PAGE_PROGRAM; + flash->write_proto = SPI_FLASH_PROTO_1_1_4; + } else { + flash->write_cmd = CMD_PAGE_PROGRAM; + flash->write_proto = SPI_FLASH_PROTO_1_1_1; + } + + /* Set number of dummy cycles for Fast Read operations */ + if (params->e_rd_cmd & (QUAD_CMD_FAST | DUAL_CMD_FAST)) + ret = spi_flash_micron_set_dummy_cycles(flash, dummy_cycles); + return ret ? : spi_flash_set_dummy_byte(flash, dummy_cycles); +} diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 50250491d228..922afc811a0b 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -1022,11 +1022,6 @@ static int set_quad_mode(struct spi_flash *flash, u8 idcode0) case SPI_FLASH_CFI_MFR_WINBOND: return spansion_quad_enable(flash); #endif -#ifdef CONFIG_SPI_FLASH_STMICRO - case SPI_FLASH_CFI_MFR_STMICRO: - debug("SF: QEB is volatile for %02x flash\n", idcode0); - return 0; -#endif default: printf("SF: Need set QEB func for %02x flash\n", idcode0); return -1; @@ -1113,10 +1108,10 @@ static int spi_flash_setup(struct spi_flash *flash, * manufacturer. */ switch (params->jedec >> 16) { - /* - * TODO: Manufacturer dedicated setup will be added HERE by - * further patches. - */ +#ifdef CONFIG_SPI_FLASH_STMICRO + case SPI_FLASH_CFI_MFR_STMICRO: + return spi_flash_setup_micron(flash, params, match); +#endif
default: return spi_flash_setup_deprecated(flash, params, match);