mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-07 17:31:57 +00:00
This reverts commit 8739517107
.
Pull Request #23437 was merged by mistake with an invalid manifest.
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
682 lines
16 KiB
C
682 lines
16 KiB
C
/*
|
|
* Copyright (c) 2019, Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <drivers/flash.h>
|
|
#include <init.h>
|
|
#include <string.h>
|
|
#include <logging/log.h>
|
|
|
|
#include "spi_nor.h"
|
|
#include "flash_priv.h"
|
|
#include <nrfx_qspi.h>
|
|
|
|
#define qspi_nor_config spi_nor_config
|
|
#define QSPI_NOR_MAX_ID_LEN SPI_NOR_MAX_ID_LEN
|
|
|
|
/* Status register bits */
|
|
#define QSPI_SECTOR_SIZE SPI_NOR_SECTOR_SIZE
|
|
#define QSPI_BLOCK_SIZE SPI_NOR_BLOCK_SIZE
|
|
|
|
/* instance 0 flash size in bytes */
|
|
#define INST_0_BYTES (DT_INST_0_NORDIC_QSPI_NOR_SIZE / 8)
|
|
|
|
LOG_MODULE_REGISTER(qspi_nor, CONFIG_FLASH_LOG_LEVEL);
|
|
|
|
/**
|
|
* @brief QSPI buffer structure
|
|
* Structure used both for TX and RX purposes.
|
|
*
|
|
* @param buf is a valid pointer to a data buffer.
|
|
* Can not be NULL.
|
|
* @param len is the length of the data to be handled.
|
|
* If no data to transmit/receive - pass 0.
|
|
*/
|
|
struct qspi_buf {
|
|
u8_t *buf;
|
|
size_t len;
|
|
};
|
|
|
|
/**
|
|
* @brief QSPI command structure
|
|
* Structure used for custom command usage.
|
|
*
|
|
* @param op_code is a command value (i.e 0x9F - get Jedec ID)
|
|
* @param tx_buf structure used for TX purposes. Can be NULL if not used.
|
|
* @param rx_buf structure used for RX purposes. Can be NULL if not used.
|
|
*/
|
|
struct qspi_cmd {
|
|
u8_t op_code;
|
|
const struct qspi_buf *tx_buf;
|
|
const struct qspi_buf *rx_buf;
|
|
};
|
|
|
|
/**
|
|
* @brief Structure for defining the QSPI NOR access
|
|
* @param sem The semaphore to access to the flash
|
|
* @param sync The semaphore to ensure that transfer has finished
|
|
* @param write_protection Indicates if write protection for flash
|
|
* device is enabled
|
|
*/
|
|
struct qspi_nor_data {
|
|
struct k_sem sem;
|
|
struct k_sem sync;
|
|
bool write_protection;
|
|
};
|
|
|
|
static inline int qspi_get_mode(bool cpol, bool cpha)
|
|
{
|
|
register int ret = -EINVAL;
|
|
|
|
if ((!cpol) && (!cpha)) {
|
|
ret = 0;
|
|
} else if (cpol && cpha) {
|
|
ret = 1;
|
|
}
|
|
__ASSERT(ret != -EINVAL, "Invalid QSPI mode");
|
|
return ret;
|
|
}
|
|
|
|
static inline bool qspi_is_used_write_quad_mode(nrf_qspi_writeoc_t lines)
|
|
{
|
|
switch (lines) {
|
|
case NRF_QSPI_WRITEOC_PP4IO:
|
|
case NRF_QSPI_WRITEOC_PP4O:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool qspi_is_used_read_quad_mode(nrf_qspi_readoc_t lines)
|
|
{
|
|
switch (lines) {
|
|
case NRF_QSPI_READOC_READ4IO:
|
|
case NRF_QSPI_READOC_READ4O:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline int qspi_get_lines_write(u8_t lines)
|
|
{
|
|
register int ret = -EINVAL;
|
|
|
|
switch (lines) {
|
|
case 3:
|
|
ret = NRF_QSPI_WRITEOC_PP4IO;
|
|
break;
|
|
case 2:
|
|
ret = NRF_QSPI_WRITEOC_PP4O;
|
|
break;
|
|
case 1:
|
|
ret = NRF_QSPI_WRITEOC_PP2O;
|
|
break;
|
|
case 0:
|
|
ret = NRF_QSPI_WRITEOC_PP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
__ASSERT(ret != -EINVAL, "Invalid QSPI write line");
|
|
return ret;
|
|
}
|
|
|
|
static inline int qspi_get_lines_read(u8_t lines)
|
|
{
|
|
register int ret = -EINVAL;
|
|
|
|
switch (lines) {
|
|
case 4:
|
|
ret = NRF_QSPI_READOC_READ4IO;
|
|
break;
|
|
case 3:
|
|
ret = NRF_QSPI_READOC_READ4O;
|
|
break;
|
|
case 2:
|
|
ret = NRF_QSPI_READOC_READ2IO;
|
|
break;
|
|
case 1:
|
|
ret = NRF_QSPI_READOC_READ2O;
|
|
break;
|
|
case 0:
|
|
ret = NRF_QSPI_READOC_FASTREAD;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
__ASSERT(ret != -EINVAL, "Invalid QSPI read line");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get QSPI prescaler
|
|
* Get supported frequency prescaler not exceeding the requested one.
|
|
*
|
|
* @param frequency - desired QSPI bus frequency
|
|
* @retval NRF_SPI_PRESCALER in case of success or;
|
|
* -EINVAL in case of failure
|
|
*/
|
|
static inline nrf_qspi_frequency_t get_nrf_qspi_prescaler(u32_t frequency)
|
|
{
|
|
register int ret = -EINVAL;
|
|
|
|
if (frequency < 2000000UL) {
|
|
ret = -EINVAL;
|
|
} else if (frequency >= 32000000UL) {
|
|
ret = NRF_QSPI_FREQ_32MDIV1;
|
|
} else {
|
|
ret = (nrf_qspi_frequency_t)((32000000UL / frequency) - 1);
|
|
}
|
|
|
|
__ASSERT(ret != -EINVAL, "Invalid QSPI frequency");
|
|
return ret;
|
|
}
|
|
|
|
static inline nrf_qspi_addrmode_t qspi_get_address_size(bool addr_size)
|
|
{
|
|
return addr_size ? NRF_QSPI_ADDRMODE_32BIT : NRF_QSPI_ADDRMODE_24BIT;
|
|
}
|
|
|
|
/**
|
|
* @brief Test whether offset is aligned.
|
|
*/
|
|
#define QSPI_IS_SECTOR_ALIGNED(_ofs) (((_ofs) & (QSPI_SECTOR_SIZE - 1U)) == 0)
|
|
#define QSPI_IS_BLOCK_ALIGNED(_ofs) (((_ofs) & (QSPI_BLOCK_SIZE - 1U)) == 0)
|
|
|
|
/**
|
|
* @brief Main configuration structure
|
|
*/
|
|
static struct qspi_nor_data qspi_nor_memory_data = {
|
|
.sem = Z_SEM_INITIALIZER(qspi_nor_memory_data.sem, 1, 1),
|
|
.sync = Z_SEM_INITIALIZER(qspi_nor_memory_data.sync, 0, 1),
|
|
};
|
|
|
|
/**
|
|
* @brief Converts NRFX return codes to the zephyr ones
|
|
*/
|
|
static inline int qspi_get_zephyr_ret_code(int res)
|
|
{
|
|
switch (res) {
|
|
case NRFX_SUCCESS:
|
|
return 0;
|
|
case NRFX_ERROR_INVALID_PARAM:
|
|
case NRFX_ERROR_INVALID_ADDR:
|
|
return -EINVAL;
|
|
case NRFX_ERROR_INVALID_STATE:
|
|
return -ECANCELED;
|
|
case NRFX_ERROR_BUSY:
|
|
case NRFX_ERROR_TIMEOUT:
|
|
default:
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
|
|
static inline struct qspi_nor_data *get_dev_data(struct device *dev)
|
|
{
|
|
return dev->driver_data;
|
|
}
|
|
|
|
static inline void qspi_lock(struct device *dev)
|
|
{
|
|
struct qspi_nor_data *dev_data = get_dev_data(dev);
|
|
|
|
k_sem_take(&dev_data->sem, K_FOREVER);
|
|
}
|
|
|
|
static inline void qspi_unlock(struct device *dev)
|
|
{
|
|
struct qspi_nor_data *dev_data = get_dev_data(dev);
|
|
|
|
k_sem_give(&dev_data->sem);
|
|
}
|
|
|
|
static inline void qspi_wait_for_completion(struct device *dev)
|
|
{
|
|
struct qspi_nor_data *dev_data = get_dev_data(dev);
|
|
|
|
k_sem_take(&dev_data->sync, K_FOREVER);
|
|
}
|
|
|
|
static inline void qspi_complete(struct device *dev)
|
|
{
|
|
struct qspi_nor_data *dev_data = get_dev_data(dev);
|
|
|
|
k_sem_give(&dev_data->sync);
|
|
}
|
|
|
|
/**
|
|
* @brief QSPI handler
|
|
*
|
|
* @param event Driver event type
|
|
* @param p_context Pointer to context. Use in interrupt handler.
|
|
* @retval None
|
|
*/
|
|
static void qspi_handler(nrfx_qspi_evt_t event, void *p_context)
|
|
{
|
|
struct device *dev = p_context;
|
|
|
|
if (event == NRFX_QSPI_EVENT_DONE) {
|
|
qspi_complete(dev);
|
|
qspi_unlock(dev);
|
|
}
|
|
}
|
|
|
|
|
|
/* QSPI send custom command */
|
|
static int qspi_send_cmd(struct device *dev, const struct qspi_cmd *cmd)
|
|
{
|
|
/* Check input parameters */
|
|
if (!cmd) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
qspi_lock(dev);
|
|
|
|
nrf_qspi_cinstr_conf_t cinstr_cfg = {
|
|
.opcode = cmd->op_code,
|
|
.io2_level = true,
|
|
.io3_level = true,
|
|
.wipwait = false,
|
|
.wren = true,
|
|
};
|
|
cinstr_cfg.length = sizeof(cmd->op_code);
|
|
if ((cmd->tx_buf != 0) && (cmd->rx_buf != 0)) {
|
|
cinstr_cfg.length += cmd->tx_buf->len + cmd->rx_buf->len;
|
|
} else if ((cmd->tx_buf != 0) && (cmd->rx_buf == 0)) {
|
|
cinstr_cfg.length += cmd->tx_buf->len;
|
|
} else if ((cmd->tx_buf == 0) && (cmd->rx_buf != 0)) {
|
|
cinstr_cfg.length += cmd->rx_buf->len;
|
|
}
|
|
|
|
int res = nrfx_qspi_cinstr_xfer(&cinstr_cfg, cmd->tx_buf->buf,
|
|
cmd->rx_buf->buf);
|
|
|
|
qspi_unlock(dev);
|
|
return qspi_get_zephyr_ret_code(res);
|
|
}
|
|
|
|
/* QSPI erase */
|
|
static int qspi_erase(struct device *dev, u32_t addr, u32_t size)
|
|
{
|
|
/* Check input parameters */
|
|
if (!size) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
int res = 0;
|
|
const struct qspi_nor_config *params = dev->config->config_info;
|
|
|
|
while (size) {
|
|
if (size == params->size) {
|
|
/* chip erase */
|
|
qspi_lock(dev);
|
|
res = nrfx_qspi_chip_erase();
|
|
qspi_wait_for_completion(dev);
|
|
size = 0;
|
|
} else if ((size >= QSPI_BLOCK_SIZE) &&
|
|
QSPI_IS_BLOCK_ALIGNED(addr)) {
|
|
/* 64 kB block erase */
|
|
qspi_lock(dev);
|
|
res = nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, addr);
|
|
qspi_wait_for_completion(dev);
|
|
addr += QSPI_BLOCK_SIZE;
|
|
size -= QSPI_BLOCK_SIZE;
|
|
} else if ((size >= QSPI_SECTOR_SIZE) &&
|
|
QSPI_IS_SECTOR_ALIGNED(addr)) {
|
|
/* 4kB sector erase */
|
|
qspi_lock(dev);
|
|
res = nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, addr);
|
|
qspi_wait_for_completion(dev);
|
|
addr += QSPI_SECTOR_SIZE;
|
|
size -= QSPI_SECTOR_SIZE;
|
|
} else {
|
|
/* minimal erase size is at least a sector size */
|
|
LOG_ERR("unsupported at 0x%lx size %zu", (long)addr, size);
|
|
return -EINVAL;
|
|
}
|
|
if (res != NRFX_SUCCESS) {
|
|
LOG_ERR("erase error at 0x%lx size %zu", (long)addr, size);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return qspi_get_zephyr_ret_code(res);
|
|
}
|
|
|
|
/**
|
|
* @brief Fills init struct
|
|
*
|
|
* @param config Pointer to the config struct provided by user
|
|
* @param initstruct Pointer to the configuration struct
|
|
* @retval None
|
|
*/
|
|
static inline void qspi_fill_init_struct(nrfx_qspi_config_t *initstruct)
|
|
{
|
|
/* Configure XIP offset */
|
|
initstruct->xip_offset = 0;
|
|
|
|
/* Configure pins */
|
|
initstruct->pins.sck_pin = DT_NORDIC_NRF_QSPI_QSPI_0_SCK_PIN;
|
|
initstruct->pins.csn_pin = DT_NORDIC_NRF_QSPI_QSPI_0_CSN_PINS_0;
|
|
initstruct->pins.io0_pin = DT_NORDIC_NRF_QSPI_QSPI_0_IO_PINS_0;
|
|
initstruct->pins.io1_pin = DT_NORDIC_NRF_QSPI_QSPI_0_IO_PINS_1;
|
|
initstruct->pins.io2_pin = DT_NORDIC_NRF_QSPI_QSPI_0_IO_PINS_2;
|
|
initstruct->pins.io3_pin = DT_NORDIC_NRF_QSPI_QSPI_0_IO_PINS_3;
|
|
|
|
/* Configure Protocol interface */
|
|
#ifdef DT_INST_0_NORDIC_QSPI_NOR_READOC_ENUM
|
|
initstruct->prot_if.readoc =
|
|
(nrf_qspi_writeoc_t)qspi_get_lines_read(DT_INST_0_NORDIC_QSPI_NOR_READOC_ENUM);
|
|
#else
|
|
initstruct->prot_if.readoc = NRF_QSPI_READOC_FASTREAD;
|
|
#endif
|
|
|
|
#ifdef DT_INST_0_NORDIC_QSPI_NOR_WRITEOC_ENUM
|
|
initstruct->prot_if.writeoc =
|
|
(nrf_qspi_writeoc_t)qspi_get_lines_write(DT_INST_0_NORDIC_QSPI_NOR_WRITEOC_ENUM);
|
|
#else
|
|
initstruct->prot_if.writeoc = NRF_QSPI_WRITEOC_PP;
|
|
#endif
|
|
initstruct->prot_if.addrmode =
|
|
qspi_get_address_size(DT_INST_0_NORDIC_QSPI_NOR_ADDRESS_SIZE_32);
|
|
|
|
initstruct->prot_if.dpmconfig = false;
|
|
|
|
/* Configure physical interface */
|
|
initstruct->phy_if.sck_freq =
|
|
get_nrf_qspi_prescaler(DT_INST_0_NORDIC_QSPI_NOR_SCK_FREQUENCY);
|
|
initstruct->phy_if.sck_delay = DT_INST_0_NORDIC_QSPI_NOR_SCK_DELAY;
|
|
initstruct->phy_if.spi_mode = qspi_get_mode(DT_INST_0_NORDIC_QSPI_NOR_CPOL,
|
|
DT_INST_0_NORDIC_QSPI_NOR_CPHA);
|
|
|
|
initstruct->phy_if.dpmen = false;
|
|
}
|
|
|
|
/* Configures QSPI memory for the transfer */
|
|
static int qspi_nrfx_configure(struct device *dev)
|
|
{
|
|
if (!dev) {
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Main config structure */
|
|
nrfx_qspi_config_t QSPIconfig;
|
|
|
|
qspi_fill_init_struct(&QSPIconfig);
|
|
|
|
int res = nrfx_qspi_init(&QSPIconfig, qspi_handler, dev);
|
|
|
|
if (res == NRFX_SUCCESS) {
|
|
/* If quad transfer was chosen - enable it now */
|
|
if ((qspi_is_used_write_quad_mode(QSPIconfig.prot_if.writeoc))
|
|
|| (qspi_is_used_read_quad_mode(QSPIconfig.prot_if.readoc))) {
|
|
|
|
/* WRITE ENABLE has to be sent before QUAR ENABLE */
|
|
struct qspi_cmd cmd = { .op_code = SPI_NOR_CMD_WREN };
|
|
|
|
if (qspi_send_cmd(dev, &cmd) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
u8_t tx = BIT(CONFIG_NORDIC_QSPI_NOR_QE_BIT);
|
|
|
|
const struct qspi_buf tx_buff = { .buf = &tx, .len = sizeof(tx), };
|
|
|
|
cmd.op_code = SPI_NOR_CMD_WRSR;
|
|
cmd.tx_buf = &tx_buff;
|
|
cmd.rx_buf = NULL;
|
|
|
|
if (qspi_send_cmd(dev, &cmd) != 0) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qspi_get_zephyr_ret_code(res);
|
|
}
|
|
|
|
/**
|
|
* @brief Retrieve the Flash JEDEC ID and compare it with the one expected
|
|
*
|
|
* @param dev The device structure
|
|
* @param flash_id The flash info structure which contains the
|
|
* expected JEDEC ID
|
|
* @return 0 on success, negative errno code otherwise
|
|
*/
|
|
static inline int qspi_nor_read_id(struct device *dev,
|
|
const struct qspi_nor_config *const flash_id)
|
|
{
|
|
u8_t rx_b[QSPI_NOR_MAX_ID_LEN];
|
|
const struct qspi_buf q_rx_buf = {
|
|
.buf = rx_b,
|
|
.len = QSPI_NOR_MAX_ID_LEN
|
|
};
|
|
const struct qspi_cmd cmd = {
|
|
.op_code = SPI_NOR_CMD_RDID,
|
|
.rx_buf = &q_rx_buf,
|
|
.tx_buf = NULL
|
|
};
|
|
|
|
if (qspi_send_cmd(dev, &cmd) != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (memcmp(flash_id->id, rx_b, QSPI_NOR_MAX_ID_LEN) != 0) {
|
|
LOG_ERR("flash id error. Extected: [%d %d %d], got: [%d %d %d]",
|
|
flash_id->id[0], flash_id->id[1], flash_id->id[2],
|
|
rx_b[0], rx_b[1], rx_b[2]);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int qspi_nor_read(struct device *dev, off_t addr, void *dest,
|
|
size_t size)
|
|
{
|
|
if (!dest) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* read size must be multiple of 4 bytes */
|
|
if (size % sizeof(uint32_t) ||
|
|
!(size > 0)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
const struct qspi_nor_config *params = dev->config->config_info;
|
|
|
|
/* should be between 0 and flash size */
|
|
if (addr >= params->size ||
|
|
addr < 0 ||
|
|
size > params->size ||
|
|
(addr) + size > params->size) {
|
|
LOG_ERR("read error: address or size "
|
|
"exceeds expected values."
|
|
"Addr: 0x%lx size %zu", (long)addr, size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
qspi_lock(dev);
|
|
|
|
int ret = nrfx_qspi_read(dest, size, addr);
|
|
|
|
qspi_wait_for_completion(dev);
|
|
return qspi_get_zephyr_ret_code(ret);
|
|
}
|
|
|
|
static int qspi_nor_write(struct device *dev, off_t addr, const void *src,
|
|
size_t size)
|
|
{
|
|
if (!src) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* write size must be multiple of 4 bytes */
|
|
if (size % sizeof(uint32_t) ||
|
|
!(size > 0)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct qspi_nor_data *const driver_data = dev->driver_data;
|
|
const struct qspi_nor_config *params = dev->config->config_info;
|
|
|
|
if (driver_data->write_protection) {
|
|
return -EACCES;
|
|
}
|
|
|
|
/* should be between 0 and flash size */
|
|
if (addr >= params->size ||
|
|
addr < 0 ||
|
|
size > params->size ||
|
|
(addr) + size > params->size) {
|
|
LOG_ERR("write error: address or size "
|
|
"exceeds expected values."
|
|
"Addr: 0x%lx size %zu", (long)addr, size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
qspi_lock(dev);
|
|
|
|
int ret = nrfx_qspi_write(src, size, addr);
|
|
|
|
qspi_wait_for_completion(dev);
|
|
return qspi_get_zephyr_ret_code(ret);
|
|
}
|
|
|
|
static int qspi_nor_erase(struct device *dev, off_t addr, size_t size)
|
|
{
|
|
struct qspi_nor_data *const driver_data = dev->driver_data;
|
|
const struct qspi_nor_config *params = dev->config->config_info;
|
|
|
|
if (driver_data->write_protection) {
|
|
return -EACCES;
|
|
}
|
|
|
|
/* should be between 0 and flash size */
|
|
if (addr >= params->size ||
|
|
addr < 0 ||
|
|
size > params->size ||
|
|
(addr) + size > params->size) {
|
|
LOG_ERR("erase error: address or size "
|
|
"exceeds expected values."
|
|
"Addr: 0x%lx size %zu", (long)addr, size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int ret = qspi_erase(dev, addr, size);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int qspi_nor_write_protection_set(struct device *dev,
|
|
bool write_protect)
|
|
{
|
|
struct qspi_nor_data *const driver_data = dev->driver_data;
|
|
|
|
int ret = 0;
|
|
struct qspi_cmd cmd = {
|
|
.op_code = ((write_protect) ? SPI_NOR_CMD_WRDI : SPI_NOR_CMD_WREN),
|
|
};
|
|
|
|
driver_data->write_protection = write_protect;
|
|
|
|
if (qspi_send_cmd(dev, &cmd) != 0) {
|
|
ret = -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Configure the flash
|
|
*
|
|
* @param dev The flash device structure
|
|
* @param info The flash info structure
|
|
* @return 0 on success, negative errno code otherwise
|
|
*/
|
|
static int qspi_nor_configure(struct device *dev)
|
|
{
|
|
const struct qspi_nor_config *params = dev->config->config_info;
|
|
|
|
int ret = qspi_nrfx_configure(dev);
|
|
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* now the spi bus is configured, we can verify the flash id */
|
|
if (qspi_nor_read_id(dev, params) != 0) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize and configure the flash
|
|
*
|
|
* @param name The flash name
|
|
* @return 0 on success, negative errno code otherwise
|
|
*/
|
|
static int qspi_nor_init(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_NORDIC_NRF_QSPI_QSPI_0_IRQ_0,
|
|
DT_NORDIC_NRF_QSPI_QSPI_0_IRQ_0_PRIORITY,
|
|
nrfx_isr, nrfx_qspi_irq_handler, 0);
|
|
return qspi_nor_configure(dev);
|
|
}
|
|
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
|
|
/* instance 0 page count */
|
|
#define LAYOUT_PAGES_COUNT (INST_0_BYTES / \
|
|
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE)
|
|
|
|
BUILD_ASSERT_MSG((CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE *
|
|
LAYOUT_PAGES_COUNT)
|
|
== INST_0_BYTES,
|
|
"QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size");
|
|
|
|
static const struct flash_pages_layout dev_layout = {
|
|
.pages_count = LAYOUT_PAGES_COUNT,
|
|
.pages_size = CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE,
|
|
};
|
|
#undef LAYOUT_PAGES_COUNT
|
|
|
|
static void qspi_nor_pages_layout(struct device *dev,
|
|
const struct flash_pages_layout **layout,
|
|
size_t *layout_size)
|
|
{
|
|
*layout = &dev_layout;
|
|
*layout_size = 1;
|
|
}
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
static const struct flash_driver_api qspi_nor_api = {
|
|
.read = qspi_nor_read,
|
|
.write = qspi_nor_write,
|
|
.erase = qspi_nor_erase,
|
|
.write_protection = qspi_nor_write_protection_set,
|
|
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
|
|
.page_layout = qspi_nor_pages_layout,
|
|
#endif
|
|
.write_block_size = 1,
|
|
};
|
|
|
|
|
|
static const struct qspi_nor_config flash_id = {
|
|
.id = DT_INST_0_NORDIC_QSPI_NOR_JEDEC_ID,
|
|
.size = INST_0_BYTES,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(qspi_flash_memory, DT_INST_0_NORDIC_QSPI_NOR_LABEL,
|
|
&qspi_nor_init, &qspi_nor_memory_data,
|
|
&flash_id, POST_KERNEL, CONFIG_NORDIC_QSPI_NOR_INIT_PRIORITY,
|
|
&qspi_nor_api);
|