Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 297 additions & 5 deletions drivers/flash/spi_nor.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ struct spi_nor_data {
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
#endif /* CONFIG_SPI_NOR_SFDP_RUNTIME */
#endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */

#ifdef CONFIG_SPI_EXTENDED_MODES
enum spi_nor_protocol nor_protocol;

uint8_t read_cmd;
uint8_t program_cmd;
uint8_t erase_cmd;
#endif
};

#ifdef CONFIG_SPI_NOR_SFDP_MINIMAL
Expand Down Expand Up @@ -393,6 +401,7 @@ static int spi_nor_access(const struct device *const dev,
}
};

#ifndef CONFIG_SPI_EXTENDED_MODES
buf[0] = opcode;
if (is_addressed) {
bool access_24bit = (access & NOR_ACCESS_24BIT_ADDR) != 0;
Expand Down Expand Up @@ -425,6 +434,131 @@ static int spi_nor_access(const struct device *const dev,
.buffers = spi_buf,
.count = 2,
};
#else

spi_buf[0].spi_mem_op.cmd.opcode = opcode;
spi_buf[0].spi_mem_op.cmd.nbytes = 1;

if (is_addressed) {
bool access_24bit = (access & NOR_ACCESS_24BIT_ADDR) != 0;
bool access_32bit = (access & NOR_ACCESS_32BIT_ADDR) != 0;
bool use_32bit = (access_32bit
|| (!access_24bit
&& driver_data->flag_access_32bit));

spi_buf[0].spi_mem_op.addr.nbytes = use_32bit ? 4 : 3;
};
spi_buf[0].spi_mem_op.addr.val = addr;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.dtr = 0;

spi_buf[0].spi_mem_op.data.nbytes = length;
spi_buf[0].spi_mem_op.data.buf = data;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.data.dtr = 0;


switch (driver_data->nor_protocol) {
case PROTO_1_1_1:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.cmd.dtr = 0;
break;
case PROTO_1_1_2:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_DUAL;
break;
case PROTO_1_2_2:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_DUAL;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_DUAL;
break;
case PROTO_1_1_4:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_QUAD;
break;
case PROTO_1_4_4:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_QUAD;
spi_buf[0].spi_mem_op.addr.dtr = 1;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_QUAD;
spi_buf[0].spi_mem_op.data.dtr = 1;
break;
case PROTO_1_4D_4D:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_SINGLE;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_QUAD;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_QUAD;
break;
case PROTO_4_4_4:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_QUAD;
spi_buf[0].spi_mem_op.cmd.dtr = 0;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_QUAD;
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_QUAD;
if (opcode == SPI_NOR_CMD_4READ) {
spi_buf[0].spi_mem_op.dummy.nbytes = 6;
}
break;
case PROTO_8_8_8:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.cmd.dtr = 0;
spi_buf[0].spi_mem_op.cmd.opcode = (opcode << 8) | (0xFF - opcode);
spi_buf[0].spi_mem_op.cmd.nbytes = 2;

spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.addr.dtr = 0;

if ((opcode == SPI_NOR_CMD_RDID) || (opcode == SPI_NOR_CMD_RDSR)) {
spi_buf[0].spi_mem_op.addr.val = 0;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.addr.nbytes = 4;

spi_buf[0].spi_mem_op.dummy.nbytes = 4;
}
if (opcode == 0xEC) {
spi_buf[0].spi_mem_op.dummy.nbytes = 20;
}
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.data.dtr = 0;
break;
case PROTO_8D_8D_8D:
spi_buf[0].spi_mem_op.cmd.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.cmd.dtr = 1;
spi_buf[0].spi_mem_op.cmd.opcode = (opcode << 8) | (0xFF - opcode);
spi_buf[0].spi_mem_op.cmd.nbytes = 2;

spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.addr.dtr = 1;

spi_buf[0].spi_mem_op.data.dtr = 1;

if ((opcode == SPI_NOR_CMD_RDID) || (opcode == SPI_NOR_CMD_RDSR)) {
spi_buf[0].spi_mem_op.addr.val = 0;
spi_buf[0].spi_mem_op.addr.buswidth = SPI_LINES_OCTAL;
spi_buf[0].spi_mem_op.addr.nbytes = 4;
spi_buf[0].spi_mem_op.dummy.nbytes = 6;
spi_buf[0].spi_mem_op.data.dtr = 0;
} else if (opcode == 0xEE) {
spi_buf[0].spi_mem_op.dummy.nbytes = 20;
}
spi_buf[0].spi_mem_op.data.buswidth = SPI_LINES_OCTAL;

break;
default:
break;
};

const struct spi_buf_set tx_set = {
.buffers = spi_buf,
.count = 1,
};

const struct spi_buf_set rx_set = {
.buffers = spi_buf,
.count = 1,
};

#endif

if (is_write) {
return spi_write_dt(&driver_cfg->spi, &tx_set);
Expand Down Expand Up @@ -785,9 +919,121 @@ static int mxicy_configure(const struct device *dev, const uint8_t *jedec_id)

#endif /* ANY_INST_HAS_MXICY_MX25R_POWER_MODE */

#ifdef CONFIG_SPI_EXTENDED_MODES
/**
* @brief Write the configuration register2.
*
* @note The device must be externally acquired before invoking this
* function.
*
* @param dev Device struct
* @param sr The new value of the configuration register2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace sr with cr.

*
* @return 0 on success or a negative error code.
*/
static int spi_nor_wrcr2(const struct device *dev,
uint8_t cr)
{
int ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_WREN);

if (ret == 0) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this empty line?

struct spi_nor_data *data = dev->data;

data->flag_access_32bit = true;

ret = spi_nor_cmd_addr_write(dev, SPI_NOR_CMD_WR_CFGREG2, 0, &cr, sizeof(cr));
}

return ret;
}

