You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
5.0 KiB
185 lines
5.0 KiB
/* |
|
* 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_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_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__*/
|
|
|