This adds flash driver for Renesas SmartBond(tm) family. This technically uses QSPI controller but since default and most commonly used configuration is to boot from external QSPI flash (DA1469x do not have built-in flash) and that flash is mapped into memory space, it can be represented as internal flash. Signed-off-by: Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl> Signed-off-by: Ben Lauret <ben.lauret.wm@renesas.com>
296 lines
6.4 KiB
C
296 lines
6.4 KiB
C
/*
|
|
* Copyright (c) 2022 Renesas Electronics Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_smartbond_flash_controller
|
|
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
|
|
#define QSPIF_NODE DT_NODELABEL(qspif)
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/flash.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <DA1469xAB.h>
|
|
|
|
#define FLASH_ERASE_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
|
|
#define FLASH_PAGE_SIZE 256
|
|
|
|
struct flash_smartbond_config {
|
|
uint32_t qspif_base_address;
|
|
};
|
|
|
|
static const struct flash_parameters flash_smartbond_parameters = {
|
|
.write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size),
|
|
.erase_value = 0xff,
|
|
};
|
|
|
|
static bool range_is_valid(off_t offset, uint32_t size)
|
|
{
|
|
return (offset + size) <= (CONFIG_FLASH_SIZE * 1024);
|
|
}
|
|
|
|
static ALWAYS_INLINE void qspic_data_write8(uint8_t data)
|
|
{
|
|
volatile uint8_t *reg8 = (uint8_t *)&QSPIC->QSPIC_WRITEDATA_REG;
|
|
|
|
*reg8 = data;
|
|
}
|
|
|
|
static ALWAYS_INLINE void qspic_data_write32(uint32_t data)
|
|
{
|
|
volatile uint32_t *reg32 = (uint32_t *)&QSPIC->QSPIC_WRITEDATA_REG;
|
|
|
|
*reg32 = data;
|
|
}
|
|
|
|
static ALWAYS_INLINE uint8_t qspic_data_read8(void)
|
|
{
|
|
volatile uint8_t *reg8 = (uint8_t *)&QSPIC->QSPIC_READDATA_REG;
|
|
|
|
return *reg8;
|
|
}
|
|
|
|
static __ramfunc uint8_t qspic_read_status(void)
|
|
{
|
|
uint8_t status;
|
|
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk;
|
|
qspic_data_write8(0x05);
|
|
status = qspic_data_read8();
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
static __ramfunc void qspic_wait_busy(void)
|
|
{
|
|
do {
|
|
} while (qspic_read_status() & 0x01);
|
|
}
|
|
|
|
static __ramfunc void qspic_automode_exit(void)
|
|
{
|
|
QSPIC->QSPIC_CTRLMODE_REG &= ~QSPIC_QSPIC_CTRLMODE_REG_QSPIC_AUTO_MD_Msk;
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_SET_SINGLE_Msk;
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk;
|
|
qspic_data_write8(0xff);
|
|
qspic_data_write8(0xff);
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk;
|
|
}
|
|
|
|
static __ramfunc void qspic_write_enable(void)
|
|
{
|
|
uint8_t status;
|
|
|
|
do {
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk;
|
|
qspic_data_write8(0x06);
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk;
|
|
|
|
do {
|
|
status = qspic_read_status();
|
|
} while (status & 0x01);
|
|
} while (!(status & 0x02));
|
|
}
|
|
|
|
static __ramfunc size_t qspic_write_page(uint32_t address, const uint8_t *data, size_t size)
|
|
{
|
|
size_t written;
|
|
|
|
/* Make sure we write up to page boundary */
|
|
size = MIN(size, FLASH_PAGE_SIZE - (address & (FLASH_PAGE_SIZE - 1)));
|
|
written = size;
|
|
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk;
|
|
|
|
address = sys_cpu_to_be32(address);
|
|
qspic_data_write32(address | 0x02);
|
|
|
|
while (size >= 4) {
|
|
qspic_data_write32(*(uint32_t *) data);
|
|
data += 4;
|
|
size -= 4;
|
|
}
|
|
|
|
while (size) {
|
|
qspic_data_write8(*data);
|
|
data++;
|
|
size--;
|
|
}
|
|
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk;
|
|
|
|
return written;
|
|
}
|
|
|
|
static __ramfunc void qspic_write(uint32_t address, const uint8_t *data, size_t size)
|
|
{
|
|
size_t written;
|
|
|
|
while (size) {
|
|
qspic_write_enable();
|
|
|
|
written = qspic_write_page(address, data, size);
|
|
address += written;
|
|
data += written;
|
|
size -= written;
|
|
|
|
qspic_wait_busy();
|
|
}
|
|
}
|
|
|
|
static int flash_smartbond_read(const struct device *dev, off_t offset,
|
|
void *data, size_t size)
|
|
{
|
|
const struct flash_smartbond_config *config = dev->config;
|
|
|
|
if (!range_is_valid(offset, size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!size) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(data, (uint8_t *)(config->qspif_base_address + offset), size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __ramfunc int flash_smartbond_write(const struct device *dev,
|
|
off_t offset, const void *data,
|
|
size_t size)
|
|
{
|
|
unsigned int key;
|
|
uint32_t ctrlmode;
|
|
|
|
if (!range_is_valid(offset, size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!size) {
|
|
return 0;
|
|
}
|
|
|
|
key = irq_lock();
|
|
|
|
ctrlmode = QSPIC->QSPIC_CTRLMODE_REG;
|
|
qspic_automode_exit();
|
|
qspic_wait_busy();
|
|
|
|
qspic_write(offset, data, size);
|
|
|
|
QSPIC->QSPIC_CTRLMODE_REG = ctrlmode;
|
|
CACHE->CACHE_CTRL1_REG |= CACHE_CACHE_CTRL1_REG_CACHE_FLUSH_Msk;
|
|
|
|
irq_unlock(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __ramfunc int flash_smartbond_erase(const struct device *dev, off_t offset,
|
|
size_t size)
|
|
{
|
|
unsigned int key;
|
|
uint32_t ctrlmode;
|
|
uint32_t address;
|
|
|
|
if (!range_is_valid(offset, size)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((offset % FLASH_ERASE_SIZE) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((size % FLASH_ERASE_SIZE) != 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!size) {
|
|
return 0;
|
|
}
|
|
|
|
key = irq_lock();
|
|
|
|
ctrlmode = QSPIC->QSPIC_CTRLMODE_REG;
|
|
qspic_automode_exit();
|
|
qspic_wait_busy();
|
|
|
|
while (size) {
|
|
qspic_write_enable();
|
|
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_EN_CS_Msk;
|
|
|
|
address = sys_cpu_to_be32(offset);
|
|
qspic_data_write32(address | 0x20);
|
|
QSPIC->QSPIC_CTRLBUS_REG = QSPIC_QSPIC_CTRLBUS_REG_QSPIC_DIS_CS_Msk;
|
|
|
|
qspic_wait_busy();
|
|
|
|
offset += FLASH_ERASE_SIZE;
|
|
size -= FLASH_ERASE_SIZE;
|
|
}
|
|
|
|
QSPIC->QSPIC_CTRLMODE_REG = ctrlmode;
|
|
CACHE->CACHE_CTRL1_REG |= CACHE_CACHE_CTRL1_REG_CACHE_FLUSH_Msk;
|
|
|
|
irq_unlock(key);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct flash_parameters *
|
|
flash_smartbond_get_parameters(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return &flash_smartbond_parameters;
|
|
}
|
|
|
|
#if CONFIG_FLASH_PAGE_LAYOUT
|
|
static const struct flash_pages_layout flash_smartbond_0_pages_layout = {
|
|
.pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) /
|
|
DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
|
|
.pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
|
|
};
|
|
|
|
void flash_smartbond_page_layout(const struct device *dev,
|
|
const struct flash_pages_layout **layout,
|
|
size_t *layout_size)
|
|
{
|
|
*layout = &flash_smartbond_0_pages_layout;
|
|
*layout_size = 1;
|
|
}
|
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
|
|
|
|
static int flash_smartbond_init(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct flash_driver_api flash_smartbond_driver_api = {
|
|
.read = flash_smartbond_read,
|
|
.write = flash_smartbond_write,
|
|
.erase = flash_smartbond_erase,
|
|
.get_parameters = flash_smartbond_get_parameters,
|
|
#ifdef CONFIG_FLASH_PAGE_LAYOUT
|
|
.page_layout = flash_smartbond_page_layout,
|
|
#endif
|
|
};
|
|
|
|
static const struct flash_smartbond_config flash_smartbond_0_config = {
|
|
.qspif_base_address = DT_REG_ADDR(QSPIF_NODE),
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0, flash_smartbond_init, NULL, NULL, &flash_smartbond_0_config,
|
|
POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_smartbond_driver_api);
|