zephyr/drivers/memc/memc_smartbond_nor_psram.c
Ioannis Karachalios 02e739873e drivers: memc: smartbond: Add support for the memory driver class.
Add support for the memory controller by utilizing QSPIC2. The latter is
capable to drive both NOR and PSRAM memory devices. For this to work,
the RAM driving mode is enabled.

Signed-off-by: Ioannis Karachalios <ioannis.karachalios.px@renesas.com>
2024-05-23 07:51:41 -04:00

255 lines
8.2 KiB
C

/*
* Copyright (c) 2023 Renesas Electronics Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_smartbond_nor_psram
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/irq.h>
#include <DA1469xAB.h>
#include <zephyr/pm/device.h>
#include <da1469x_qspic.h>
#include <da1469x_pd.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(smartbond_nor_psram, CONFIG_MEMC_LOG_LEVEL);
#define CLK_AMBA_REG_SET_FIELD(_field, _var, _val) \
((_var)) = \
((_var) & ~(CRG_TOP_CLK_AMBA_REG_ ## _field ## _Msk)) | \
(((_val) << CRG_TOP_CLK_AMBA_REG_ ## _field ## _Pos) & \
CRG_TOP_CLK_AMBA_REG_ ## _field ## _Msk)
#define QSPIC2_CTRLMODE_REG_SET_FIELD(_field, _var, _val) \
((_var)) = \
((_var) & ~(QSPIC2_QSPIC2_CTRLMODE_REG_ ## _field ## _Msk)) | \
(((_val) << QSPIC2_QSPIC2_CTRLMODE_REG_ ## _field ## _Pos) & \
QSPIC2_QSPIC2_CTRLMODE_REG_ ## _field ## _Msk)
#define QSPIC2_BURSTCMDA_REG_SET_FIELD(_field, _var, _val) \
((_var)) = \
((_var) & ~(QSPIC2_QSPIC2_BURSTCMDA_REG_ ## _field ## _Msk)) | \
(((_val) << QSPIC2_QSPIC2_BURSTCMDA_REG_ ## _field ## _Pos) & \
QSPIC2_QSPIC2_BURSTCMDA_REG_ ## _field ## _Msk)
#define QSPIC2_BURSTCMDB_REG_SET_FIELD(_field, _var, _val) \
((_var)) = \
((_var) & ~(QSPIC2_QSPIC2_BURSTCMDB_REG_ ## _field ## _Msk)) | \
(((_val) << QSPIC2_QSPIC2_BURSTCMDB_REG_ ## _field ## _Pos) & \
QSPIC2_QSPIC2_BURSTCMDB_REG_ ## _field ## _Msk)
#define QSPIC2_AWRITECMD_REG_SET_FIELD(_field, _var, _val) \
((_var)) = \
((_var) & ~(QSPIC2_QSPIC2_AWRITECMD_REG_ ## _field ## _Msk)) | \
(((_val) << QSPIC2_QSPIC2_AWRITECMD_REG_ ## _field ## _Pos) & \
QSPIC2_QSPIC2_AWRITECMD_REG_ ## _field ## _Msk)
static void memc_set_status(bool status, int clk_div)
{
unsigned int key;
uint32_t clk_amba_reg;
/* Clock AMBA register might be accessed by multiple driver classes */
key = irq_lock();
clk_amba_reg = CRG_TOP->CLK_AMBA_REG;
if (status) {
CLK_AMBA_REG_SET_FIELD(QSPI2_ENABLE, clk_amba_reg, 1);
CLK_AMBA_REG_SET_FIELD(QSPI2_DIV, clk_amba_reg, clk_div);
} else {
CLK_AMBA_REG_SET_FIELD(QSPI2_ENABLE, clk_amba_reg, 0);
}
CRG_TOP->CLK_AMBA_REG = clk_amba_reg;
irq_unlock(key);
}
static void memc_automode_configure(void)
{
uint32_t reg;
reg = QSPIC2->QSPIC2_CTRLMODE_REG;
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_SRAM_EN, reg,
DT_INST_PROP(0, is_ram));
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_USE_32BA, reg,
DT_INST_ENUM_IDX(0, addr_range));
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_CLK_MD, reg,
DT_INST_ENUM_IDX(0, clock_mode));
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_AUTO_MD, reg, 1);
QSPIC2->QSPIC2_CTRLMODE_REG = reg;
reg = QSPIC2->QSPIC2_BURSTCMDA_REG;
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_DMY_TX_MD, reg,
DT_INST_ENUM_IDX(0, rx_dummy_mode));
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_ADR_TX_MD, reg,
DT_INST_ENUM_IDX(0, rx_addr_mode));
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_INST_TX_MD, reg,
DT_INST_ENUM_IDX(0, rx_inst_mode));
#if DT_INST_PROP(0, extra_byte_enable)
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_EXT_TX_MD, reg,
DT_INST_ENUM_IDX(0, rx_extra_mode));
#endif
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_INST, reg,
DT_INST_PROP(0, read_cmd));
#if DT_INST_PROP(0, extra_byte_enable)
QSPIC2_BURSTCMDA_REG_SET_FIELD(QSPIC_EXT_BYTE, reg,
DT_INST_PROP(0, extra_byte));
#endif
QSPIC2->QSPIC2_BURSTCMDA_REG = reg;
reg = QSPIC2->QSPIC2_BURSTCMDB_REG;
QSPIC2_BURSTCMDB_REG_SET_FIELD(QSPIC_DMY_NUM, reg,
DT_INST_ENUM_IDX(0, dummy_bytes_count));
QSPIC2_BURSTCMDB_REG_SET_FIELD(QSPIC_DAT_RX_MD, reg,
DT_INST_ENUM_IDX(0, rx_data_mode));
QSPIC2_BURSTCMDB_REG_SET_FIELD(QSPIC_INST_MD, reg, 0);
QSPIC2_BURSTCMDB_REG_SET_FIELD(QSPIC_EXT_BYTE_EN, reg,
DT_INST_PROP(0, extra_byte_enable));
QSPIC2->QSPIC2_BURSTCMDB_REG = reg;
reg = QSPIC2->QSPIC2_AWRITECMD_REG;
QSPIC2_AWRITECMD_REG_SET_FIELD(QSPIC_WR_DAT_TX_MD, reg,
DT_INST_ENUM_IDX(0, tx_data_mode));
QSPIC2_AWRITECMD_REG_SET_FIELD(QSPIC_WR_ADR_TX_MD, reg,
DT_INST_ENUM_IDX(0, tx_addr_mode));
QSPIC2_AWRITECMD_REG_SET_FIELD(QSPIC_WR_INST_TX_MD, reg,
DT_INST_ENUM_IDX(0, tx_inst_mode));
QSPIC2_AWRITECMD_REG_SET_FIELD(QSPIC_WR_INST, reg,
DT_INST_PROP(0, write_cmd));
QSPIC2->QSPIC2_AWRITECMD_REG = reg;
}
/* Read PSRAM/NOR device ID using JEDEC commands. */
static bool memc_jedec_read_and_verify_id(QSPIC_TYPE qspi_id)
{
uint16_t device_density;
bool ret = 0;
qspi_memory_id_t memory_id;
da1469x_qspi_memory_jedec_read_id(qspi_id, &memory_id);
device_density = DT_INST_PROP(0, dev_density);
ret |= !(memory_id.id == DT_INST_PROP(0, dev_id));
ret |= !(memory_id.type == DT_INST_PROP(0, dev_type));
ret |= !((memory_id.density & (device_density >> 8)) == (device_density & 0xFF));
return ret;
}
static int memc_smartbond_init(const struct device *dev)
{
uint32_t qspic_ctrlmode_reg;
/* First QSPI controller is enabled so registers can be accessed */
memc_set_status(true, DT_INST_PROP_OR(0, clock_div, 0));
/* Apply the min. required settings before performing any transaction in manual mode. */
qspic_ctrlmode_reg = QSPIC2->QSPIC2_CTRLMODE_REG;
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_CLK_MD, qspic_ctrlmode_reg,
DT_INST_ENUM_IDX(0, clock_mode));
QSPIC2_CTRLMODE_REG_SET_FIELD(QSPIC_AUTO_MD, qspic_ctrlmode_reg, 0);
QSPIC2->QSPIC2_CTRLMODE_REG = qspic_ctrlmode_reg;
/* Reset PSRAM/NOR device using JDEC commands */
da1469x_qspi_memory_jedec_reset(QSPIC2_ID);
/* Wait till reset is completed */
k_usleep(DT_INST_PROP(0, reset_delay_us));
if (memc_jedec_read_and_verify_id(QSPIC2_ID)) {
LOG_ERR("Device detection failed");
memc_set_status(false, 0);
return -EINVAL;
}
#if DT_INST_PROP(0, enter_qpi_mode)
da1469x_qspi_enter_exit_qpi_mode(QSPIC2_ID, true, DT_INST_PROP(0, enter_qpi_cmd));
#endif
/* Should be called prior to switching to auto mode and when the quad bus is selected! */
da1469x_qspi_set_bus_mode(QSPIC2_ID, QSPI_BUS_MODE_QUAD);
#if CONFIG_PM_DEVICE_RUNTIME
/*
* Turn off the controller to minimize power consumption. Application is responsible to
* configure/de-configure the controller before interacting with the memory.
*/
memc_set_status(false, 0);
/* Make sure device is marked as suspended */
pm_device_init_suspended(dev);
return pm_device_runtime_enable(dev);
#else
da1469x_pd_acquire(MCU_PD_DOMAIN_SYS);
/* From this point onwards memory device should be seen as memory mapped device. */
memc_automode_configure();
#endif
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int memc_smartbond_pm_action(const struct device *dev, enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
/*
* CLK_AMBA_REG, that controlls QSPIC2, is retained during sleep
* (resides in PD_AON). However, unused blocks should be disabled
* to minimize power consumption at sleep.
*/
memc_set_status(false, 0);
da1469x_pd_release(MCU_PD_DOMAIN_SYS);
break;
case PM_DEVICE_ACTION_RESUME:
/*
* Mainly, required when in PM runtime mode. When in PM static mode,
* the device will block till an ongoing/pending AMBA bus transfer
* completes.
*/
da1469x_pd_acquire(MCU_PD_DOMAIN_SYS);
/*
* QSPIC2 is powered by PD_SYS which is turned off during sleep and
* so QSPIC2 auto mode re-initialization is required.
*
* XXX: It's assumed that memory device's power rail, that should
* be 1V8P, is not turned off and so the device itsef does not
* require re-initialization. Revisit this part if power settings
* are changed in the future, that should include:
*
* 1. Powering off the memory device by turning off 1V8P
* (valid for FLASH/PSRAM).
* 2. Powering down the memory device so it enters the suspend/low-power
* state during sleep (valid for FLASH/NOR devices).
*/
memc_set_status(true, DT_INST_PROP_OR(0, clock_div, 0));
memc_automode_configure();
default:
return -ENOTSUP;
}
return 0;
}
#endif
#define SMARTBOND_MEMC_INIT(inst) \
BUILD_ASSERT(inst == 0, "multiple instances are not permitted"); \
BUILD_ASSERT(DT_INST_PROP(inst, is_ram), \
"current driver version suports only PSRAM devices"); \
\
PM_DEVICE_DT_INST_DEFINE(inst, memc_smartbond_pm_action); \
\
DEVICE_DT_INST_DEFINE(inst, memc_smartbond_init, PM_DEVICE_DT_INST_GET(inst), \
NULL, NULL, \
POST_KERNEL, CONFIG_MEMC_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(SMARTBOND_MEMC_INIT)