289 lines
6.3 KiB
C
289 lines
6.3 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 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, NULL, NULL, NULL, &flash_smartbond_0_config,
|
|
POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_smartbond_driver_api);
|