drivers: flash: mspi_nor: support MODE_SINGLE and MODE_QUAD_1_4_4
Extend driver to support single lane and 1-4-4 IO modes. Move flash chip quirks to a separate file. Signed-off-by: Marcin Szymczyk <marcin.szymczyk@nordicsemi.no>
This commit is contained in:
parent
0f0964f873
commit
b6642bb996
3 changed files with 798 additions and 187 deletions
|
@ -6,47 +6,56 @@
|
|||
|
||||
#define DT_DRV_COMPAT jedec_mspi_nor
|
||||
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/mspi.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/device_runtime.h>
|
||||
|
||||
#include "jesd216.h"
|
||||
#include "spi_nor.h"
|
||||
#include "flash_mspi_nor.h"
|
||||
#include "flash_mspi_nor_quirks.h"
|
||||
|
||||
LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL);
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
#define WITH_RESET_GPIO 1
|
||||
#endif
|
||||
void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_nor_cmd *cmd)
|
||||
{
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
|
||||
struct flash_mspi_nor_data {
|
||||
struct k_sem acquired;
|
||||
struct mspi_xfer_packet packet;
|
||||
struct mspi_xfer xfer;
|
||||
};
|
||||
memset(&dev_data->xfer, 0, sizeof(dev_data->xfer));
|
||||
memset(&dev_data->packet, 0, sizeof(dev_data->packet));
|
||||
|
||||
struct flash_mspi_nor_config {
|
||||
const struct device *bus;
|
||||
uint32_t flash_size;
|
||||
struct mspi_dev_id mspi_id;
|
||||
struct mspi_dev_cfg mspi_cfg;
|
||||
enum mspi_dev_cfg_mask mspi_cfg_mask;
|
||||
#if defined(CONFIG_MSPI_XIP)
|
||||
struct mspi_xip_cfg xip_cfg;
|
||||
#endif
|
||||
#if defined(WITH_RESET_GPIO)
|
||||
struct gpio_dt_spec reset;
|
||||
uint32_t reset_pulse_us;
|
||||
uint32_t reset_recovery_us;
|
||||
#endif
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
struct flash_pages_layout layout;
|
||||
#endif
|
||||
uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
|
||||
};
|
||||
dev_data->xfer.xfer_mode = MSPI_PIO;
|
||||
dev_data->xfer.packets = &dev_data->packet;
|
||||
dev_data->xfer.num_packet = 1;
|
||||
dev_data->xfer.timeout = 10;
|
||||
|
||||
dev_data->xfer.cmd_length = cmd->cmd_length;
|
||||
dev_data->xfer.addr_length = cmd->addr_length;
|
||||
dev_data->xfer.tx_dummy = (cmd->dir == MSPI_TX) ?
|
||||
cmd->tx_dummy : dev_config->mspi_nor_cfg.tx_dummy;
|
||||
dev_data->xfer.rx_dummy = (cmd->dir == MSPI_RX) ?
|
||||
cmd->rx_dummy : dev_config->mspi_nor_cfg.rx_dummy;
|
||||
|
||||
dev_data->packet.dir = cmd->dir;
|
||||
dev_data->packet.cmd = cmd->cmd;
|
||||
}
|
||||
|
||||
static int dev_cfg_apply(const struct device *dev, const struct mspi_dev_cfg *cfg)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
|
||||
if (dev_data->curr_cfg == cfg) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id,
|
||||
MSPI_DEVICE_CONFIG_ALL, cfg);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to set device config: %p error: %d", cfg, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int acquire(const struct device *dev)
|
||||
{
|
||||
|
@ -64,8 +73,8 @@ static int acquire(const struct device *dev)
|
|||
* if needed for the flash device.
|
||||
*/
|
||||
rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id,
|
||||
dev_config->mspi_cfg_mask,
|
||||
&dev_config->mspi_cfg);
|
||||
dev_config->mspi_nor_cfg_mask,
|
||||
&dev_config->mspi_nor_cfg);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("mspi_dev_config() failed: %d", rc);
|
||||
} else {
|
||||
|
@ -125,12 +134,18 @@ static int api_read(const struct device *dev, off_t addr, void *dest,
|
|||
return rc;
|
||||
}
|
||||
|
||||
if (dev_config->jedec_cmds->read.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* TODO: get rid of all these hard-coded values for MX25Ux chips */
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->xfer.rx_dummy = 20;
|
||||
dev_data->packet.dir = MSPI_RX;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_RD;
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->read);
|
||||
dev_data->packet.address = addr;
|
||||
dev_data->packet.data_buf = dest;
|
||||
dev_data->packet.num_bytes = size;
|
||||
|
@ -140,35 +155,58 @@ static int api_read(const struct device *dev, off_t addr, void *dest,
|
|||
release(dev);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_OCMD_RD xfer failed: %d", rc);
|
||||
LOG_ERR("Read xfer failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_until_ready(const struct device *dev, k_timeout_t poll_period)
|
||||
static int status_get(const struct device *dev, uint8_t *status)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
uint8_t status_reg;
|
||||
int rc;
|
||||
|
||||
/* Enter command mode */
|
||||
if (dev_config->jedec_cmds->status.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Switching to dev_cfg failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->status);
|
||||
dev_data->packet.data_buf = status;
|
||||
dev_data->packet.num_bytes = sizeof(uint8_t);
|
||||
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Status xfer failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int wait_until_ready(const struct device *dev, k_timeout_t poll_period)
|
||||
{
|
||||
int rc;
|
||||
uint8_t status_reg;
|
||||
|
||||
while (true) {
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->xfer.rx_dummy = 4;
|
||||
dev_data->packet.dir = MSPI_RX;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_RDSR;
|
||||
dev_data->packet.address = 0;
|
||||
dev_data->packet.data_buf = &status_reg;
|
||||
dev_data->packet.num_bytes = sizeof(status_reg);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
rc = status_get(dev, &status_reg);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_OCMD_RDSR xfer failed: %d", rc);
|
||||
LOG_ERR("Wait until ready - status xfer failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!(status_reg & SPI_NOR_WIP_BIT)) {
|
||||
break;
|
||||
}
|
||||
|
@ -179,6 +217,26 @@ static int wait_until_ready(const struct device *dev, k_timeout_t poll_period)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int write_enable(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
int rc;
|
||||
|
||||
if (dev_config->jedec_cmds->write_en.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->write_en);
|
||||
return mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
}
|
||||
|
||||
static int api_write(const struct device *dev, off_t addr, const void *src,
|
||||
size_t size)
|
||||
{
|
||||
|
@ -207,29 +265,29 @@ static int api_write(const struct device *dev, off_t addr, const void *src,
|
|||
uint16_t page_left = page_size - page_offset;
|
||||
uint16_t to_write = (uint16_t)MIN(size, page_left);
|
||||
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.tx_dummy = 0;
|
||||
dev_data->packet.dir = MSPI_TX;
|
||||
|
||||
dev_data->xfer.addr_length = 0;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_WREN;
|
||||
dev_data->packet.num_bytes = 0;
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc);
|
||||
if (write_enable(dev) < 0) {
|
||||
LOG_ERR("Write enable xfer failed: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_PAGE_PRG;
|
||||
if (dev_config->jedec_cmds->page_program.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->page_program);
|
||||
dev_data->packet.address = addr;
|
||||
dev_data->packet.data_buf = (uint8_t *)src;
|
||||
dev_data->packet.num_bytes = to_write;
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_OCMD_PAGE_PRG xfer failed: %d", rc);
|
||||
LOG_ERR("Page program xfer failed: %d", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -273,32 +331,40 @@ static int api_erase(const struct device *dev, off_t addr, size_t size)
|
|||
}
|
||||
|
||||
while (size > 0) {
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.tx_dummy = 0;
|
||||
dev_data->packet.dir = MSPI_TX;
|
||||
dev_data->packet.num_bytes = 0;
|
||||
|
||||
dev_data->xfer.addr_length = 0;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_WREN;
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
rc = write_enable(dev);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc);
|
||||
LOG_ERR("Write enable failed.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (size == flash_size) {
|
||||
/* Chip erase. */
|
||||
dev_data->xfer.addr_length = 0;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_CE;
|
||||
if (dev_config->jedec_cmds->chip_erase.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->chip_erase);
|
||||
size -= flash_size;
|
||||
} else {
|
||||
/* Sector erase. */
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->packet.cmd = SPI_NOR_OCMD_SE;
|
||||
dev_data->packet.address = addr;
|
||||
if (dev_config->jedec_cmds->sector_erase.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->sector_erase);
|
||||
dev_data->packet.address = addr;
|
||||
addr += SPI_NOR_SECTOR_SIZE;
|
||||
size -= SPI_NOR_SECTOR_SIZE;
|
||||
}
|
||||
|
@ -340,19 +406,24 @@ static int read_jedec_id(const struct device *dev, uint8_t *id)
|
|||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
int rc;
|
||||
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->xfer.rx_dummy = 4;
|
||||
dev_data->packet.dir = MSPI_RX;
|
||||
dev_data->packet.cmd = JESD216_OCMD_READ_ID;
|
||||
dev_data->packet.address = 0;
|
||||
if (dev_config->jedec_cmds->id.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->id);
|
||||
dev_data->packet.data_buf = id;
|
||||
dev_data->packet.num_bytes = JESD216_READ_ID_LEN;
|
||||
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
printk("mspi_transceive() failed: %d\n", rc);
|
||||
return rc;
|
||||
LOG_ERR("Read JEDEC ID failed: %d\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -387,18 +458,24 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest,
|
|||
return rc;
|
||||
}
|
||||
|
||||
dev_data->xfer.cmd_length = 2;
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->xfer.rx_dummy = 20;
|
||||
dev_data->packet.dir = MSPI_RX;
|
||||
dev_data->packet.cmd = JESD216_OCMD_READ_SFDP;
|
||||
if (dev_config->jedec_cmds->sfdp.force_single) {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
} else {
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->sfdp);
|
||||
dev_data->packet.address = addr;
|
||||
dev_data->packet.data_buf = dest;
|
||||
dev_data->packet.num_bytes = size;
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
printk("JESD216_OCMD_READ_SFDP xfer failed: %d\n", rc);
|
||||
printk("Read SFDP xfer failed: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -409,9 +486,7 @@ static int api_sfdp_read(const struct device *dev, off_t addr, void *dest,
|
|||
|
||||
static int api_read_jedec_id(const struct device *dev, uint8_t *id)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = acquire(dev);
|
||||
int rc = acquire(dev);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
@ -439,85 +514,191 @@ static int dev_pm_action_cb(const struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int quad_enable_set(const struct device *dev, bool enable)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
int rc;
|
||||
|
||||
flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to set write enable: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (dev_config->dw15_qer == JESD216_DW15_QER_VAL_S1B6) {
|
||||
const struct flash_mspi_nor_cmd cmd_status = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_WRSR,
|
||||
.cmd_length = 1,
|
||||
};
|
||||
uint8_t mode_payload = enable ? BIT(6) : 0;
|
||||
|
||||
flash_mspi_command_set(dev, &cmd_status);
|
||||
dev_data->packet.data_buf = &mode_payload;
|
||||
dev_data->packet.num_bytes = sizeof(mode_payload);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to enable/disable quad mode: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
/* TODO: handle all DW15 QER values */
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
rc = wait_until_ready(dev, K_USEC(1));
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed waiting until device ready after enabling quad: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int default_io_mode(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode;
|
||||
int rc = 0;
|
||||
|
||||
/* For Quad 1-1-4 and 1-4-4, entering or leaving mode is defined in JEDEC216 BFP DW15 QER */
|
||||
if (io_mode == MSPI_IO_MODE_SINGLE) {
|
||||
rc = quad_enable_set(dev, false);
|
||||
} else if ((io_mode == MSPI_IO_MODE_QUAD_1_1_4) || (io_mode == MSPI_IO_MODE_QUAD_1_4_4)) {
|
||||
rc = quad_enable_set(dev, true);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to modify Quad Enable bit: %d", rc);
|
||||
}
|
||||
|
||||
if ((dev_config->quirks != NULL) && (dev_config->quirks->post_switch_mode != NULL)) {
|
||||
rc = dev_config->quirks->post_switch_mode(dev);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to change IO mode: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return dev_cfg_apply(dev, &dev_config->mspi_nor_cfg);
|
||||
}
|
||||
|
||||
#if defined(WITH_RESET_GPIO)
|
||||
static int gpio_reset(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
int rc;
|
||||
|
||||
if (dev_config->reset.port) {
|
||||
if (!gpio_is_ready_dt(&dev_config->reset)) {
|
||||
LOG_ERR("Device %s is not ready",
|
||||
dev_config->reset.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure_dt(&dev_config->reset,
|
||||
GPIO_OUTPUT_ACTIVE);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to activate RESET: %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev_config->reset_pulse_us != 0) {
|
||||
k_busy_wait(dev_config->reset_pulse_us);
|
||||
}
|
||||
|
||||
rc = gpio_pin_set_dt(&dev_config->reset, 0);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to deactivate RESET: %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev_config->reset_recovery_us != 0) {
|
||||
k_busy_wait(dev_config->reset_recovery_us);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int flash_chip_init(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
struct mspi_dev_cfg init_dev_cfg = dev_config->mspi_cfg;
|
||||
enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode;
|
||||
uint8_t id[JESD216_READ_ID_LEN] = {0};
|
||||
int rc;
|
||||
|
||||
init_dev_cfg.freq = MHZ(1);
|
||||
init_dev_cfg.io_mode = MSPI_IO_MODE_SINGLE;
|
||||
rc = dev_cfg_apply(dev, &dev_config->mspi_nor_init_cfg);
|
||||
|
||||
rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id,
|
||||
MSPI_DEVICE_CONFIG_ALL, &init_dev_cfg);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to set initial device config: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_data->xfer.xfer_mode = MSPI_PIO;
|
||||
dev_data->xfer.packets = &dev_data->packet;
|
||||
dev_data->xfer.num_packet = 1;
|
||||
dev_data->xfer.timeout = 10;
|
||||
/* Some chips reuse RESET pin for data in Quad modes:
|
||||
* force single line mode before resetting.
|
||||
*/
|
||||
if ((io_mode == MSPI_IO_MODE_SINGLE) || (io_mode == MSPI_IO_MODE_QUAD_1_1_4) ||
|
||||
(io_mode == MSPI_IO_MODE_QUAD_1_4_4)) {
|
||||
rc = quad_enable_set(dev, false);
|
||||
|
||||
dev_data->xfer.cmd_length = 1;
|
||||
dev_data->xfer.addr_length = 0;
|
||||
dev_data->xfer.tx_dummy = 0;
|
||||
dev_data->xfer.rx_dummy = 0;
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to switch to single line mode: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_data->packet.dir = MSPI_RX;
|
||||
dev_data->packet.cmd = JESD216_CMD_READ_ID;
|
||||
rc = wait_until_ready(dev, K_USEC(1));
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed waiting for device after switch to single line: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WITH_RESET_GPIO)
|
||||
rc = gpio_reset(dev);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to reset with GPIO: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].id);
|
||||
dev_data->packet.data_buf = id;
|
||||
dev_data->packet.num_bytes = sizeof(id);
|
||||
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to read JEDEC ID in single line mode: %d", rc);
|
||||
LOG_ERR("Failed to read JEDEC ID in initial line mode: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the read ID does not match the one from DTS, assume the flash
|
||||
* is already in the Octa I/O mode, so switching it is not needed.
|
||||
rc = default_io_mode(dev);
|
||||
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to switch to default io mode: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Reading JEDEC ID for mode that forces single lane would be redundant,
|
||||
* since it switches back to single lane mode. Use ID from previous read.
|
||||
*/
|
||||
if (memcmp(id, dev_config->jedec_id, sizeof(id)) == 0) {
|
||||
static const uint8_t enable_sopi[] = { 0x01 };
|
||||
|
||||
dev_data->packet.dir = MSPI_TX;
|
||||
dev_data->packet.cmd = SPI_NOR_CMD_WREN;
|
||||
dev_data->packet.num_bytes = 0;
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (!dev_config->jedec_cmds->id.force_single) {
|
||||
rc = read_jedec_id(dev, id);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("SPI_NOR_CMD_WREN xfer failed: %d", rc);
|
||||
LOG_ERR("Failed to read JEDEC ID in final line mode: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_data->xfer.addr_length = 4;
|
||||
dev_data->packet.cmd = SPI_NOR_CMD_WR_CFGREG2;
|
||||
dev_data->packet.address = 0;
|
||||
dev_data->packet.data_buf = (uint8_t *)&enable_sopi;
|
||||
dev_data->packet.num_bytes = sizeof(enable_sopi);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
printk("SPI_NOR_CMD_WR_CFGREG2 xfer failed: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id,
|
||||
MSPI_DEVICE_CONFIG_ALL, &dev_config->mspi_cfg);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to set device config: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = read_jedec_id(dev, id);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (memcmp(id, dev_config->jedec_id, sizeof(id)) != 0) {
|
||||
|
@ -555,37 +736,6 @@ static int drv_init(const struct device *dev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
#if defined(WITH_RESET_GPIO)
|
||||
if (dev_config->reset.port) {
|
||||
if (!gpio_is_ready_dt(&dev_config->reset)) {
|
||||
LOG_ERR("Device %s is not ready",
|
||||
dev_config->reset.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure_dt(&dev_config->reset,
|
||||
GPIO_OUTPUT_ACTIVE);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to activate RESET: %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev_config->reset_pulse_us != 0) {
|
||||
k_busy_wait(dev_config->reset_pulse_us);
|
||||
}
|
||||
|
||||
rc = gpio_pin_set_dt(&dev_config->reset, 0);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Failed to deactivate RESET: %d", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (dev_config->reset_recovery_us != 0) {
|
||||
k_busy_wait(dev_config->reset_recovery_us);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = pm_device_runtime_get(dev_config->bus);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("pm_device_runtime_get() failed: %d", rc);
|
||||
|
@ -624,7 +774,27 @@ static DEVICE_API(flash, drv_api) = {
|
|||
#endif
|
||||
};
|
||||
|
||||
#define FLASH_INITIAL_CONFIG(inst) \
|
||||
{ \
|
||||
.ce_num = DT_INST_PROP_OR(inst, mspi_hardware_ce_num, 0), \
|
||||
.freq = MIN(DT_INST_PROP(inst, mspi_max_frequency), MHZ(50)), \
|
||||
.io_mode = MSPI_IO_MODE_SINGLE, \
|
||||
.data_rate = MSPI_DATA_RATE_SINGLE, \
|
||||
.cpp = MSPI_CPP_MODE_0, \
|
||||
.endian = MSPI_XFER_BIG_ENDIAN, \
|
||||
.ce_polarity = MSPI_CE_ACTIVE_LOW, \
|
||||
.dqs_enable = false, \
|
||||
}
|
||||
|
||||
#define FLASH_SIZE_INST(inst) (DT_INST_PROP(inst, size) / 8)
|
||||
#define FLASH_CMDS(inst) &commands[DT_INST_ENUM_IDX(inst, mspi_io_mode)]
|
||||
#define FLASH_QUIRKS(inst) FLASH_MSPI_QUIRKS_GET(DT_DRV_INST(inst))
|
||||
|
||||
#define FLASH_DW15_QER_VAL(inst) _CONCAT(JESD216_DW15_QER_VAL_, \
|
||||
DT_INST_STRING_TOKEN(inst, quad_enable_requirements))
|
||||
#define FLASH_DW15_QER(inst) COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, quad_enable_requirements), \
|
||||
(FLASH_DW15_QER_VAL(inst)), (JESD216_DW15_QER_VAL_NONE))
|
||||
|
||||
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0,
|
||||
|
@ -651,17 +821,22 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) ==
|
|||
#endif
|
||||
|
||||
#define FLASH_MSPI_NOR_INST(inst) \
|
||||
BUILD_ASSERT(DT_INST_ENUM_IDX(inst, mspi_io_mode) == \
|
||||
MSPI_IO_MODE_OCTAL, \
|
||||
"Only Octal I/O mode is supported for now"); \
|
||||
BUILD_ASSERT((DT_INST_ENUM_IDX(inst, mspi_io_mode) == \
|
||||
MSPI_IO_MODE_SINGLE) || \
|
||||
(DT_INST_ENUM_IDX(inst, mspi_io_mode) == \
|
||||
MSPI_IO_MODE_QUAD_1_4_4) || \
|
||||
(DT_INST_ENUM_IDX(inst, mspi_io_mode) == \
|
||||
MSPI_IO_MODE_OCTAL), \
|
||||
"Only 1x, 1-4-4 and 8x I/O modes are supported for now"); \
|
||||
PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \
|
||||
static struct flash_mspi_nor_data dev##inst##_data; \
|
||||
static const struct flash_mspi_nor_config dev##inst##_config = { \
|
||||
.bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \
|
||||
.flash_size = FLASH_SIZE_INST(inst), \
|
||||
.mspi_id = MSPI_DEVICE_ID_DT_INST(inst), \
|
||||
.mspi_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \
|
||||
.mspi_cfg_mask = DT_PROP(DT_INST_BUS(inst), \
|
||||
.mspi_nor_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \
|
||||
.mspi_nor_init_cfg = FLASH_INITIAL_CONFIG(inst), \
|
||||
.mspi_nor_cfg_mask = DT_PROP(DT_INST_BUS(inst), \
|
||||
software_multiperipheral) \
|
||||
? MSPI_DEVICE_CONFIG_ALL \
|
||||
: MSPI_DEVICE_CONFIG_NONE, \
|
||||
|
@ -675,6 +850,9 @@ BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) ==
|
|||
/ 1000,)) \
|
||||
FLASH_PAGE_LAYOUT_DEFINE(inst) \
|
||||
.jedec_id = DT_INST_PROP(inst, jedec_id), \
|
||||
.jedec_cmds = FLASH_CMDS(inst), \
|
||||
.quirks = FLASH_QUIRKS(inst), \
|
||||
.dw15_qer = FLASH_DW15_QER(inst), \
|
||||
}; \
|
||||
FLASH_PAGE_LAYOUT_CHECK(inst) \
|
||||
DEVICE_DT_INST_DEFINE(inst, \
|
||||
|
|
248
drivers/flash/flash_mspi_nor.h
Normal file
248
drivers/flash/flash_mspi_nor.h
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_MSPI_NOR_H__
|
||||
#define __FLASH_MSPI_NOR_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/drivers/mspi.h>
|
||||
#include "jesd216.h"
|
||||
#include "spi_nor.h"
|
||||
|
||||
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios)
|
||||
#define WITH_RESET_GPIO 1
|
||||
#endif
|
||||
|
||||
struct flash_mspi_nor_config {
|
||||
const struct device *bus;
|
||||
uint32_t flash_size;
|
||||
struct mspi_dev_id mspi_id;
|
||||
struct mspi_dev_cfg mspi_nor_cfg;
|
||||
struct mspi_dev_cfg mspi_nor_init_cfg;
|
||||
enum mspi_dev_cfg_mask mspi_nor_cfg_mask;
|
||||
#if defined(CONFIG_MSPI_XIP)
|
||||
struct mspi_xip_cfg xip_cfg;
|
||||
#endif
|
||||
#if defined(WITH_RESET_GPIO)
|
||||
struct gpio_dt_spec reset;
|
||||
uint32_t reset_pulse_us;
|
||||
uint32_t reset_recovery_us;
|
||||
#endif
|
||||
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
||||
struct flash_pages_layout layout;
|
||||
#endif
|
||||
uint8_t jedec_id[SPI_NOR_MAX_ID_LEN];
|
||||
struct flash_mspi_nor_cmds *jedec_cmds;
|
||||
struct flash_mspi_nor_quirks *quirks;
|
||||
uint8_t dw15_qer;
|
||||
};
|
||||
|
||||
struct flash_mspi_nor_data {
|
||||
struct k_sem acquired;
|
||||
struct mspi_xfer_packet packet;
|
||||
struct mspi_xfer xfer;
|
||||
struct mspi_dev_cfg *curr_cfg;
|
||||
};
|
||||
|
||||
struct flash_mspi_nor_cmd {
|
||||
enum mspi_xfer_direction dir;
|
||||
uint32_t cmd;
|
||||
uint16_t tx_dummy;
|
||||
uint16_t rx_dummy;
|
||||
uint8_t cmd_length;
|
||||
uint8_t addr_length;
|
||||
bool force_single;
|
||||
};
|
||||
|
||||
struct flash_mspi_nor_cmds {
|
||||
struct flash_mspi_nor_cmd id;
|
||||
struct flash_mspi_nor_cmd write_en;
|
||||
struct flash_mspi_nor_cmd read;
|
||||
struct flash_mspi_nor_cmd status;
|
||||
struct flash_mspi_nor_cmd config;
|
||||
struct flash_mspi_nor_cmd page_program;
|
||||
struct flash_mspi_nor_cmd sector_erase;
|
||||
struct flash_mspi_nor_cmd chip_erase;
|
||||
struct flash_mspi_nor_cmd sfdp;
|
||||
};
|
||||
|
||||
struct flash_mspi_nor_cmds commands[] = {
|
||||
[MSPI_IO_MODE_SINGLE] = {
|
||||
.id = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_CMD_READ_ID,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.write_en = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_WREN,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.read = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_READ_FAST,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
.rx_dummy = 8,
|
||||
},
|
||||
.status = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_RDSR,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.config = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_RDCR,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.page_program = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_PP,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
},
|
||||
.sector_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_SE,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
},
|
||||
.chip_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_CE,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.sfdp = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_CMD_READ_SFDP,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
.rx_dummy = 8,
|
||||
},
|
||||
},
|
||||
[MSPI_IO_MODE_QUAD_1_4_4] = {
|
||||
.id = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_CMD_READ_ID,
|
||||
.cmd_length = 1,
|
||||
.force_single = true,
|
||||
},
|
||||
.write_en = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_WREN,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.read = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_4READ,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
.rx_dummy = 6,
|
||||
},
|
||||
.status = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_RDSR,
|
||||
.cmd_length = 1,
|
||||
.force_single = true,
|
||||
},
|
||||
.config = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_CMD_RDCR,
|
||||
.cmd_length = 1,
|
||||
.force_single = true,
|
||||
},
|
||||
.page_program = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_PP_1_4_4,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
},
|
||||
.sector_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_SE,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
.force_single = true,
|
||||
},
|
||||
.chip_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_CE,
|
||||
.cmd_length = 1,
|
||||
},
|
||||
.sfdp = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_CMD_READ_SFDP,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 3,
|
||||
.rx_dummy = 8,
|
||||
.force_single = true,
|
||||
},
|
||||
},
|
||||
[MSPI_IO_MODE_OCTAL] = {
|
||||
.id = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_OCMD_READ_ID,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
.rx_dummy = 4
|
||||
},
|
||||
.write_en = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_OCMD_WREN,
|
||||
.cmd_length = 2,
|
||||
},
|
||||
.read = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_OCMD_RD,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
.rx_dummy = 20,
|
||||
},
|
||||
.status = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = SPI_NOR_OCMD_RDSR,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
.rx_dummy = 4,
|
||||
},
|
||||
.page_program = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_OCMD_PAGE_PRG,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
},
|
||||
.sector_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_OCMD_SE,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
},
|
||||
.chip_erase = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_OCMD_CE,
|
||||
.cmd_length = 2,
|
||||
},
|
||||
.sfdp = {
|
||||
.dir = MSPI_RX,
|
||||
.cmd = JESD216_OCMD_READ_SFDP,
|
||||
.cmd_length = 2,
|
||||
.addr_length = 4,
|
||||
.rx_dummy = 20,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
void flash_mspi_command_set(const struct device *dev, const struct flash_mspi_nor_cmd *cmd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__FLASH_MSPI_NOR_H__*/
|
185
drivers/flash/flash_mspi_nor_quirks.h
Normal file
185
drivers/flash/flash_mspi_nor_quirks.h
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_MSPI_NOR_QUIRKS_H__
|
||||
#define __FLASH_MSPI_NOR_QUIRKS_H__
|
||||
|
||||
/* Flash chip specific quirks */
|
||||
struct flash_mspi_nor_quirks {
|
||||
/* Called after switching to default IO mode. */
|
||||
int (*post_switch_mode)(const struct device *dev);
|
||||
};
|
||||
|
||||
/* Extend this macro when adding new flash chip with quirks */
|
||||
#define FLASH_MSPI_QUIRKS_GET(node) \
|
||||
COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25r, okay), \
|
||||
(&flash_quirks_mxicy_mx25r), \
|
||||
(COND_CODE_1(DT_NODE_HAS_COMPAT_STATUS(node, mxicy_mx25u, okay), \
|
||||
(&flash_quirks_mxicy_mx25u), \
|
||||
(NULL))))
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25r)
|
||||
|
||||
#define MXICY_MX25R_LH_MASK BIT(1)
|
||||
#define MXICY_MX25R_QE_MASK BIT(6)
|
||||
#define MXICY_MX25R_REGS_LEN 3
|
||||
|
||||
static uint8_t mxicy_mx25r_hp_payload[MXICY_MX25R_REGS_LEN] = {
|
||||
MXICY_MX25R_QE_MASK, 0x0, MXICY_MX25R_LH_MASK
|
||||
};
|
||||
|
||||
/* For quad io mode above 8 MHz and single io mode above 33 MHz,
|
||||
* high performance mode needs to be enabled.
|
||||
*/
|
||||
static inline bool needs_hp(enum mspi_io_mode io_mode, uint32_t freq)
|
||||
{
|
||||
if ((io_mode == MSPI_IO_MODE_QUAD_1_1_4) || (io_mode == MSPI_IO_MODE_QUAD_1_4_4)) {
|
||||
if (freq > MHZ(8)) {
|
||||
return true;
|
||||
}
|
||||
} else if (io_mode == MSPI_IO_MODE_SINGLE) {
|
||||
if (freq > MHZ(33)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int mxicy_mx25r_post_switch_mode(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode;
|
||||
uint32_t freq = dev_config->mspi_nor_cfg.freq;
|
||||
int rc;
|
||||
uint8_t status;
|
||||
uint8_t config[MXICY_MX25R_REGS_LEN - 1];
|
||||
|
||||
if (!needs_hp(io_mode, freq)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait for previous write to finish */
|
||||
do {
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->status);
|
||||
dev_data->packet.data_buf = &status;
|
||||
dev_data->packet.num_bytes = sizeof(status);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
} while (status & SPI_NOR_WIP_BIT);
|
||||
|
||||
/* Write enable */
|
||||
flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write status and config registers */
|
||||
const struct flash_mspi_nor_cmd cmd_status = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_WRSR,
|
||||
.cmd_length = 1,
|
||||
};
|
||||
|
||||
flash_mspi_command_set(dev, &cmd_status);
|
||||
dev_data->packet.data_buf = mxicy_mx25r_hp_payload;
|
||||
dev_data->packet.num_bytes = sizeof(mxicy_mx25r_hp_payload);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for write to end and verify status register */
|
||||
do {
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->status);
|
||||
dev_data->packet.data_buf = &status;
|
||||
dev_data->packet.num_bytes = sizeof(status);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
} while (status & SPI_NOR_WIP_BIT);
|
||||
|
||||
if (status != mxicy_mx25r_hp_payload[0]) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Verify configuration registers */
|
||||
flash_mspi_command_set(dev, &dev_config->jedec_cmds->config);
|
||||
dev_data->packet.data_buf = config;
|
||||
dev_data->packet.num_bytes = sizeof(config);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MXICY_MX25R_REGS_LEN - 1; i++) {
|
||||
if (config[i] != mxicy_mx25r_hp_payload[i + 1]) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25r = {
|
||||
.post_switch_mode = mxicy_mx25r_post_switch_mode,
|
||||
};
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25r) */
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25u)
|
||||
|
||||
#define MXICY_MX25R_OE_MASK BIT(0)
|
||||
|
||||
static uint8_t mxicy_mx25u_oe_payload = MXICY_MX25R_OE_MASK;
|
||||
|
||||
static inline int mxicy_mx25u_post_switch_mode(const struct device *dev)
|
||||
{
|
||||
const struct flash_mspi_nor_config *dev_config = dev->config;
|
||||
struct flash_mspi_nor_data *dev_data = dev->data;
|
||||
enum mspi_io_mode io_mode = dev_config->mspi_nor_cfg.io_mode;
|
||||
int rc;
|
||||
|
||||
if (io_mode != MSPI_IO_MODE_OCTAL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Write enable */
|
||||
flash_mspi_command_set(dev, &commands[MSPI_IO_MODE_SINGLE].write_en);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
||||
&dev_data->xfer);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write config register 2 */
|
||||
const struct flash_mspi_nor_cmd cmd_status = {
|
||||
.dir = MSPI_TX,
|
||||
.cmd = SPI_NOR_CMD_WR_CFGREG2,
|
||||
.cmd_length = 1,
|
||||
.addr_length = 4,
|
||||
};
|
||||
|
||||
flash_mspi_command_set(dev, &cmd_status);
|
||||
dev_data->packet.data_buf = &mxicy_mx25u_oe_payload;
|
||||
dev_data->packet.num_bytes = sizeof(mxicy_mx25u_oe_payload);
|
||||
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, &dev_data->xfer);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct flash_mspi_nor_quirks flash_quirks_mxicy_mx25u = {
|
||||
.post_switch_mode = mxicy_mx25u_post_switch_mode,
|
||||
};
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(mxicy_mx25u) */
|
||||
|
||||
#endif /*__FLASH_MSPI_NOR_QUIRKS_H__*/
|
Loading…
Add table
Add a link
Reference in a new issue