diff --git a/drivers/flash/spi_nor.c b/drivers/flash/spi_nor.c index 9f849a4780b..f064f4179de 100644 --- a/drivers/flash/spi_nor.c +++ b/drivers/flash/spi_nor.c @@ -43,7 +43,7 @@ LOG_MODULE_REGISTER(spi_nor, CONFIG_FLASH_LOG_LEVEL); */ #define SPI_NOR_MAX_ADDR_WIDTH 4 - +#define SPI_NOR_3B_ADDR_MAX 0xFFFFFF #define ANY_INST_HAS_TRUE_(idx, bool_prop) \ COND_CODE_1(DT_INST_PROP(idx, bool_prop), (1,), ()) @@ -65,6 +65,7 @@ LOG_MODULE_REGISTER(spi_nor, CONFIG_FLASH_LOG_LEVEL); #define ANY_INST_HAS_RESET_GPIOS ANY_INST_HAS_PROP(reset_gpios) #define ANY_INST_HAS_WP_GPIOS ANY_INST_HAS_PROP(wp_gpios) #define ANY_INST_HAS_HOLD_GPIOS ANY_INST_HAS_PROP(hold_gpios) +#define ANY_INST_USE_4B_ADDR_OPCODES ANY_INST_HAS_TRUE(use_4b_addr_opcodes) #ifdef CONFIG_SPI_NOR_ACTIVE_DWELL_MS #define ACTIVE_DWELL_MS CONFIG_SPI_NOR_ACTIVE_DWELL_MS @@ -152,6 +153,7 @@ struct spi_nor_config { #if ANY_INST_HAS_MXICY_MX25R_POWER_MODE bool mxicy_mx25r_power_mode; #endif + bool use_4b_addr_opcodes:1; /* exist flags for dts opt-ins */ bool dpd_exist:1; @@ -220,6 +222,16 @@ static const struct jesd216_erase_type minimal_erase_types[JESD216_NUM_ERASE_TYP .exp = 12, }, }; +static const struct jesd216_erase_type minimal_erase_types_4b[JESD216_NUM_ERASE_TYPES] = { + { + .cmd = SPI_NOR_CMD_BE_4B, + .exp = 16, + }, + { + .cmd = SPI_NOR_CMD_SE_4B, + .exp = 12, + }, +}; #endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */ /* Register writes should be ready extremely quickly */ @@ -239,6 +251,9 @@ static inline const struct jesd216_erase_type * dev_erase_types(const struct device *dev) { #ifdef CONFIG_SPI_NOR_SFDP_MINIMAL + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && DEV_CFG(dev)->use_4b_addr_opcodes) { + return minimal_erase_types_4b; + } return minimal_erase_types; #else /* CONFIG_SPI_NOR_SFDP_MINIMAL */ const struct spi_nor_data *data = dev->data; @@ -432,11 +447,25 @@ static int spi_nor_access(const struct device *const dev, spi_nor_access(dev, opcode, 0, 0, dest, length) #define spi_nor_cmd_addr_read(dev, opcode, addr, dest, length) \ spi_nor_access(dev, opcode, NOR_ACCESS_ADDRESSED, addr, dest, length) +#define spi_nor_cmd_addr_read_3b(dev, opcode, addr, dest, length) \ + spi_nor_access(dev, opcode, NOR_ACCESS_24BIT_ADDR | NOR_ACCESS_ADDRESSED, addr, dest, \ + length) +#define spi_nor_cmd_addr_read_4b(dev, opcode, addr, dest, length) \ + spi_nor_access(dev, opcode, NOR_ACCESS_32BIT_ADDR | NOR_ACCESS_ADDRESSED, addr, dest, \ + length) #define spi_nor_cmd_write(dev, opcode) \ spi_nor_access(dev, opcode, NOR_ACCESS_WRITE, 0, NULL, 0) #define spi_nor_cmd_addr_write(dev, opcode, addr, src, length) \ spi_nor_access(dev, opcode, NOR_ACCESS_WRITE | NOR_ACCESS_ADDRESSED, \ addr, (void *)src, length) +#define spi_nor_cmd_addr_write_3b(dev, opcode, addr, src, length) \ + spi_nor_access(dev, opcode, \ + NOR_ACCESS_24BIT_ADDR | NOR_ACCESS_WRITE | NOR_ACCESS_ADDRESSED, addr, \ + (void *)src, length) +#define spi_nor_cmd_addr_write_4b(dev, opcode, addr, src, length) \ + spi_nor_access(dev, opcode, \ + NOR_ACCESS_32BIT_ADDR | NOR_ACCESS_WRITE | NOR_ACCESS_ADDRESSED, addr, \ + (void *)src, length) /** * @brief Wait until the flash is ready @@ -784,7 +813,15 @@ static int spi_nor_read(const struct device *dev, off_t addr, void *dest, acquire_device(dev); - ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size); + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && DEV_CFG(dev)->use_4b_addr_opcodes) { + if (addr > SPI_NOR_3B_ADDR_MAX) { + ret = spi_nor_cmd_addr_read_4b(dev, SPI_NOR_CMD_READ_4B, addr, dest, size); + } else { + ret = spi_nor_cmd_addr_read_3b(dev, SPI_NOR_CMD_READ, addr, dest, size); + } + } else { + ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size); + } release_device(dev); @@ -867,8 +904,20 @@ static int spi_nor_write(const struct device *dev, off_t addr, break; } - ret = spi_nor_cmd_addr_write(dev, SPI_NOR_CMD_PP, addr, - src, to_write); + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && + DEV_CFG(dev)->use_4b_addr_opcodes) { + if (addr > SPI_NOR_3B_ADDR_MAX) { + ret = spi_nor_cmd_addr_write_4b(dev, SPI_NOR_CMD_PP_4B, + addr, src, to_write); + } else { + ret = spi_nor_cmd_addr_write_3b(dev, SPI_NOR_CMD_PP, addr, + src, to_write); + } + } else { + ret = spi_nor_cmd_addr_write(dev, SPI_NOR_CMD_PP, addr, src, + to_write); + } + if (ret != 0) { break; } @@ -953,7 +1002,13 @@ static int spi_nor_erase(const struct device *dev, off_t addr, size_t size) } } if (bet != NULL) { - ret = spi_nor_cmd_addr_write(dev, bet->cmd, addr, NULL, 0); + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && + DEV_CFG(dev)->use_4b_addr_opcodes) { + ret = spi_nor_cmd_addr_write_4b(dev, bet->cmd, addr, NULL, + 0); + } else { + ret = spi_nor_cmd_addr_write(dev, bet->cmd, addr, NULL, 0); + } addr += BIT(bet->exp); size -= BIT(bet->exp); } else { @@ -1164,6 +1219,11 @@ static int spi_nor_process_bfp(const struct device *dev, struct jesd216_bfp_dw16 dw16; int rc = 0; + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && DEV_CFG(dev)->use_4b_addr_opcodes) { + LOG_DBG("4-byte addressing supported, using it via specific opcodes"); + return 0; + } + if (jesd216_bfp_decode_dw16(php, bfp, &dw16) == 0) { rc = spi_nor_set_address_mode(dev, dw16.enter_4ba); } @@ -1181,6 +1241,7 @@ static int spi_nor_process_sfdp(const struct device *dev) int rc; #if defined(CONFIG_SPI_NOR_SFDP_RUNTIME) + struct spi_nor_data *dev_data = dev->data; /* For runtime we need to read the SFDP table, identify the * BFP block, and process it. */ @@ -1236,6 +1297,45 @@ static int spi_nor_process_sfdp(const struct device *dev) break; } } + if (id == JESD216_SFDP_PARAM_ID_4B_ADDR_INSTR) { + if (IS_ENABLED(ANY_INST_USE_4B_ADDR_OPCODES) && + DEV_CFG(dev)->use_4b_addr_opcodes) { + /* + * Check table 4 byte address instruction table to get supported + * erase opcodes when running in 4 byte address mode + */ + union { + uint32_t dw[2]; + struct { + uint32_t dummy; + uint8_t type[4]; + } types; + } u2; + rc = spi_nor_sfdp_read( + dev, jesd216_param_addr(php), (uint8_t *)u2.dw, + MIN(sizeof(uint32_t) * php->len_dw, sizeof(u2.dw))); + if (rc != 0) { + break; + } + for (uint8_t ei = 0; ei < JESD216_NUM_ERASE_TYPES; ++ei) { + struct jesd216_erase_type *etp = &dev_data->erase_types[ei]; + const uint8_t cmd = u2.types.type[ei]; + /* 0xff means not supported */ + if (cmd == 0xff) { + etp->exp = 0; + etp->cmd = 0; + } else { + etp->cmd = cmd; + }; + } + + if (!((sys_le32_to_cpu(u2.dw[0]) & BIT(0)) && + (sys_le32_to_cpu(u2.dw[1]) & BIT(6)))) { + LOG_ERR("4-byte addressing not supported"); + return -ENOTSUP; + } + } + } ++php; } #elif defined(CONFIG_SPI_NOR_SFDP_DEVICETREE) @@ -1692,6 +1792,7 @@ static const struct flash_driver_api spi_nor_api = { .requires_ulbpr_exist = DT_INST_PROP(idx, requires_ulbpr), \ .wp_gpios_exist = DT_INST_NODE_HAS_PROP(idx, wp_gpios), \ .hold_gpios_exist = DT_INST_NODE_HAS_PROP(idx, hold_gpios), \ + .use_4b_addr_opcodes = DT_INST_PROP(idx, use_4b_addr_opcodes), \ IF_ENABLED(INST_HAS_LOCK(idx), (.has_lock = DT_INST_PROP(idx, has_lock),)) \ IF_ENABLED(ANY_INST_HAS_DPD, (INIT_T_ENTER_DPD(idx),)) \ IF_ENABLED(UTIL_AND(ANY_INST_HAS_DPD, ANY_INST_HAS_T_EXIT_DPD), \ diff --git a/drivers/flash/spi_nor.h b/drivers/flash/spi_nor.h index 5f38c98289f..a918e0a99d0 100644 --- a/drivers/flash/spi_nor.h +++ b/drivers/flash/spi_nor.h @@ -39,6 +39,7 @@ #define SPI_NOR_CMD_SE_4B 0x21 /* Sector erase 4 byte address*/ #define SPI_NOR_CMD_BE_32K 0x52 /* Block erase 32KB */ #define SPI_NOR_CMD_BE 0xD8 /* Block erase */ +#define SPI_NOR_CMD_BE_4B 0xDC /* Block erase 4 byte address*/ #define SPI_NOR_CMD_CE 0xC7 /* Chip erase */ #define SPI_NOR_CMD_RDID 0x9F /* Read JEDEC ID */ #define SPI_NOR_CMD_ULBPR 0x98 /* Global Block Protection Unlock */ diff --git a/dts/bindings/mtd/jedec,spi-nor-common.yaml b/dts/bindings/mtd/jedec,spi-nor-common.yaml index 3e39c967097..7ef04f24929 100644 --- a/dts/bindings/mtd/jedec,spi-nor-common.yaml +++ b/dts/bindings/mtd/jedec,spi-nor-common.yaml @@ -102,3 +102,14 @@ properties: low power mode. Only supported on Macronix MX25R Ultra Low Power series. + + use-4b-addr-opcodes: + type: boolean + description: | + Indicates the device uses special 4-byte address opcodes. + Instead of switching to 4-byte addressing mode, the device uses + special opcodes for 4-byte addressing. + + Some devices support 4-byte address opcodes for read/write/erase + operations. Use this property to indicate that the device supports + 4-byte address opcodes.