diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 6b896288d1..46694eb479 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -59,6 +59,7 @@ if(CONFIG_FLASH_MCUX_FLEXSPI_XIP) dt_prop(compat_flash PATH ${chosen_flash} PROPERTY compatible) if(compat_flash MATCHES "nxp,imx-flexspi-nor") zephyr_code_relocate(FILES flash_mcux_flexspi_nor.c LOCATION ${CONFIG_FLASH_MCUX_FLEXSPI_XIP_MEM}_TEXT) + zephyr_code_relocate(FILES jesd216.c LOCATION ${CONFIG_FLASH_MCUX_FLEXSPI_XIP_MEM}_TEXT) elseif(compat_flash MATCHES "nxp,imx-flexspi-mx25um51345g") zephyr_code_relocate(FILES flash_mcux_flexspi_mx25um51345g.c LOCATION ${CONFIG_FLASH_MCUX_FLEXSPI_XIP_MEM}_TEXT) elseif(compat_flash MATCHES "nxp,imx-flexspi-hyperflash") diff --git a/drivers/flash/Kconfig.mcux b/drivers/flash/Kconfig.mcux index 89d411f064..ec7f5be22b 100644 --- a/drivers/flash/Kconfig.mcux +++ b/drivers/flash/Kconfig.mcux @@ -40,6 +40,7 @@ config FLASH_MCUX_FLEXSPI_NOR depends on DT_HAS_NXP_IMX_FLEXSPI_NOR_ENABLED select FLASH_HAS_PAGE_LAYOUT select FLASH_HAS_DRIVER_ENABLED + select FLASH_JESD216 select MEMC select MEMC_MCUX_FLEXSPI diff --git a/drivers/flash/flash_mcux_flexspi_nor.c b/drivers/flash/flash_mcux_flexspi_nor.c index 8a9c4f182a..bfcef6daeb 100644 --- a/drivers/flash/flash_mcux_flexspi_nor.c +++ b/drivers/flash/flash_mcux_flexspi_nor.c @@ -6,11 +6,13 @@ #define DT_DRV_COMPAT nxp_imx_flexspi_nor +#include #include #include #include #include #include "spi_nor.h" +#include "jesd216.h" #include "memc_mcux_flexspi.h" #ifdef CONFIG_HAS_MCUX_CACHE @@ -40,22 +42,21 @@ static uint8_t nor_write_buf[SPI_NOR_PAGE_SIZE]; LOG_MODULE_REGISTER(flash_flexspi_nor, CONFIG_FLASH_LOG_LEVEL); enum { - /* Instructions matching with XIP layout */ - READ_FAST_QUAD_OUTPUT, - READ_FAST_OUTPUT, - READ_NORMAL_OUTPUT, + READ, + PAGE_PROGRAM, READ_STATUS, WRITE_ENABLE, ERASE_SECTOR, ERASE_BLOCK, - PAGE_PROGRAM_INPUT, - PAGE_PROGRAM_QUAD_INPUT, READ_ID, - WRITE_STATUS_REG, - ENTER_QPI, - EXIT_QPI, READ_STATUS_REG, ERASE_CHIP, + READ_JESD216, + /* Used for temporary commands during initialization */ + SCRATCH_CMD, + SCRATCH_CMD2, + /* Must be last entry */ + FLEXSPI_INSTR_END, }; struct flash_flexspi_nor_config { @@ -71,98 +72,70 @@ struct flash_flexspi_nor_data { struct device controller; flexspi_device_config_t config; flexspi_port_t port; + bool legacy_poll; struct flash_pages_layout layout; struct flash_parameters flash_parameters; }; -static const uint32_t flash_flexspi_nor_lut[][4] = { - [READ_ID] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDID, - kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), +/* Initial LUT table */ +static const uint32_t flash_flexspi_nor_base_lut[][MEMC_FLEXSPI_CMD_PER_SEQ] = { + /* 1S-1S-1S flash read command, should be compatible with all SPI nor flashes */ + [READ] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_READ, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0), }, - - [READ_STATUS_REG] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, - kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), + [READ_JESD216] = { + /* Install read SFDP command */ + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, JESD216_CMD_READ_SFDP, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 24), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 8, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x4), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0), }, - - [WRITE_STATUS_REG] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WRSR, - kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04), + /* Standard 1S-1S-1S flash write command, can be switched to 1S-1S-4S when QE is set */ + [PAGE_PROGRAM] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_PP, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), }, [WRITE_ENABLE] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WREN, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WREN, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), }, [ERASE_SECTOR] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_SE, + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_SE, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), }, [ERASE_BLOCK] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_BE, + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_BE, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), }, [ERASE_CHIP] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_CE, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_CE, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), }, - [READ_FAST_QUAD_OUTPUT] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x6B, - kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x08, - kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04), + [READ_ID] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDID, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x01), }, - [READ_FAST_OUTPUT] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, - kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, - kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), - }, - - [READ_NORMAL_OUTPUT] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_READ, - kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), - }, - - [READ_STATUS] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, - kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04), - }, - - [PAGE_PROGRAM_INPUT] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_PP, - kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), - }, - - [PAGE_PROGRAM_QUAD_INPUT] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, - kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18), - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), - }, - - [ENTER_QPI] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), - }, - - [EXIT_QPI] = { - FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, - kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0), + [READ_STATUS_REG] = { + FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x01), }, }; -static int flash_flexspi_nor_get_vendor_id(struct flash_flexspi_nor_data *data, +/* Helper so we can read flash ID without flash access for XIP */ +static int flash_flexspi_nor_read_id_helper(struct flash_flexspi_nor_data *data, uint8_t *vendor_id) { uint32_t buffer = 0; @@ -175,17 +148,28 @@ static int flash_flexspi_nor_get_vendor_id(struct flash_flexspi_nor_data *data, .SeqNumber = 1, .seqIndex = READ_ID, .data = &buffer, - .dataSize = 1, + .dataSize = 3, }; LOG_DBG("Reading id"); ret = memc_flexspi_transfer(&data->controller, &transfer); - *vendor_id = buffer; + if (ret < 0) { + return ret; + } + + memcpy(vendor_id, &buffer, 3); return ret; } +static int flash_flexspi_nor_read_id(const struct device *dev, uint8_t *vendor_id) +{ + struct flash_flexspi_nor_data *data = dev->data; + + return flash_flexspi_nor_read_id_helper(data, vendor_id); +} + static int flash_flexspi_nor_read_status(struct flash_flexspi_nor_data *data, uint32_t *status) { @@ -204,24 +188,6 @@ static int flash_flexspi_nor_read_status(struct flash_flexspi_nor_data *data, return memc_flexspi_transfer(&data->controller, &transfer); } -static int flash_flexspi_nor_write_status(struct flash_flexspi_nor_data *data, - uint32_t *status) -{ - flexspi_transfer_t transfer = { - .deviceAddress = 0, - .port = data->port, - .cmdType = kFLEXSPI_Write, - .SeqNumber = 1, - .seqIndex = WRITE_STATUS_REG, - .data = status, - .dataSize = 1, - }; - - LOG_DBG("Writing status register"); - - return memc_flexspi_transfer(&data->controller, &transfer); -} - static int flash_flexspi_nor_write_enable(struct flash_flexspi_nor_data *data) { flexspi_transfer_t transfer = { @@ -300,7 +266,7 @@ static int flash_flexspi_nor_page_program(struct flash_flexspi_nor_data *data, .port = data->port, .cmdType = kFLEXSPI_Write, .SeqNumber = 1, - .seqIndex = PAGE_PROGRAM_QUAD_INPUT, + .seqIndex = PAGE_PROGRAM, .data = (uint32_t *) buffer, .dataSize = len, }; @@ -315,25 +281,24 @@ static int flash_flexspi_nor_wait_bus_busy(struct flash_flexspi_nor_data *data) uint32_t status = 0; int ret; - do { + while (1) { ret = flash_flexspi_nor_read_status(data, &status); LOG_DBG("status: 0x%x", status); if (ret) { LOG_ERR("Could not read status"); return ret; } - } while (status & BIT(0)); - return 0; -} - -static int flash_flexspi_nor_enable_quad_mode(struct flash_flexspi_nor_data *data) -{ - uint32_t status = 0x40; - - flash_flexspi_nor_write_status(data, &status); - flash_flexspi_nor_wait_bus_busy(data); - memc_flexspi_reset(&data->controller); + if (data->legacy_poll) { + if ((status & BIT(0)) == 0) { + break; + } + } else { + if (status & BIT(7)) { + break; + } + } + } return 0; } @@ -494,11 +459,583 @@ static void flash_flexspi_nor_pages_layout(const struct device *dev, } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +/* + * This function enables quad mode, when supported. Otherwise it + * returns an error. + * @param dev: Flexspi device + * @param flexspi_lut: flexspi lut table, useful if instruction writes are needed + * @param qer: DW15 quad enable parameter + * @return 0 if quad mode was entered, or -ENOTSUP if quad mode is not supported + */ +static int flash_flexspi_nor_quad_enable(struct flash_flexspi_nor_data *data, + uint32_t (*flexspi_lut)[MEMC_FLEXSPI_CMD_PER_SEQ], + uint8_t qer) +{ + int ret; + uint32_t buffer = 0; + uint16_t bit = 0; + uint8_t rd_size, wr_size; + flexspi_transfer_t transfer = { + .deviceAddress = 0, + .port = data->port, + .SeqNumber = 1, + .data = &buffer, + }; + flexspi_device_config_t config = { + .flexspiRootClk = MHZ(50), + .flashSize = FLEXSPI_FLSHCR0_FLSHSZ_MASK, /* Max flash size */ + .ARDSeqNumber = 1, + .ARDSeqIndex = READ, + }; + + switch (qer) { + case JESD216_DW15_QER_VAL_NONE: + /* No init needed */ + return 0; + case JESD216_DW15_QER_VAL_S2B1v1: + case JESD216_DW15_QER_VAL_S2B1v4: + /* Install read and write status command */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WRSR, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + + /* Set bit 1 of status register 2 */ + bit = BIT(9); + rd_size = 2; + wr_size = 2; + break; + case JESD216_DW15_QER_VAL_S1B6: + /* Install read and write status command */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WRSR, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + + /* Set bit 6 of status register 1 */ + bit = BIT(6); + rd_size = 1; + wr_size = 1; + break; + case JESD216_DW15_QER_VAL_S2B7: + /* Install read and write status command */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x3F, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x3E, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + + /* Set bit 7 of status register 2 */ + bit = BIT(7); + rd_size = 1; + wr_size = 1; + break; + case JESD216_DW15_QER_VAL_S2B1v5: + /* Install read and write status command */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR2, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WRSR, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + + /* Set bit 1 of status register 2 */ + bit = BIT(9); + rd_size = 1; + wr_size = 2; + break; + case JESD216_DW15_QER_VAL_S2B1v6: + /* Install read and write status command */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR2, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_WRSR2, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + + /* Set bit 7 of status register 2 */ + bit = BIT(7); + rd_size = 1; + wr_size = 1; + break; + default: + return -ENOTSUP; + } + ret = memc_flexspi_set_device_config(&data->controller, + &config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + return ret; + } + transfer.dataSize = rd_size; + transfer.seqIndex = SCRATCH_CMD; + transfer.cmdType = kFLEXSPI_Read; + /* Read status register */ + ret = memc_flexspi_transfer(&data->controller, &transfer); + if (ret < 0) { + return ret; + } + buffer |= bit; + transfer.dataSize = wr_size; + transfer.seqIndex = SCRATCH_CMD2; + transfer.cmdType = kFLEXSPI_Write; + return memc_flexspi_transfer(&data->controller, &transfer); +} + +/* + * This function enables 4 byte addressing, when supported. Otherwise it + * returns an error. + * @param dev: Flexspi device + * @param flexspi_lut: flexspi lut table, useful if instruction writes are needed + * @param en4b: DW16 enable 4 byte mode parameter + * @return 0 if 4 byte mode was entered, or -ENOTSUP if 4 byte mode was not supported + */ +static int flash_flexspi_nor_4byte_enable(struct flash_flexspi_nor_data *data, + uint32_t (*flexspi_lut)[MEMC_FLEXSPI_CMD_PER_SEQ], + uint32_t en4b) +{ + int ret; + uint32_t buffer = 0; + flexspi_transfer_t transfer = { + .deviceAddress = 0, + .port = data->port, + .SeqNumber = 1, + .data = &buffer, + }; + flexspi_device_config_t config = { + .flexspiRootClk = MHZ(50), + .flashSize = FLEXSPI_FLSHCR0_FLSHSZ_MASK, /* Max flash size */ + .ARDSeqNumber = 1, + .ARDSeqIndex = READ, + }; + if (en4b & BIT(6)) { + /* Flash is always in 4 byte mode. We just need to configure LUT */ + return 0; + } else if (en4b & BIT(5)) { + /* Dedicated vendor instruction set, which we don't support. Exit here */ + return -ENOTSUP; + } else if (en4b & BIT(4)) { + /* Set bit 0 of 16 bit configuration register */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xB5, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x1); + flexspi_lut[SCRATCH_CMD2][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xB1, + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x1); + ret = memc_flexspi_set_device_config(&data->controller, + &config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + return ret; + } + transfer.dataSize = 2; + transfer.seqIndex = SCRATCH_CMD; + transfer.cmdType = kFLEXSPI_Read; + /* Read config register */ + ret = memc_flexspi_transfer(&data->controller, &transfer); + if (ret < 0) { + return ret; + } + buffer |= BIT(0); + /* Set config register */ + transfer.seqIndex = SCRATCH_CMD2; + transfer.cmdType = kFLEXSPI_Read; + return memc_flexspi_transfer(&data->controller, &transfer); + } else if (en4b & BIT(1)) { + /* Issue write enable, then instruction 0xB7 */ + flash_flexspi_nor_write_enable(data); + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xB7, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + ret = memc_flexspi_set_device_config(&data->controller, + &config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + return ret; + } + transfer.dataSize = 0; + transfer.seqIndex = SCRATCH_CMD; + transfer.cmdType = kFLEXSPI_Command; + return memc_flexspi_transfer(&data->controller, &transfer); + } else if (en4b & BIT(0)) { + /* Issue instruction 0xB7 */ + flexspi_lut[SCRATCH_CMD][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xB7, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + ret = memc_flexspi_set_device_config(&data->controller, + &config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + return ret; + } + transfer.dataSize = 0; + transfer.seqIndex = SCRATCH_CMD; + transfer.cmdType = kFLEXSPI_Command; + return memc_flexspi_transfer(&data->controller, &transfer); + } + /* Other methods not supported */ + return -ENOTSUP; +} + +/* + * This function configures the FlexSPI to manage the flash device + * based on values in SFDP header + * @param data: Flexspi device data + * @param header: SFDP header for flash + * @param bfp: basic flash parameters for flash + * @param flexspi_lut: LUT table, filled with READ LUT command + * @return 0 on success, or negative value on error + */ +static int flash_flexspi_nor_config_flash(struct flash_flexspi_nor_data *data, + struct jesd216_sfdp_header *header, + struct jesd216_bfp *bfp, + uint32_t (*flexspi_lut)[MEMC_FLEXSPI_CMD_PER_SEQ]) +{ + struct jesd216_instr instr; + struct jesd216_bfp_dw16 dw16; + struct jesd216_bfp_dw15 dw15; + struct jesd216_bfp_dw14 dw14; + uint8_t addr_width; + uint8_t mode_cmd; + int ret; + + addr_width = jesd216_bfp_addrbytes(bfp) == + JESD216_SFDP_BFP_DW1_ADDRBYTES_VAL_4B ? 32 : 24; + + /* Check to see if we can enable 4 byte addressing */ + ret = jesd216_bfp_decode_dw16(&header->phdr[0], bfp, &dw16); + if (ret < 0) { + return ret; + } + + /* Attempt to enable 4 byte addressing */ + ret = flash_flexspi_nor_4byte_enable(data, flexspi_lut, dw16.enter_4ba); + if (ret == 0) { + /* Use 4 byte address width */ + addr_width = 32; + /* Update LUT for ERASE_SECTOR and ERASE_BLOCK to use 32 bit addr */ + flexspi_lut[ERASE_SECTOR][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_SE, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, addr_width); + flexspi_lut[ERASE_BLOCK][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_BE, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, addr_width); + } + /* Extract the read command. + * Note- enhanced XIP not currently supported, nor is 4-4-4 mode. + */ + if (jesd216_bfp_read_support(&header->phdr[0], bfp, + JESD216_MODE_144, &instr) > 0) { + LOG_DBG("Enable 144 mode"); + /* Configure for 144 QUAD read mode */ + if (instr.mode_clocks == 2) { + mode_cmd = kFLEXSPI_Command_MODE8_SDR; + } else if (instr.mode_clocks == 1) { + mode_cmd = kFLEXSPI_Command_MODE4_SDR; + } else if (instr.mode_clocks == 0) { + /* Just send dummy cycles during mode clock period */ + mode_cmd = kFLEXSPI_Command_DUMMY_SDR; + } else { + return -ENOTSUP; + } + flexspi_lut[READ][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, instr.instr, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, addr_width); + /* Note- we always set mode bits to 0x0 */ + flexspi_lut[READ][1] = FLEXSPI_LUT_SEQ( + mode_cmd, kFLEXSPI_4PAD, 0x00, + kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, instr.wait_states); + flexspi_lut[READ][2] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + /* Read 1S-4S-4S enable method */ + ret = jesd216_bfp_decode_dw15(&header->phdr[0], bfp, &dw15); + if (ret < 0) { + return ret; + } + ret = flash_flexspi_nor_quad_enable(data, flexspi_lut, dw15.qer); + if (ret < 0) { + return ret; + } + /* Now, install 1S-1S-4S page program command */ + flexspi_lut[PAGE_PROGRAM][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_PP_1_1_4, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, addr_width); + flexspi_lut[PAGE_PROGRAM][1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x4, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + + } else if (jesd216_bfp_read_support(&header->phdr[0], bfp, + JESD216_MODE_122, &instr) > 0) { + LOG_DBG("Enable 122 mode"); + if (instr.mode_clocks == 4) { + mode_cmd = kFLEXSPI_Command_MODE8_SDR; + } else if (instr.mode_clocks == 2) { + mode_cmd = kFLEXSPI_Command_MODE4_SDR; + } else if (instr.mode_clocks == 1) { + mode_cmd = kFLEXSPI_Command_MODE2_SDR; + } else if (instr.mode_clocks == 0) { + /* Just send dummy cycles during mode clock period */ + mode_cmd = kFLEXSPI_Command_DUMMY_SDR; + } else { + return -ENOTSUP; + } + flexspi_lut[READ][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, instr.instr, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_2PAD, addr_width); + /* Note- we always set mode bits to 0x0 */ + flexspi_lut[READ][1] = FLEXSPI_LUT_SEQ( + mode_cmd, kFLEXSPI_2PAD, 0x0, + kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_2PAD, instr.wait_states); + flexspi_lut[READ][2] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_READ_SDR, kFLEXSPI_2PAD, 0x02, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + /* Now, install 1S-1S-2S page program command */ + flexspi_lut[PAGE_PROGRAM][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_PP_1_1_2, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, addr_width); + flexspi_lut[PAGE_PROGRAM][1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_2PAD, 0x4, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + } + /* Default to 111 mode if no support exists, leave READ/WRITE untouched */ + + /* Now, read DW14 to determine the polling method we should use while programming */ + ret = jesd216_bfp_decode_dw14(&header->phdr[0], bfp, &dw14); + if (ret < 0) { + return ret; + } + if (dw14.poll_options & BIT(1)) { + /* Read instruction used for polling is 0x70 */ + data->legacy_poll = false; + flexspi_lut[READ_STATUS_REG][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x70, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x01); + } else { + /* Read instruction used for polling is 0x05 */ + data->legacy_poll = true; + flexspi_lut[READ_STATUS_REG][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x01); + } + + return 0; +} + +/* Helper so we can avoid flash access while performing SFDP probe */ +static int flash_flexspi_nor_sfdp_read_helper(struct flash_flexspi_nor_data *dev_data, + off_t offset, void *data, size_t len) +{ + flexspi_transfer_t transfer = { + .deviceAddress = offset, + .port = dev_data->port, + .cmdType = kFLEXSPI_Read, + .seqIndex = READ_JESD216, + .SeqNumber = 1, + .data = (uint32_t *)data, + .dataSize = len, + }; + + /* Get SFDP data */ + return memc_flexspi_transfer(&dev_data->controller, &transfer); +} + + +#if defined(CONFIG_FLASH_JESD216_API) + +static int flash_flexspi_nor_sfdp_read(const struct device *dev, + off_t offset, void *data, size_t len) +{ + struct flash_flexspi_nor_data *dev_data = dev->data; + + return flash_flexspi_nor_sfdp_read_helper(dev_data, offset, data, len); +} + +#endif + +/* Checks JEDEC ID of flash. If supported, installs custom LUT table */ +static int flash_flexspi_nor_check_jedec(struct flash_flexspi_nor_data *data, + uint32_t (*flexspi_lut)[MEMC_FLEXSPI_CMD_PER_SEQ]) +{ + int ret; + uint32_t vendor_id; + + ret = flash_flexspi_nor_read_id_helper(data, (uint8_t *)&vendor_id); + if (ret < 0) { + return ret; + } + + /* Switch on manufacturer and vendor ID */ + switch (vendor_id & 0xFFFF) { + case 0x25C2: + /* MX25 flash, use 4 byte read/write */ + flexspi_lut[READ][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_4READ_4B, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 32); + /* Flash needs 10 dummy cycles */ + flexspi_lut[READ][1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 10, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04); + /* Only 1S-4S-4S page program supported */ + flexspi_lut[PAGE_PROGRAM][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_PP_1_4_4_4B, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 32); + flexspi_lut[PAGE_PROGRAM][1] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x4, + kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x0); + /* Update ERASE commands for 4 byte mode */ + flexspi_lut[ERASE_SECTOR][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_SE_4B, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 32); + flexspi_lut[ERASE_BLOCK][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xDC, + kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 32), + /* Read instruction used for polling is 0x05 */ + data->legacy_poll = true; + flexspi_lut[READ_STATUS_REG][0] = FLEXSPI_LUT_SEQ( + kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, SPI_NOR_CMD_RDSR, + kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x01); + /* Device uses bit 6 of status reg 1 for QE */ + return flash_flexspi_nor_quad_enable(data, flexspi_lut, JESD216_DW15_QER_VAL_S1B6); + default: + return -ENOTSUP; + } +} + +/* Probe parameters from flash SFDP header, and use them to configure the FlexSPI */ +static int flash_flexspi_nor_probe(struct flash_flexspi_nor_data *data) +{ + uint32_t flexspi_lut[FLEXSPI_INSTR_END][MEMC_FLEXSPI_CMD_PER_SEQ] = {0}; + /* JESD216B defines up to 23 basic flash parameters */ + uint32_t param_buf[23]; + /* Space to store SFDP header and first parameter header */ + uint8_t sfdp_buf[JESD216_SFDP_SIZE(1)] __aligned(4); + struct jesd216_bfp *bfp = (struct jesd216_bfp *)param_buf; + struct jesd216_sfdp_header *header = (struct jesd216_sfdp_header *)sfdp_buf; + int ret; + unsigned int key = 0U; + + flexspi_device_config_t config = { + .flexspiRootClk = MHZ(50), + .flashSize = FLEXSPI_FLSHCR0_FLSHSZ_MASK, /* Max flash size */ + .ARDSeqNumber = 1, + .ARDSeqIndex = READ, + }; + + if (memc_flexspi_is_running_xip(&data->controller)) { + /* + * ==== ENTER CRITICAL SECTION ==== + * No flash access should be performed in critical section. All + * code and data accessed must reside in ram. + */ + key = irq_lock(); + memc_flexspi_wait_bus_idle(&data->controller); + } + + /* SFDP spec requires that we downclock the FlexSPI to 50MHz or less */ + ret = memc_flexspi_update_clock(&data->controller, &config, + data->port, MHZ(50)); + if (ret < 0) { + goto _exit; + } + + /* Setup initial LUT table and FlexSPI configuration */ + memcpy(flexspi_lut, flash_flexspi_nor_base_lut, sizeof(flash_flexspi_nor_base_lut)); + + ret = memc_flexspi_set_device_config(&data->controller, &config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + goto _exit; + } + + /* First, check if the JEDEC ID of this flash has explicit support + * in this driver + */ + ret = flash_flexspi_nor_check_jedec(data, flexspi_lut); + if (ret == 0) { + /* Flash was supported, SFDP probe not needed */ + goto _program_lut; + } + + ret = flash_flexspi_nor_sfdp_read_helper(data, 0, sfdp_buf, sizeof(sfdp_buf)); + if (ret < 0) { + goto _exit; + } + + LOG_DBG("SFDP header magic: 0x%x", header->magic); + if (jesd216_sfdp_magic(header) != JESD216_SFDP_MAGIC) { + /* Header was read incorrectly */ + LOG_WRN("Invalid header, using legacy SPI mode"); + data->legacy_poll = true; + goto _program_lut; + } + + if (header->phdr[0].len_dw > ARRAY_SIZE(param_buf)) { + /* Not enough space to read parameter table */ + ret = -ENOBUFS; + goto _exit; + } + + /* Read basic flash parameter table */ + ret = flash_flexspi_nor_sfdp_read_helper(data, + jesd216_param_addr(&header->phdr[0]), + param_buf, + sizeof(uint32_t) * header->phdr[0].len_dw); + if (ret < 0) { + goto _exit; + } + + /* Configure flash */ + ret = flash_flexspi_nor_config_flash(data, header, bfp, flexspi_lut); + if (ret < 0) { + goto _exit; + } + +_program_lut: + /* + * Update the FlexSPI with the config structure provided + * from devicetree and the configured LUT + */ + ret = memc_flexspi_set_device_config(&data->controller, &data->config, + (uint32_t *)flexspi_lut, + FLEXSPI_INSTR_END * MEMC_FLEXSPI_CMD_PER_SEQ, + data->port); + if (ret < 0) { + return ret; + } + +_exit: + memc_flexspi_reset(&data->controller); + + if (memc_flexspi_is_running_xip(&data->controller)) { + /* ==== EXIT CRITICAL SECTION ==== */ + irq_unlock(key); + } + + return ret; +} + static int flash_flexspi_nor_init(const struct device *dev) { const struct flash_flexspi_nor_config *config = dev->config; struct flash_flexspi_nor_data *data = dev->data; - uint8_t vendor_id; + uint32_t vendor_id; /* First step- use ROM pointer to controller device to create * a copy of the device structure in RAM we can use while in @@ -511,31 +1048,36 @@ static int flash_flexspi_nor_init(const struct device *dev) return -ENODEV; } - if (memc_flexspi_is_running_xip(&data->controller)) { - /* Wait for bus idle before configuring */ - memc_flexspi_wait_bus_idle(&data->controller); + if (flash_flexspi_nor_probe(data)) { + if (memc_flexspi_is_running_xip(&data->controller)) { + /* We can't continue from here- the LUT stored in + * the FlexSPI will be invalid so we cannot XIP. + * Instead, spin here + */ + while (1) { + /* Spin */ + } + } + LOG_ERR("SFDP probe failed"); + return -EIO; } - if (memc_flexspi_set_device_config(&data->controller, &data->config, - (const uint32_t *)flash_flexspi_nor_lut, - sizeof(flash_flexspi_nor_lut) / MEMC_FLEXSPI_CMD_SIZE, - data->port)) { - LOG_ERR("Could not set device configuration"); - return -EINVAL; + + /* Set the FlexSPI to full clock speed */ + if (memc_flexspi_update_clock(&data->controller, &data->config, + data->port, data->config.flexspiRootClk)) { + LOG_ERR("Could not set flexspi clock speed"); + return -ENOTSUP; } + memc_flexspi_reset(&data->controller); - if (flash_flexspi_nor_get_vendor_id(data, &vendor_id)) { + if (flash_flexspi_nor_read_id(dev, (uint8_t *)&vendor_id)) { LOG_ERR("Could not read vendor id"); return -EIO; } LOG_DBG("Vendor id: 0x%0x", vendor_id); - if (flash_flexspi_nor_enable_quad_mode(data)) { - LOG_ERR("Could not enable quad mode"); - return -EIO; - } - return 0; } @@ -547,6 +1089,10 @@ static const struct flash_driver_api flash_flexspi_nor_api = { #if defined(CONFIG_FLASH_PAGE_LAYOUT) .page_layout = flash_flexspi_nor_pages_layout, #endif +#if defined(CONFIG_FLASH_JESD216_API) + .sfdp_read = flash_flexspi_nor_sfdp_read, + .read_jedec_id = flash_flexspi_nor_read_id, +#endif }; #define CONCAT3(x, y, z) x ## y ## z @@ -559,7 +1105,7 @@ static const struct flash_driver_api flash_flexspi_nor_api = { #define FLASH_FLEXSPI_DEVICE_CONFIG(n) \ { \ - .flexspiRootClk = MHZ(120), \ + .flexspiRootClk = DT_INST_PROP(n, spi_max_frequency), \ .flashSize = DT_INST_PROP(n, size) / 8 / KB(1), \ .CSIntervalUnit = \ CS_INTERVAL_UNIT( \ @@ -572,7 +1118,7 @@ static const struct flash_driver_api flash_flexspi_nor_api = { .enableWordAddress = DT_INST_PROP(n, word_addressable), \ .AWRSeqIndex = 0, \ .AWRSeqNumber = 0, \ - .ARDSeqIndex = READ_FAST_QUAD_OUTPUT, \ + .ARDSeqIndex = READ, \ .ARDSeqNumber = 1, \ .AHBWriteWaitUnit = \ AHB_WRITE_WAIT_UNIT( \ diff --git a/drivers/memc/memc_mcux_flexspi.h b/drivers/memc/memc_mcux_flexspi.h index ee9fa46365..707d9f088c 100644 --- a/drivers/memc/memc_mcux_flexspi.h +++ b/drivers/memc/memc_mcux_flexspi.h @@ -10,6 +10,8 @@ /* Size of a command in the LUT table */ #define MEMC_FLEXSPI_CMD_SIZE 4U +/* Number of commands in an instruction sequence */ +#define MEMC_FLEXSPI_CMD_PER_SEQ 4U /** * @brief Wait for the FlexSPI bus to be idle