static int spi_nor_change_protocol(const struct device *dev,
enum spi_nor_protocol nor_protocol)
{
struct spi_nor_data *data = dev->data;
uint8_t cr2;
int ret = 0;

switch (nor_protocol) {
case PROTO_1_1_1:
data->nor_protocol = PROTO_1_1_1;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_1_1_2:
data->nor_protocol = PROTO_1_1_2;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_1_2_2:
data->nor_protocol = PROTO_1_2_2;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_1_1_4:
data->nor_protocol = PROTO_1_1_4;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_1_4_4:
data->nor_protocol = PROTO_1_4_4;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_1_4D_4D:
data->nor_protocol = PROTO_1_4D_4D;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could shrink the base cases:

	case PROTO_1_1_1:
	case PROTO_1_1_2:
	case PROTO_1_2_2:
	case PROTO_1_1_4:
	case PROTO_1_4_4:
	case PROTO_1_4D_4D:
		data->nor_protocol = nor_protocol;
		data->flag_access_32bit = false;
		data->read_cmd = SPI_NOR_CMD_READ;
		data->program_cmd = SPI_NOR_CMD_PP;
		data->erase_cmd = SPI_NOR_CMD_SE;
		break;

case PROTO_4_4_4:
ret = spi_nor_cmd_write(dev, SPI_NOR_CMD_EQIO);
data->nor_protocol = PROTO_4_4_4;
data->flag_access_32bit = false;
data->read_cmd = SPI_NOR_CMD_4READ;
data->program_cmd = SPI_NOR_CMD_PP;
data->erase_cmd = SPI_NOR_CMD_SE;
break;
case PROTO_8_8_8:
data->flag_access_32bit = true;
data->read_cmd = SPI_NOR_OCMD_RD;
data->program_cmd = SPI_NOR_CMD_PP_4B;
data->erase_cmd = SPI_NOR_OCMD_SE;
cr2 = 0x01;
ret = spi_nor_wrcr2(dev, cr2);
data->nor_protocol = PROTO_8_8_8;
break;
case PROTO_8D_8D_8D:
data->flag_access_32bit = true;
data->read_cmd = SPI_NOR_OCMD_DTR_RD;
data->program_cmd = SPI_NOR_CMD_PP_4B;
data->erase_cmd = SPI_NOR_OCMD_SE;
cr2 = 0x02;
ret = spi_nor_wrcr2(dev, cr2);
data->nor_protocol = PROTO_8D_8D_8D;
break;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default case missing

}

return ret;
}
#endif

static int spi_nor_read(const struct device *dev, off_t addr, void *dest,
size_t size)
{
struct spi_nor_data *data = dev->data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger a build warning on data being unused when CONFIG_SPI_EXTENDED_MODES is disabled.

Suggestion: at line 1052:

-	struct spi_nor_data *data = dev->data;
+	uint8_t read_cmd = SPI_NOR_CMD_READ;
 	const size_t flash_size = dev_flash_size(dev);
 	int ret;
 
+	if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES))
+		read_cmd = ((struct spi_nor_data *)(dev->data))->read_cmd;

Ditto in spi_nor_write().

const size_t flash_size = dev_flash_size(dev);
int ret;

