core/flash/spi_flash_sfdp.c (601 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stddef.h> #include <stdlib.h> #include <string.h> #include "flash_common.h" #include "platform_io_api.h" #include "spi_flash_sfdp.h" #include "common/common_math.h" #include "common/unused.h" /** * The expected signature in the SFDP header. */ #define SPI_FLASH_SFDP_SIGNATURE 0x50444653 /** * Get the parameter ID from a table header. */ #define SPI_FLASH_SFDP_PARAMETER_ID(x) ((x.id_msb << 8) | x.id_lsb) /** * Get the starting address of a parameter table. */ #define SPI_FLASH_SFDP_PARAMETER_PTR(x) \ ((x.table_pointer[2] << 16) | (x.table_pointer[1] << 8) | x.table_pointer[0]) #pragma pack(push,1) /** * SFDP basic flash parameter table format version 1.0. */ struct spi_flash_sfdp_basic_parameter_table_1_0 { uint8_t write_attr; /**< 1st DWORD: Write attributes. */ uint8_t erase_4kb; /**< 1st DWORD: 4kB erase instruction. */ uint8_t dspi_qspi; /**< 1st DWORD: DSPI and QSPI support flags. */ #define SPI_FLASH_SFDP_SUPPORTS_1_1_2 (1U << 0) #define SPI_FLASH_SFDP_ADDRESS_BYTES (3U << 1) #define SPI_FLASH_SFDP_3BYTE_ONLY (0) #define SPI_FLASH_SFDP_3BYTE_4BYTE (1U << 1) #define SPI_FLASH_SFDP_4BYTE_ONLY (1U << 2) #define SPI_FLASH_SFDP_SUPPORTS_DTR (1U << 3) #define SPI_FLASH_SFDP_SUPPORTS_1_2_2 (1U << 4) #define SPI_FLASH_SFDP_SUPPORTS_1_4_4 (1U << 5) #define SPI_FLASH_SFDP_SUPPORTS_1_1_4 (1U << 6) uint8_t unused1; /**< 1st DWORD: Unused. */ uint32_t memory_density; /**< 2nd DWORD: Size of the flash device. */ #define SPI_FLASH_SFDP_4GB_DENSITY (1U << 31) uint8_t dummy_1_4_4; /**< 3rd DWORD: 1-4-4 dummy clocks. */ #define SPI_FLASH_SFDP_MODE_CLKS_MASK 0x7 #define SPI_FLASH_SFDP_MODE_CLKS_SHIFT 5 #define SPI_FLASH_SFDP_MODE_CLKS(\ x) (((x) >> SPI_FLASH_SFDP_MODE_CLKS_SHIFT) & SPI_FLASH_SFDP_MODE_CLKS_MASK) #define SPI_FLASH_SFDP_DUMMY_CLKS_MASK 0x1f #define SPI_FLASH_SFDP_DUMMY_CLKS_SHIFT 0 #define SPI_FLASH_SFDP_DUMMY_CLKS(\ x) (((x) >> SPI_FLASH_SFDP_DUMMY_CLKS_SHIFT) & SPI_FLASH_SFDP_DUMMY_CLKS_MASK) uint8_t opcode_1_4_4; /**< 3rd DWORD: 1-4-4 command opcode. */ uint8_t dummy_1_1_4; /**< 3rd DWORD: 1-1-4 dummy clocks. */ uint8_t opcode_1_1_4; /**< 3rd DWORD: 1-1-4 command opcode. */ uint8_t dummy_1_1_2; /**< 4th DWORD: 1-1-2 dummy clocks. */ uint8_t opcode_1_1_2; /**< 4th DWORD: 1-1-2 command opcode. */ uint8_t dummy_1_2_2; /**< 4th DWORD: 1-2-2 dummy clocks. */ uint8_t opcode_1_2_2; /**< 4th DWORD: 1-2-2 command opcode. */ uint32_t dpi_qpi; /**< 5th DWORD: DPI and QPI support flags. */ #define SPI_FLASH_SFDP_SUPPORTS_2_2_2 (1U << 0) #define SPI_FLASH_SFDP_SUPPORTS_4_4_4 (1U << 4) uint16_t unused6; /**< 6th DWORD: Unused. */ uint8_t dummy_2_2_2; /**< 6th DWORD: 2-2-2 dummy clocks. */ uint8_t opcode_2_2_2; /**< 6th DWORD: 2-2-2 command opcode. */ uint16_t unused7; /**< 7th DWORD: Unused. */ uint8_t dummy_4_4_4; /**< 7th DWORD: 4-4-4 dummy clocks. */ uint8_t opcode_4_4_4; /**< 7th DWORD: 4-4-4 command opcode. */ uint8_t erase1_size; /**< 8th DWORD: Erase 1 block size. */ uint8_t erase1; /**< 8th DWORD: Erase 1 instruction. */ uint8_t erase2_size; /**< 8th DWORD: Erase 2 block size. */ uint8_t erase2; /**< 8th DWORD: Erase 2 instruction. */ uint8_t erase3_size; /**< 9th DWORD: Erase 3 block size. */ uint8_t erase3; /**< 9th DWORD: Erase 3 instruction. */ uint8_t erase4_size; /**< 9th DWORD: Erase 4 block size. */ uint8_t erase4; /**< 9th DWORD: Erase 4 instruction. */ }; /** * SFDP basic flash parameter table format version 1.5. */ struct spi_flash_sfdp_basic_parameter_table_1_5 { struct spi_flash_sfdp_basic_parameter_table_1_0 table_1_0; uint32_t erase_time; /**< 10th DWORD: Erase typical timing. */ uint8_t page_size; /**< 11th DWORD: Page size. */ #define SPI_FLASH_SFDP_PAGE_SIZE(x) (((x) & 0xf0) >> 4) uint16_t program_time; /**< 11th DWORD: Page programming typical timing. */ uint8_t chip_erase_time; /**< 11th DWORD: Chip erase typical timing. */ uint32_t suspend_attr; /**< 12th DWORD: Suspend/Resume attributes. */ uint8_t program_resume; /**< 13th DWORD: Program Resume instruction. */ uint8_t program_suspend; /**< 13th DWORD: Program Suspend instruction. */ uint8_t resume; /**< 13th DWORD: Resume instruction. */ uint8_t suspend; /**< 13th DWORD: Suspend instruction. */ uint8_t status_reg; /**< 14th DWORD: Status register polling device busy. */ #define SPI_FLASH_SFDP_BUSY_SR_WIP (1U << 2) #define SPI_FLASH_SFDP_BUSY_SR_FLAG (1U << 3) uint8_t deep_powerdown[3]; /**< 14th DWORD: Deep power down attributes. */ #define SPI_FLASH_SFDP_PWRDWN_NO_SUPPORT(x) ((x)[2] & (1U << 7)) #define SPI_FLASH_SFDP_PWRDWN_ENTER(x) ((((x)[2] & 0x7f) << 1) | (((x)[1] & 0x80) >> 7)) #define SPI_FLASH_SFDP_PWRDWN_EXIT(x) ((((x)[1] & 0x7f) << 1) | (((x)[0] & 0x80) >> 7)) uint32_t quad_enable; /**< 15th DWORD: Quad enable sequences. */ #define SPI_FLASH_SFDP_QER_MASK 0x7 #define SPI_FLASH_SFDP_QER_SHIFT 20 #define SPI_FLASH_SFDP_QER(\ x) (((x) >> SPI_FLASH_SFDP_QER_SHIFT) & SPI_FLASH_SFDP_QER_MASK) #define SPI_FLASH_SFDP_QER_NO_QUAD_ENABLE 0 #define SPI_FLASH_SFDP_QER_BIT1_SR2 1 #define SPI_FLASH_SFDP_QER_BIT6_SR1 2 #define SPI_FLASH_SFDP_QER_BIT7_SR2_3E 3 #define SPI_FLASH_SFDP_QER_BIT1_SR2_NO_CLR 4 #define SPI_FLASH_SFDP_QER_BIT1_SR2_35 5 #define SPI_FLASH_SFDP_QER_RESERVED1 6 #define SPI_FLASH_SFDP_QER_RESERVED2 7 #define SPI_FLASH_SFDP_HOLD_RST_DISABLE (1U << 23) uint8_t sr_write_enable; /**< 16th DWORD: Status register 1 write enable. */ #define SPI_FLASH_SFDP_NV_SR_06 (1U << 0) #define SPI_FLASH_SFDP_VOLATILE_SR_06 (1U << 1) #define SPI_FLASH_SFDP_VOLATILE_SR_50 (1U << 2) #define SPI_FLASH_SFDP_NV_V_SR_06_50 (1U << 3) #define SPI_FLASH_SFDP_NV_V_SR_06 (1U << 4) #define SPI_FLASH_SFDP_SR_WE_RESERVED (0xe0) uint16_t reset_exit_4b; /**< 16th DWORD: Soft reset and Exit 4-byte address mode. */ #define SPI_FLASH_SFDP_RST_F0 (1U << 3) #define SPI_FLASH_SFDP_RST_66_99 (1U << 4) #define SPI_FLASH_SFDP_4B_EXIT_E9 (1U << 6) #define SPI_FLASH_SFDP_4B_EXIT_06_E9 (1U << 7) #define SPI_FLASH_SFDP_4B_EXIT_EAR_C5 (1U << 8) #define SPI_FLASH_SFDP_4B_EXIT_BANK_REG (1U << 9) #define SPI_FLASH_SFDP_4B_EXIT_NV_CFG (1U << 10) #define SPI_FLASH_SFDP_4B_EXIT_HW_RESET (1U << 11) #define SPI_FLASH_SFDP_4B_EXIT_SW_RESET (1U << 12) #define SPI_FLASH_SFDP_4B_EXIT_AC_RESET (1U << 13) uint8_t enter_4b; /**< 16th DWORD: Enter 4-byte address mode. */ #define SPI_FLASH_SFDP_4B_ENTER_B7 (1U << 0) #define SPI_FLASH_SFDP_4B_ENTER_06_B7 (1U << 1) #define SPI_FLASH_SFDP_4B_ENTER_EAR_C5 (1U << 2) #define SPI_FLASH_SFDP_4B_ENTER_BANK_REG (1U << 3) #define SPI_FLASH_SFDP_4B_ENTER_NV_CFG (1U << 4) #define SPI_FLASH_SFDP_4B_OPCODES (1U << 5) #define SPI_FLASH_SFDP_ALWAYS_4B (1U << 6) }; #pragma pack(pop) /** * Flag to check if SFDP table reports support for exit from 4byte adress mode. */ #define SPI_FLASH_SFDP_SUPPORTS_4B_EXIT (SPI_FLASH_SFDP_4B_EXIT_E9 | \ SPI_FLASH_SFDP_4B_EXIT_06_E9 | SPI_FLASH_SFDP_4B_EXIT_EAR_C5 | \ SPI_FLASH_SFDP_4B_EXIT_BANK_REG | SPI_FLASH_SFDP_4B_EXIT_NV_CFG | \ SPI_FLASH_SFDP_4B_EXIT_HW_RESET | SPI_FLASH_SFDP_4B_EXIT_SW_RESET | \ SPI_FLASH_SFDP_4B_EXIT_AC_RESET) /** * Initialize an SFDP interface for SPI flash. * * @param sfdp The SFDP interface to initialize. * @param flash The SPI master for the flash device. * * @return 0 if the SFDP interface was successfully initialized or an error code. */ int spi_flash_sfdp_init (struct spi_flash_sfdp *sfdp, const struct flash_master *flash) { struct flash_xfer xfer; int status; uint8_t id[3]; if ((sfdp == NULL) || (flash == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } memset (sfdp, 0, sizeof (struct spi_flash_sfdp)); FLASH_XFER_INIT_READ_REG (xfer, FLASH_CMD_RDID, id, sizeof (id), 0); status = flash->xfer (flash, &xfer); if (status != 0) { return status; } FLASH_XFER_INIT_READ (xfer, FLASH_CMD_SFDP, 0, 1, 0, (uint8_t*) &sfdp->sfdp_header, sizeof (sfdp->sfdp_header), 0); status = flash->xfer (flash, &xfer); if (status != 0) { return status; } if (sfdp->sfdp_header.signature != SPI_FLASH_SFDP_SIGNATURE) { return SPI_FLASH_SFDP_BAD_SIGNATURE; } if (SPI_FLASH_SFDP_PARAMETER_ID (sfdp->sfdp_header.parameter0) != 0xff00) { return SPI_FLASH_SFDP_BAD_HEADER; } sfdp->flash = flash; sfdp->vendor = id[0]; sfdp->device = (id[1] << 8) | id[2]; if ((sfdp->vendor == FLASH_ID_INFINEON) && (FLASH_ID_DEVICE_SERIES (sfdp->device) == FLASH_ID_S28HS)) { /* Infineon OSPI flash has 20 DWORDS for parameter table 0. However, it reports parameter0 * minor revision 0. We are manually setting the minor revision to 5 as a workaround. */ sfdp->sfdp_header.parameter0.minor_revision = 5; } return 0; } /** * Release the resources used by an SFDP interface. * * @param sfdp The SFDP interface to release. */ void spi_flash_sfdp_release (struct spi_flash_sfdp *sfdp) { UNUSED (sfdp); } /** * Print the contents of the SFDP header. * * @param sfdp The SFDP information to print. */ void spi_flash_sfdp_dump_header (const struct spi_flash_sfdp *sfdp) { struct flash_xfer xfer; uint32_t sfdp_data[sizeof (struct spi_flash_sfdp_header)]; size_t hdr_size = sizeof (struct spi_flash_sfdp_parameter_header); uint32_t i; int status; if (sfdp) { memcpy (sfdp_data, &sfdp->sfdp_header, sizeof (sfdp_data)); platform_printf ("Vendor: 0x%x, Device: 0x%x" NEWLINE, sfdp->vendor, sfdp->device); platform_printf ("SFDP Header: 0x%08lx" NEWLINE, (long unsigned int) sfdp_data[0]); platform_printf (" 0x%08lx" NEWLINE, (long unsigned int) sfdp_data[1]); platform_printf ("1st Param Hdr: 0x%08lx" NEWLINE, (long unsigned int) sfdp_data[2]); platform_printf (" 0x%08lx" NEWLINE, (long unsigned int) sfdp_data[3]); for (i = 0; i < sfdp->sfdp_header.header_count; i++) { FLASH_XFER_INIT_READ (xfer, FLASH_CMD_SFDP, sizeof (struct spi_flash_sfdp_header) + (i * hdr_size), 1, 0, (uint8_t*) &sfdp_data, hdr_size, 0); status = sfdp->flash->xfer (sfdp->flash, &xfer); if (status != 0) { platform_printf ("Failed to read parameter header: 0x%x" NEWLINE, status); break; } platform_printf ("%d%s Param Hdr: 0x%08lx" NEWLINE, i + 2, (i == 0) ? "nd" : ((i == 1) ? "rd" : "th"), (long unsigned int) sfdp_data[0]); platform_printf (" 0x%08lx" NEWLINE, (long unsigned int) sfdp_data[1]); } platform_printf (NEWLINE); } } /** * Read the SFDP basic parameter table from the SPI flash. * * @param table The basic parameter table instance to load. * @param sfdp The SFDP interface to use to load the table. * * @return 0 if the basic parameter table was successfully read or an error code. */ int spi_flash_sfdp_basic_table_init (struct spi_flash_sfdp_basic_table *table, const struct spi_flash_sfdp *sfdp) { struct flash_xfer xfer; size_t length; int status; if ((table == NULL) || (sfdp == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } memset (table, 0, sizeof (struct spi_flash_sfdp_basic_table)); length = min (sfdp->sfdp_header.parameter0.length * 4, sizeof (table->data)); FLASH_XFER_INIT_READ (xfer, FLASH_CMD_SFDP, SPI_FLASH_SFDP_PARAMETER_PTR (sfdp->sfdp_header.parameter0), 1, 0, (uint8_t*) table->data, length, 0); status = sfdp->flash->xfer (sfdp->flash, &xfer); if (status != 0) { return status; } table->sfdp = sfdp; return 0; } /** * Release the resources used by a SFDP basic parameter table. * * @param table The table to release. */ void spi_flash_sfdp_basic_table_release (struct spi_flash_sfdp_basic_table *table) { UNUSED (table); } /** * Determine the capabilities of the flash device. * * @param table The basic parameters table that will be queried to determine the capabilities. * @param capabilities Output for the device capabilities. This will be a bit mask using the same * capabilities defined for the SPI flash master. * * @return 0 if the capabilities were retrieved successfully or an error code. */ int spi_flash_sfdp_get_device_capabilities (const struct spi_flash_sfdp_basic_table *table, uint32_t *capabilities) { struct spi_flash_sfdp_basic_parameter_table_1_0 *params; if ((table == NULL) || (capabilities == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } params = (struct spi_flash_sfdp_basic_parameter_table_1_0*) table->data; *capabilities = 0; if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_1_2) { *capabilities |= FLASH_CAP_DUAL_1_1_2; } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_2_2) { *capabilities |= FLASH_CAP_DUAL_1_2_2; } if (params->dpi_qpi & SPI_FLASH_SFDP_SUPPORTS_2_2_2) { *capabilities |= FLASH_CAP_DUAL_2_2_2; } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_1_4) { *capabilities |= FLASH_CAP_QUAD_1_1_4; } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_4_4) { *capabilities |= FLASH_CAP_QUAD_1_4_4; } if (params->dpi_qpi & SPI_FLASH_SFDP_SUPPORTS_4_4_4) { *capabilities |= FLASH_CAP_QUAD_4_4_4; } if ((table->sfdp->vendor == FLASH_ID_WINBOND) && (FLASH_ID_DEVICE_SERIES (table->sfdp->device) == FLASH_ID_W25Q)) { /* Winbond W25Q series devices falsely report support for 4-4-4 read mode. This mode is * only available in the W25Q-DTR series. */ *capabilities &= ~FLASH_CAP_QUAD_4_4_4; } switch (params->dspi_qspi & SPI_FLASH_SFDP_ADDRESS_BYTES) { case SPI_FLASH_SFDP_3BYTE_ONLY: *capabilities |= FLASH_CAP_3BYTE_ADDR; break; case SPI_FLASH_SFDP_3BYTE_4BYTE: *capabilities |= (FLASH_CAP_3BYTE_ADDR | FLASH_CAP_4BYTE_ADDR); break; case SPI_FLASH_SFDP_4BYTE_ONLY: *capabilities |= FLASH_CAP_4BYTE_ADDR; } return 0; } /** * Determine the amount of storage available in the flash device. * * @param table The basic parameters table that will be queried to determine the size. * * @return The size of the device in bytes or an error code. */ int spi_flash_sfdp_get_device_size (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_0 *params; if (table == NULL) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } params = (struct spi_flash_sfdp_basic_parameter_table_1_0*) table->data; if (!(params->memory_density & SPI_FLASH_SFDP_4GB_DENSITY)) { return (params->memory_density + 1) / 8; } else { int factor = (params->memory_density & (~SPI_FLASH_SFDP_4GB_DENSITY)) - 3; if (factor < 31) { return 1U << factor; } else { return SPI_FLASH_SFDP_LARGE_DEVICE; } } } /** * Get the maximum number of bytes that can programmed at a time. * * @param table The basic parameters table that will be queried. * * @return The device page size or an error code. */ int spi_flash_sfdp_get_page_size (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; int page = 256; if (table == NULL) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; page = 1U << SPI_FLASH_SFDP_PAGE_SIZE (params->page_size); } return page; } /** * Parse read command information from the SFDP table. * * @param cmd The read command to parse. * @param opcode The opcode used by the device for this command. * @param dummy_clocks The number of dummy clocks for the command. * @param clocks_per_byte The number of clocks for each dummy byte. */ static void spi_flash_sfdp_parse_read_command (struct spi_flash_sfdp_read_cmd *cmd, uint8_t opcode, uint8_t dummy_clocks, uint8_t clocks_per_byte) { uint8_t partial_byte; cmd->opcode = opcode; cmd->mode_bytes = SPI_FLASH_SFDP_MODE_CLKS (dummy_clocks); cmd->dummy_bytes = SPI_FLASH_SFDP_DUMMY_CLKS (dummy_clocks); partial_byte = clocks_per_byte - (cmd->mode_bytes % clocks_per_byte); if (partial_byte != clocks_per_byte) { cmd->mode_bytes += partial_byte; cmd->dummy_bytes -= partial_byte; } cmd->mode_bytes /= clocks_per_byte; cmd->dummy_bytes /= clocks_per_byte; } /** * Determine the details about how to execute read commands against the flash device. * * @param table The basic parameters table that will be queried to determine the read commands. * @param read Output for the read command information. * * @return 0 if the command information was retrieved successfully or an error code. */ int spi_flash_sfdp_get_read_commands (const struct spi_flash_sfdp_basic_table *table, struct spi_flash_sfdp_read_commands *read) { struct spi_flash_sfdp_basic_parameter_table_1_0 *params; if ((table == NULL) || (read == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } params = (struct spi_flash_sfdp_basic_parameter_table_1_0*) table->data; memset (read, 0, sizeof (struct spi_flash_sfdp_read_commands)); if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_1_2) { spi_flash_sfdp_parse_read_command (&read->dual_1_1_2, params->opcode_1_1_2, params->dummy_1_1_2, 8); } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_2_2) { spi_flash_sfdp_parse_read_command (&read->dual_1_2_2, params->opcode_1_2_2, params->dummy_1_2_2, 4); } if (params->dpi_qpi & SPI_FLASH_SFDP_SUPPORTS_2_2_2) { spi_flash_sfdp_parse_read_command (&read->dual_2_2_2, params->opcode_2_2_2, params->dummy_2_2_2, 4); } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_1_4) { spi_flash_sfdp_parse_read_command (&read->quad_1_1_4, params->opcode_1_1_4, params->dummy_1_1_4, 8); } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_1_4_4) { spi_flash_sfdp_parse_read_command (&read->quad_1_4_4, params->opcode_1_4_4, params->dummy_1_4_4, 2); } if (params->dspi_qspi & SPI_FLASH_SFDP_SUPPORTS_4_4_4) { spi_flash_sfdp_parse_read_command (&read->quad_4_4_4, params->opcode_4_4_4, params->dummy_4_4_4, 2); } if ((table->sfdp->vendor == FLASH_ID_WINBOND) && (FLASH_ID_DEVICE_SERIES (table->sfdp->device) == FLASH_ID_W25Q)) { /* Winbond W25Q series devices falsely report support for the 4-4-4 read command. This * command is only available in the W25Q-DTR series. */ memset (&read->quad_4_4_4, 0, sizeof (read->quad_4_4_4)); } return 0; } /** * Indicate if the flash state should be queried the busy bit in the Flag Status Register instead of * the WIP bit in the Status Register. * * @param table The basic parameters table that will be queried for status polling mechanism. * * @return true if Flag Status Register should be used. */ bool spi_flash_sfdp_use_busy_flag_status (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; if ((table != NULL) && (table->sfdp->sfdp_header.parameter0.minor_revision >= 5)) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (params->status_reg & SPI_FLASH_SFDP_BUSY_SR_WIP) { return false; } else { return true; } } else { return false; } } /** * Indicate if the main status register should be written using the volatile (0x50) or non-volatile * (0x06) write enable command. * * @param table The basic parameters table that will be queried for status write enable. * * @return true if the volatile write enable should be used for status register writes. */ bool spi_flash_sfdp_use_volatile_write_enable (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; if ((table != NULL) && (table->sfdp->sfdp_header.parameter0.minor_revision >= 5)) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (params->sr_write_enable == (SPI_FLASH_SFDP_SR_WE_RESERVED | SPI_FLASH_SFDP_VOLATILE_SR_50)) { return true; } else { return false; } } else { return false; } } /** * Indicate if the device supports a dedicated 4-byte address instruction set. * * @param table The basic parameter table that will be queried for command support. * * @return true if the device supports dedicated 4-byte address commands. */ bool spi_flash_sfdp_supports_4byte_commands (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; bool opcodes_4b = false; if (table != NULL) { if ((table->sfdp->vendor == FLASH_ID_MACRONIX) && (FLASH_ID_DEVICE_SERIES (table->sfdp->device) != FLASH_ID_MX66UW)) { /* SFDP tables can't be used for some Macronix devices. Earlier devices * (e.g. MX25L25635F) don't support version 1.5, and at least some newer ones * (e.g. MX25L25645G) don't correctly report support for 4-byte commands. */ if ((FLASH_ID_DEVICE_SERIES (table->sfdp->device) == FLASH_ID_MX25L) && (FLASH_ID_DEVICE_CAPACITY (table->sfdp->device) >= 0x19)) { /* Only indicate 4-byte command support for >=256Mb devices. */ opcodes_4b = true; } else if ((FLASH_ID_DEVICE_SERIES (table->sfdp->device) == FLASH_ID_MX25U) && (FLASH_ID_DEVICE_CAPACITY (table->sfdp->device) >= 0x39)) { /* The MX25U series is the 1.8V version of MX25L devices. They have the same SFDP * issues, but use different device ID and capacity values. */ opcodes_4b = true; } } else if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (params->enter_4b & SPI_FLASH_SFDP_4B_OPCODES) { opcodes_4b = true; } } } return opcodes_4b; } /** * Get the method to use to enter and exit 4-byte address mode. * * @param table The basic parameter table that will be queried. * @param addr_4byte Output for the method supported by the device. * * @return 0 if the switching method was successfully determined or an error code. */ int spi_flash_sfdp_get_4byte_mode_switch (const struct spi_flash_sfdp_basic_table *table, enum spi_flash_sfdp_4byte_addressing *addr_4byte) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; if ((table == NULL) || (addr_4byte == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { if ((params->enter_4b & 0x7f) == 0) { *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_UNSUPPORTED; } else if (params->enter_4b & SPI_FLASH_SFDP_ALWAYS_4B) { *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_FIXED; } else if ((params->enter_4b & SPI_FLASH_SFDP_4B_ENTER_B7) && (params->reset_exit_4b & SPI_FLASH_SFDP_4B_EXIT_E9)) { *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_COMMAND; } else if ((params->enter_4b & SPI_FLASH_SFDP_4B_ENTER_06_B7) && (params->reset_exit_4b & SPI_FLASH_SFDP_4B_EXIT_06_E9)) { *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_COMMAND_WRITE_ENABLE; } else if (params->enter_4b & SPI_FLASH_SFDP_4B_OPCODES) { *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_INSTRUCTION_SET; } else { return SPI_FLASH_SFDP_4BYTE_INCOMPATIBLE; } } else { switch (params->table_1_0.dspi_qspi & SPI_FLASH_SFDP_ADDRESS_BYTES) { case SPI_FLASH_SFDP_3BYTE_4BYTE: /* Assume older devices that support 3-byte and 4-byte addressing support a command * to switch modes. */ *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_COMMAND; break; case SPI_FLASH_SFDP_4BYTE_ONLY: *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_FIXED; break; default: *addr_4byte = SPI_FLASH_SFDP_4BYTE_MODE_UNSUPPORTED; } } return 0; } /** * Indicate if a device reverts back to 3-byte addressing on soft reset. * * @param table The basic parameters table that will be queried. * * @return true if the device will revert on reset or false if not. */ bool spi_flash_sfdp_exit_4byte_mode_on_reset (const struct spi_flash_sfdp_basic_table *table) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; bool revert = true; // Assume a device will revert unless SFDP explicitly says otherwise. enum spi_flash_sfdp_4byte_addressing switch_4byte; int status; status = spi_flash_sfdp_get_4byte_mode_switch (table, &switch_4byte); if (status != 0) { return true; // Assume a device will revert if we can't determine the switch method. } else if (switch_4byte == SPI_FLASH_SFDP_4BYTE_MODE_INSTRUCTION_SET) { /* As the device can't enter 4byte addressing mode, it will be in 3byte addressing mode * after reset. */ return true; } if ((table != NULL) && (table->sfdp->sfdp_header.parameter0.minor_revision >= 5)) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; revert = !!(params->reset_exit_4b & SPI_FLASH_SFDP_4B_EXIT_SW_RESET); } return revert; } /** * Get the method to use for enable QSPI mode. * * @param table The basic parameters table that will be queried. * @param quad_enable Output for the QSPI enable mode. * * @return 0 if the enable mode was determined successfully or an error code. */ int spi_flash_sfdp_get_quad_enable (const struct spi_flash_sfdp_basic_table *table, enum spi_flash_sfdp_quad_enable *quad_enable) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; int quad; uint32_t caps; if ((table == NULL) || (quad_enable == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; quad = SPI_FLASH_SFDP_QER (params->quad_enable); switch (quad) { case SPI_FLASH_SFDP_QER_RESERVED1: case SPI_FLASH_SFDP_QER_RESERVED2: if (table->sfdp->vendor == FLASH_ID_MICRON_X) { /* The Micron Xcella flash device follows SFDP parameter version 1.6, * however incorrectly reports reserved value 7 for QER. * It does not support QUAD_1_1_4, QUAD_1_4_4 or QUAD_4_4_4. */ quad = SPI_FLASH_SFDP_QUAD_NO_QE_BIT; break; } else { return SPI_FLASH_SFDP_QUAD_ENABLE_UNKNOWN; } case SPI_FLASH_SFDP_QER_BIT1_SR2_NO_CLR: if ((table->sfdp->vendor == FLASH_ID_WINBOND) && (FLASH_ID_DEVICE_SERIES (table->sfdp->device) == FLASH_ID_W25Q)) { /* Winbond WQ25 devices incorrectly reports QER value 4 in the SFDP table. * It should report SPI_FLASH_SFDP_QER_BIT1_SR2_35 instead, since SR2 must be * read individually using command code 0x35. */ quad = SPI_FLASH_SFDP_QER_BIT1_SR2_35; } break; case SPI_FLASH_SFDP_QER_NO_QUAD_ENABLE: if (params->quad_enable & SPI_FLASH_SFDP_HOLD_RST_DISABLE) { quad = SPI_FLASH_SFDP_QUAD_NO_QE_HOLD_DISABLE; } break; } } else { /* For older tables without this information, use device ID and capabilities to determine * the quad enable method. */ spi_flash_sfdp_get_device_capabilities (table, &caps); if (caps & (FLASH_CAP_QUAD_1_1_4 | FLASH_CAP_QUAD_1_4_4 | FLASH_CAP_QUAD_4_4_4)) { switch (table->sfdp->vendor) { case FLASH_ID_MACRONIX: quad = SPI_FLASH_SFDP_QUAD_QE_BIT6_SR1; break; default: return SPI_FLASH_SFDP_QUAD_ENABLE_UNKNOWN; } } else { quad = SPI_FLASH_SFDP_QUAD_NO_QE_BIT; } } *quad_enable = (enum spi_flash_sfdp_quad_enable) quad; return 0; } /** * Get the command used to execute a soft reset of the device. * * @param table The basic parameters table that will be queried. * @param reset Output for the reset command. If the command requires the 66/99 sequence, only 66 * will be returned. This will be 0 if reset is not supported. * * @return 0 if the reset command was retrieved successfully or an error code. */ int spi_flash_sfdp_get_reset_command (const struct spi_flash_sfdp_basic_table *table, uint8_t *reset) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; uint8_t command = FLASH_CMD_RST; int status = 0; if ((table == NULL) || (reset == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (params->reset_exit_4b & SPI_FLASH_SFDP_RST_66_99) { command = FLASH_CMD_RST; } else if (params->reset_exit_4b & SPI_FLASH_SFDP_RST_F0) { command = FLASH_CMD_ALT_RST; } else { command = 0; status = SPI_FLASH_SFDP_RESET_NOT_SUPPORTED; } } *reset = command; return status; } /** * Get the commands used to enter and exit deep power down mode of the device. * * @param table The basic parameters table that will be queried. * @param enter Output for the enter deep power down command. This will be set to 0 if this mode is * not supported by the device. * @param exit Output for the exit deep power down command. This will be set to 0 if this mode is * not supported by the device. * * @return 0 if the power down commands were retrieved successfully or an error code. */ int spi_flash_sfdp_get_deep_powerdown_commands (const struct spi_flash_sfdp_basic_table *table, uint8_t *enter, uint8_t *exit) { struct spi_flash_sfdp_basic_parameter_table_1_5 *params; uint8_t cmd_enter = 0; uint8_t cmd_exit = 0; int status = 0; if ((table == NULL) || (enter == NULL) || (exit == NULL)) { return SPI_FLASH_SFDP_INVALID_ARGUMENT; } if (table->sfdp->sfdp_header.parameter0.minor_revision >= 5) { params = (struct spi_flash_sfdp_basic_parameter_table_1_5*) table->data; if (!SPI_FLASH_SFDP_PWRDWN_NO_SUPPORT (params->deep_powerdown)) { cmd_enter = SPI_FLASH_SFDP_PWRDWN_ENTER (params->deep_powerdown); cmd_exit = SPI_FLASH_SFDP_PWRDWN_EXIT (params->deep_powerdown); } else { status = SPI_FLASH_SFDP_PWRDOWN_NOT_SUPPORTED; } } else { switch (table->sfdp->vendor) { case FLASH_ID_MACRONIX: cmd_enter = FLASH_CMD_DP; cmd_exit = FLASH_CMD_RDP; break; default: status = SPI_FLASH_SFDP_PWRDOWN_NOT_SUPPORTED; break; } } *enter = cmd_enter; *exit = cmd_exit; return status; } /** * Print the contents of the basic parameters table. * * @param table The basic parameters table to print. */ void spi_flash_sfdp_dump_basic_table (const struct spi_flash_sfdp_basic_table *table) { const uint32_t *sfdp_data; int i; if (table) { sfdp_data = table->data; platform_printf ("Basic Flash Parameter Table:" NEWLINE); for (i = 0; i < table->sfdp->sfdp_header.parameter0.length; i++) { platform_printf (" DWORD %2d: 0x%08lx" NEWLINE, i + 1, (long unsigned int) sfdp_data[i]); } platform_printf (NEWLINE); } }