blob: c861f33f6444d5091d4f62c4b10193a163b7cc7c [file]
/*
* SPDX-FileCopyrightText: 2026 Aesc Silicon
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT aesc_spi_flash_controller
#include <errno.h>
#include <string.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_aesc, CONFIG_FLASH_LOG_LEVEL);
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
#define FLASH_BASE DT_REG_ADDR(SOC_NV_FLASH_NODE)
#define FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE)
/*
* XIP read-mode configuration lives on the controller's XIP config bus, which
* sits 0x1000 into the controller register block:
* +0x08 CONFIGURE write strobe that latches the MODE register and, for
* quad, writes EVCR into the flash device
* +0x0c MODE [3:0] read mode, [15:8] dummy cycles, [23:16] flash EVCR
*
* Quad needs all three fields, not just the mode: the mode selects the quad
* read command, the dummy-cycle count must match the flash's quad fast-read
* latency, and EVCR is programmed into the flash to switch the device itself
* into quad. (mode=0x2 alone would read with zero dummy cycles and write
* EVCR=0x00 to the flash, returning garbage.)
*/
#define XIP_CFG_OFFSET 0x1000
#define XIP_REG_CONFIGURE (XIP_CFG_OFFSET + 0x08)
#define XIP_REG_MODE (XIP_CFG_OFFSET + 0x0c)
#define XIP_MODE_MASK 0xf /* mode field is modeWidth = 4 bits */
#define XIP_MODE_SINGLE 0x0
#define XIP_MODE_DUAL 0x1
#define XIP_MODE_QUAD 0x2
#define XIP_DUMMY_QUAD 7
#define XIP_EVCR_QUAD 0x7f
#define XIP_CONFIG_QUAD (XIP_MODE_QUAD | (XIP_DUMMY_QUAD << 8) | \
(XIP_EVCR_QUAD << 16))
static const struct flash_parameters flash_aesc_parameters = {
.write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size),
.erase_value = 0xff,
};
struct flash_aesc_config {
DEVICE_MMIO_ROM;
bool quad_mode;
};
struct flash_aesc_data {
DEVICE_MMIO_RAM;
};
static int flash_aesc_read(const struct device *dev, off_t offset, void *data,
size_t len)
{
ARG_UNUSED(dev);
if (offset < 0 || offset >= FLASH_SIZE || (FLASH_SIZE - offset) < len) {
return -EINVAL;
}
if (len == 0U) {
return 0;
}
/* The flash is execute-in-place mapped; read straight from the window. */
memcpy(data, (uint8_t *)FLASH_BASE + offset, len);
return 0;
}
static int flash_aesc_write(const struct device *dev, off_t offset,
const void *data, size_t len)
{
ARG_UNUSED(dev);
ARG_UNUSED(offset);
ARG_UNUSED(data);
ARG_UNUSED(len);
/* Programming over the XIP controller is not supported yet. */
return -ENOSYS;
}
static int flash_aesc_erase(const struct device *dev, off_t offset, size_t size)
{
ARG_UNUSED(dev);
ARG_UNUSED(offset);
ARG_UNUSED(size);
return -ENOSYS;
}
static const struct flash_parameters *flash_aesc_get_parameters(
const struct device *dev)
{
ARG_UNUSED(dev);
return &flash_aesc_parameters;
}
static int flash_aesc_init(const struct device *dev)
{
const struct flash_aesc_config *cfg = dev->config;
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
if (cfg->quad_mode) {
uintptr_t base = DEVICE_MMIO_GET(dev);
uint32_t mode = sys_read32(base + XIP_REG_MODE) & XIP_MODE_MASK;
if (mode == XIP_MODE_SINGLE || mode == XIP_MODE_DUAL) {
/* Latch the quad read settings, then strobe configure. */
sys_write32(XIP_CONFIG_QUAD, base + XIP_REG_MODE);
sys_write32(0, base + XIP_REG_CONFIGURE);
}
}
return 0;
}
static DEVICE_API(flash, flash_aesc_driver_api) = {
.read = flash_aesc_read,
.write = flash_aesc_write,
.erase = flash_aesc_erase,
.get_parameters = flash_aesc_get_parameters,
};
static struct flash_aesc_data flash_aesc_data_0;
static const struct flash_aesc_config flash_aesc_config_0 = {
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)),
.quad_mode = DT_INST_PROP(0, aesc_quad_mode),
};
DEVICE_DT_INST_DEFINE(0, flash_aesc_init, NULL,
&flash_aesc_data_0, &flash_aesc_config_0, PRE_KERNEL_1,
CONFIG_FLASH_INIT_PRIORITY, &flash_aesc_driver_api);