drivers: flash: spi_nor: add option for 4byte opcodes

some flashes support special opcodes
for 4-byte addressing, that can be used
without switching to 4-byte mode.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
Fin Maaß 2024-10-24 14:24:57 +02:00 committed by Anas Nashif
parent 1f55b8d8a4
commit cf4a398477
3 changed files with 118 additions and 5 deletions

View file

@ -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), \

View file

@ -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 */

View file

@ -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.