Add a flash driver intended to handle various flash devices connected over MSPI bus as long as they support JEDEC SFDP. This is an initial commit providing only basic operations in Octal I/O mode with some hard-coded values for Macronix MX25Ux series chips. Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
687 lines
17 KiB
C
687 lines
17 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#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"
|
|
|
|
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
|
|
|
|
struct flash_mspi_nor_data {
|
|
struct k_sem acquired;
|
|
struct mspi_xfer_packet packet;
|
|
struct mspi_xfer xfer;
|
|
};
|
|
|
|
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];
|
|
};
|
|
|
|
static int acquire(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;
|
|
|
|
k_sem_take(&dev_data->acquired, K_FOREVER);
|
|
|
|
rc = pm_device_runtime_get(dev_config->bus);
|
|
if (rc < 0) {
|
|
LOG_ERR("pm_device_runtime_get() failed: %d", rc);
|
|
} else {
|
|
/* This acquires the MSPI controller and reconfigures it
|
|
* 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);
|
|
if (rc < 0) {
|
|
LOG_ERR("mspi_dev_config() failed: %d", rc);
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
(void)pm_device_runtime_put(dev_config->bus);
|
|
}
|
|
|
|
k_sem_give(&dev_data->acquired);
|
|
return rc;
|
|
}
|
|
|
|
static void release(const struct device *dev)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
struct flash_mspi_nor_data *dev_data = dev->data;
|
|
|
|
/* This releases the MSPI controller. */
|
|
(void)mspi_get_channel_status(dev_config->bus, 0);
|
|
|
|
(void)pm_device_runtime_put(dev_config->bus);
|
|
|
|
k_sem_give(&dev_data->acquired);
|
|
}
|
|
|
|
static inline uint32_t dev_flash_size(const struct device *dev)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
|
|
return dev_config->flash_size;
|
|
}
|
|
|
|
static inline uint16_t dev_page_size(const struct device *dev)
|
|
{
|
|
return SPI_NOR_PAGE_SIZE;
|
|
}
|
|
|
|
static int api_read(const struct device *dev, off_t addr, void *dest,
|
|
size_t size)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
struct flash_mspi_nor_data *dev_data = dev->data;
|
|
const uint32_t flash_size = dev_flash_size(dev);
|
|
int rc;
|
|
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if ((addr < 0) || ((addr + size) > flash_size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = acquire(dev);
|
|
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;
|
|
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);
|
|
|
|
release(dev);
|
|
|
|
if (rc < 0) {
|
|
LOG_ERR("SPI_NOR_OCMD_RD xfer failed: %d", rc);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wait_until_ready(const struct device *dev, k_timeout_t poll_period)
|
|
{
|
|
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;
|
|
|
|
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);
|
|
if (rc < 0) {
|
|
LOG_ERR("SPI_NOR_OCMD_RDSR xfer failed: %d", rc);
|
|
return rc;
|
|
}
|
|
if (!(status_reg & SPI_NOR_WIP_BIT)) {
|
|
break;
|
|
}
|
|
|
|
k_sleep(poll_period);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int api_write(const struct device *dev, off_t addr, const void *src,
|
|
size_t size)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
struct flash_mspi_nor_data *dev_data = dev->data;
|
|
const uint32_t flash_size = dev_flash_size(dev);
|
|
const uint16_t page_size = dev_page_size(dev);
|
|
int rc;
|
|
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if ((addr < 0) || ((addr + size) > flash_size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = acquire(dev);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
while (size > 0) {
|
|
/* Split write into parts, each within one page only. */
|
|
uint16_t page_offset = (uint16_t)(addr % page_size);
|
|
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);
|
|
break;
|
|
}
|
|
|
|
dev_data->xfer.addr_length = 4;
|
|
dev_data->packet.cmd = SPI_NOR_OCMD_PAGE_PRG;
|
|
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);
|
|
break;
|
|
}
|
|
|
|
addr += to_write;
|
|
src = (const uint8_t *)src + to_write;
|
|
size -= to_write;
|
|
|
|
rc = wait_until_ready(dev, K_MSEC(1));
|
|
if (rc < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
release(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int api_erase(const struct device *dev, off_t addr, size_t size)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
struct flash_mspi_nor_data *dev_data = dev->data;
|
|
const uint32_t flash_size = dev_flash_size(dev);
|
|
int rc = 0;
|
|
|
|
if ((addr < 0) || ((addr + size) > flash_size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!SPI_NOR_IS_SECTOR_ALIGNED(addr)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((size % SPI_NOR_SECTOR_SIZE) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = acquire(dev);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
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);
|
|
if (rc < 0) {
|
|
LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc);
|
|
break;
|
|
}
|
|
|
|
if (size == flash_size) {
|
|
/* Chip erase. */
|
|
dev_data->xfer.addr_length = 0;
|
|
dev_data->packet.cmd = SPI_NOR_OCMD_CE;
|
|
|
|
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;
|
|
|
|
addr += SPI_NOR_SECTOR_SIZE;
|
|
size -= SPI_NOR_SECTOR_SIZE;
|
|
}
|
|
rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id,
|
|
&dev_data->xfer);
|
|
if (rc < 0) {
|
|
LOG_ERR("Erase command 0x%02x xfer failed: %d",
|
|
dev_data->packet.cmd, rc);
|
|
break;
|
|
}
|
|
|
|
rc = wait_until_ready(dev, K_MSEC(1));
|
|
if (rc < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
release(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static const
|
|
struct flash_parameters *api_get_parameters(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
static const struct flash_parameters parameters = {
|
|
.write_block_size = 1,
|
|
.erase_value = 0xff,
|
|
};
|
|
|
|
return ¶meters;
|
|
}
|
|
|
|
static int read_jedec_id(const struct device *dev, uint8_t *id)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
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;
|
|
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;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
static void api_page_layout(const struct device *dev,
|
|
const struct flash_pages_layout **layout,
|
|
size_t *layout_size)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
|
|
*layout = &dev_config->layout;
|
|
*layout_size = 1;
|
|
}
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
#if defined(CONFIG_FLASH_JESD216_API)
|
|
static int api_sfdp_read(const struct device *dev, off_t addr, void *dest,
|
|
size_t size)
|
|
{
|
|
const struct flash_mspi_nor_config *dev_config = dev->config;
|
|
struct flash_mspi_nor_data *dev_data = dev->data;
|
|
int rc;
|
|
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
rc = acquire(dev);
|
|
if (rc < 0) {
|
|
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;
|
|
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);
|
|
return rc;
|
|
}
|
|
|
|
release(dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int api_read_jedec_id(const struct device *dev, uint8_t *id)
|
|
{
|
|
int rc = 0;
|
|
|
|
rc = acquire(dev);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
rc = read_jedec_id(dev, id);
|
|
|
|
release(dev);
|
|
|
|
return rc;
|
|
}
|
|
#endif /* CONFIG_FLASH_JESD216_API */
|
|
|
|
static int dev_pm_action_cb(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
break;
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
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 = 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;
|
|
|
|
dev_data->xfer.cmd_length = 1;
|
|
dev_data->xfer.addr_length = 0;
|
|
dev_data->xfer.tx_dummy = 0;
|
|
dev_data->xfer.rx_dummy = 0;
|
|
|
|
dev_data->packet.dir = MSPI_RX;
|
|
dev_data->packet.cmd = JESD216_CMD_READ_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);
|
|
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.
|
|
*/
|
|
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 (rc < 0) {
|
|
LOG_ERR("SPI_NOR_CMD_WREN xfer failed: %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) {
|
|
LOG_ERR("JEDEC ID mismatch, read: %02x %02x %02x, "
|
|
"expected: %02x %02x %02x",
|
|
id[0], id[1], id[2],
|
|
dev_config->jedec_id[0],
|
|
dev_config->jedec_id[1],
|
|
dev_config->jedec_id[2]);
|
|
return -ENODEV;
|
|
}
|
|
|
|
#if defined(CONFIG_MSPI_XIP)
|
|
/* Enable XIP access for this chip if specified so in DT. */
|
|
if (dev_config->xip_cfg.enable) {
|
|
rc = mspi_xip_config(dev_config->bus, &dev_config->mspi_id,
|
|
&dev_config->xip_cfg);
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int drv_init(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 (!device_is_ready(dev_config->bus)) {
|
|
LOG_ERR("Device %s is not ready", dev_config->bus->name);
|
|
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);
|
|
return rc;
|
|
}
|
|
|
|
rc = flash_chip_init(dev);
|
|
|
|
/* Release the MSPI controller - it was acquired by the call to
|
|
* mspi_dev_config() in flash_chip_init().
|
|
*/
|
|
(void)mspi_get_channel_status(dev_config->bus, 0);
|
|
|
|
(void)pm_device_runtime_put(dev_config->bus);
|
|
|
|
if (rc < 0) {
|
|
return rc;
|
|
}
|
|
|
|
k_sem_init(&dev_data->acquired, 1, K_SEM_MAX_LIMIT);
|
|
|
|
return pm_device_driver_init(dev, dev_pm_action_cb);
|
|
}
|
|
|
|
static DEVICE_API(flash, drv_api) = {
|
|
.read = api_read,
|
|
.write = api_write,
|
|
.erase = api_erase,
|
|
.get_parameters = api_get_parameters,
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
.page_layout = api_page_layout,
|
|
#endif
|
|
#if defined(CONFIG_FLASH_JESD216_API)
|
|
.sfdp_read = api_sfdp_read,
|
|
.read_jedec_id = api_read_jedec_id,
|
|
#endif
|
|
};
|
|
|
|
#define FLASH_SIZE_INST(inst) (DT_INST_PROP(inst, size) / 8)
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0,
|
|
"MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096");
|
|
#define FLASH_PAGE_LAYOUT_DEFINE(inst) \
|
|
.layout = { \
|
|
.pages_size = CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \
|
|
.pages_count = FLASH_SIZE_INST(inst) \
|
|
/ CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \
|
|
},
|
|
#define FLASH_PAGE_LAYOUT_CHECK(inst) \
|
|
BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \
|
|
"MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size, instance " #inst);
|
|
#else
|
|
#define FLASH_PAGE_LAYOUT_DEFINE(inst)
|
|
#define FLASH_PAGE_LAYOUT_CHECK(inst)
|
|
#endif
|
|
|
|
/* MSPI bus must be initialized before this device. */
|
|
#if (CONFIG_MSPI_INIT_PRIORITY < CONFIG_FLASH_INIT_PRIORITY)
|
|
#define INIT_PRIORITY CONFIG_FLASH_INIT_PRIORITY
|
|
#else
|
|
#define INIT_PRIORITY UTIL_INC(CONFIG_MSPI_INIT_PRIORITY)
|
|
#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"); \
|
|
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), \
|
|
software_multiperipheral) \
|
|
? MSPI_DEVICE_CONFIG_ALL \
|
|
: MSPI_DEVICE_CONFIG_NONE, \
|
|
IF_ENABLED(CONFIG_MSPI_XIP, \
|
|
(.xip_cfg = MSPI_XIP_CONFIG_DT_INST(inst),)) \
|
|
IF_ENABLED(WITH_RESET_GPIO, \
|
|
(.reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
|
|
.reset_pulse_us = DT_INST_PROP_OR(inst, t_reset_pulse, 0) \
|
|
/ 1000, \
|
|
.reset_recovery_us = DT_INST_PROP_OR(inst, t_reset_recovery, 0) \
|
|
/ 1000,)) \
|
|
FLASH_PAGE_LAYOUT_DEFINE(inst) \
|
|
.jedec_id = DT_INST_PROP(inst, jedec_id), \
|
|
}; \
|
|
FLASH_PAGE_LAYOUT_CHECK(inst) \
|
|
DEVICE_DT_INST_DEFINE(inst, \
|
|
drv_init, PM_DEVICE_DT_INST_GET(inst), \
|
|
&dev##inst##_data, &dev##inst##_config, \
|
|
POST_KERNEL, INIT_PRIORITY, \
|
|
&drv_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(FLASH_MSPI_NOR_INST)
|