Expand All @@ -803,14 +1049,19 @@ static int spi_nor_read(const struct device *dev, off_t addr, void *dest,

acquire_device(dev);

uint8_t read_cmd = SPI_NOR_CMD_READ;
#ifdef CONFIG_SPI_EXTENDED_MODES
read_cmd = data->read_cmd;
#endif

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);
ret = spi_nor_cmd_addr_read_3b(dev, read_cmd, addr, dest, size);
}
} else {
ret = spi_nor_cmd_addr_read(dev, SPI_NOR_CMD_READ, addr, dest, size);
ret = spi_nor_cmd_addr_read(dev, read_cmd, addr, dest, size);
}

release_device(dev);
Expand Down Expand Up @@ -857,6 +1108,7 @@ static int spi_nor_write(const struct device *dev, off_t addr,
const void *src,
size_t size)
{
struct spi_nor_data *data = dev->data;
const size_t flash_size = dev_flash_size(dev);
const uint16_t page_size = dev_page_size(dev);
int ret;
Expand Down Expand Up @@ -894,17 +1146,22 @@ static int spi_nor_write(const struct device *dev, off_t addr,
break;
}

uint8_t program_cmd = SPI_NOR_CMD_PP;
#ifdef CONFIG_SPI_EXTENDED_MODES
program_cmd = data->program_cmd;
#endif

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);
ret = spi_nor_cmd_addr_write_3b(dev, program_cmd,
addr, src, to_write);
}
} else {
ret = spi_nor_cmd_addr_write(dev, SPI_NOR_CMD_PP, addr, src,
ret = spi_nor_cmd_addr_write(dev, program_cmd, addr, src,
to_write);
}

Expand Down Expand Up @@ -1426,6 +1683,32 @@ static int setup_pages_layout(const struct device *dev)
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
#endif /* CONFIG_SPI_NOR_SFDP_MINIMAL */

#ifdef CONFIG_SPI_EXTENDED_MODES
static enum spi_nor_protocol spi_config_get_lines(const struct spi_config *config)
{
enum spi_nor_protocol nor_protocol;

switch (config->operation & SPI_LINES_MASK) {
case SPI_LINES_SINGLE:
nor_protocol = PROTO_1_1_1;
break;
case SPI_LINES_DUAL:
nor_protocol = PROTO_1_2_2;
break;
case SPI_LINES_QUAD:
nor_protocol = PROTO_4_4_4;
break;
case SPI_LINES_OCTAL:
nor_protocol = PROTO_8_8_8;
break;
default:
nor_protocol = PROTO_1_1_1;
}

return nor_protocol;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicking suggestion:

static enum spi_nor_protocol spi_config_get_lines(const struct spi_config *config)
{
	switch (config->operation & SPI_LINES_MASK) {
	case SPI_LINES_SINGLE:
		return PROTO_1_1_1;
	case SPI_LINES_DUAL:
		return PROTO_1_2_2;
	case SPI_LINES_QUAD:
		return PROTO_4_4_4;
	case SPI_LINES_OCTAL:
		return PROTO_8_8_8;
	default:
		return PROTO_1_1_1;
	}
}

}
#endif

/**
* @brief Configure the flash
*
Expand Down Expand Up @@ -1509,6 +1792,15 @@ static int spi_nor_configure(const struct device *dev)
}
#endif

#ifdef CONFIG_SPI_EXTENDED_MODES

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove this empty line?

rc = spi_nor_change_protocol(dev, spi_config_get_lines(&cfg->spi.config));
if (rc != 0) {
LOG_ERR("Change protocol failed: %d", rc);
return -ENODEV;
}
#endif

/* Check for block protect bits that need to be cleared. This
* information cannot be determined from SFDP content, so the
* devicetree node property must be set correctly for any device
Expand Down
2 changes: 2 additions & 0 deletions drivers/flash/spi_nor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
#define SPI_NOR_CMD_PP_4B 0x12 /* Page Program 4 Byte Address */
#define SPI_NOR_CMD_PP_1_1_4_4B 0x34 /* Quad Page program (1-1-4) 4 Byte Address */
#define SPI_NOR_CMD_PP_1_4_4_4B 0x3e /* Quad Page program (1-4-4) 4 Byte Address */
#define SPI_NOR_CMD_EQIO 0x35 /* Enable QPI mode */
#define SPI_NOR_CMD_RSTQIO 0xF5 /* Disable QPI mode */

/* Flash octal opcodes */
#define SPI_NOR_OCMD_SE 0x21DE /* Octal Sector erase */
Expand Down
Loading
Loading