diff --git a/drivers/memc/CMakeLists.txt b/drivers/memc/CMakeLists.txt index 2e020548610..1648343c18f 100644 --- a/drivers/memc/CMakeLists.txt +++ b/drivers/memc/CMakeLists.txt @@ -23,3 +23,4 @@ if((DEFINED CONFIG_FLASH_MCUX_FLEXSPI_XIP) AND (DEFINED CONFIG_FLASH)) endif() zephyr_library_sources_ifdef(CONFIG_MEMC_NXP_S32_QSPI memc_nxp_s32_qspi.c) +zephyr_library_sources_ifdef(CONFIG_MEMC_SMARTBOND memc_smartbond_nor_psram.c) diff --git a/drivers/memc/Kconfig b/drivers/memc/Kconfig index f6416557355..afab61994af 100644 --- a/drivers/memc/Kconfig +++ b/drivers/memc/Kconfig @@ -31,4 +31,6 @@ source "drivers/memc/Kconfig.sifive" source "drivers/memc/Kconfig.nxp_s32" +source "drivers/memc/Kconfig.smartbond" + endif diff --git a/drivers/memc/Kconfig.smartbond b/drivers/memc/Kconfig.smartbond new file mode 100644 index 00000000000..32cb8c94f46 --- /dev/null +++ b/drivers/memc/Kconfig.smartbond @@ -0,0 +1,11 @@ +# Smartbond Cryptographic Accelerator configuration options + +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config MEMC_SMARTBOND + bool "Smartbond NOR/PSRAM memory controller" + depends on DT_HAS_RENESAS_SMARTBOND_NOR_PSRAM_ENABLED + default y + help + Enable Smartbond NOR/PSRAM memory controller. diff --git a/drivers/memc/memc_smartbond_nor_psram.c b/drivers/memc/memc_smartbond_nor_psram.c new file mode 100644 index 00000000000..37584f99dcc --- /dev/null +++ b/drivers/memc/memc_smartbond_nor_psram.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2023 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_smartbond_nor_psram + +#include +#include +#include +#include +#include +#include +#include + +#include +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) diff --git a/dts/bindings/memory-controllers/renesas,smartbond-nor-psram.yaml b/dts/bindings/memory-controllers/renesas,smartbond-nor-psram.yaml new file mode 100644 index 00000000000..f55e064683b --- /dev/null +++ b/dts/bindings/memory-controllers/renesas,smartbond-nor-psram.yaml @@ -0,0 +1,260 @@ +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas Smartbond(tm) NOR/PSRAM controller + +include: base.yaml + +compatible: "renesas,smartbond-nor-psram" + +properties: + reg: + required: true + + is-ram: + type: boolean + description: | + If present, the memory controller will be configured to drive PSRAM devices. + + dev-size: + type: int + required: true + description: | + Memory size/capacity in bits. + + dev-type: + type: int + required: true + description: | + Device type, part of device ID, used to verify the memory device used. + + dev-density: + type: int + required: true + description: | + Device density, part of device ID, used to verify the memory device used. + [7:0] should reflect the density value itself and [15:8] should reflect + the mask that should be applied to the returned device ID value. + This is because part of its byte value might contain invalid bits. + + dev-id: + type: int + required: true + description: | + Manufacturer ID, part of device ID, used to verify the memory device used. + + reset-delay-us: + type: int + required: true + description: | + Time in microseconds (us) the memory device can accept the next command following a SW reset. + + read-cs-idle-min-ns: + type: int + required: true + description: | + Min. time, in nanoseconds, the #CS line should remain inactive between + the transmission of two different instructions. + + erase-cs-idle-min-ns: + type: int + description: | + Min. time, in nanoseconds, the #CS line should remain inactive after the execution + of a write enable, erase, erase suspend or erase resume instruction. This setting + is not used if is-ram property is present. + + enter-qpi-cmd: + type: int + description: | + Command to enter the QPI mode supported by a memory device + (should be transmitted in single bus mode). + + exit-qpi-cmd: + type: int + description: | + Command to exit the QPI mode supported by a memory device + (should be transmitted in quad bus mode). + + enter-qpi-mode: + type: boolean + description: | + If present, the memory device will enter the QPI mode which typically reflects that + all bytes be sent in quad bus mode. It's a pre-requisite that read and write + commands, that should be read-cmd and write-cmd respectively, reflect the QPI mode. + + read-cmd: + type: int + default: 0x03 + description: | + Read command for single/burst read accesses in auto mode. Default value is the opcode + for single mode which is supported by all memory devices. + + write-cmd: + type: int + default: 0x02 + description: | + Write command for single/burst write accesses in auto mode. Default value is the opcode + for single mode which is supported by all memory devices. + + clock-mode: + type: string + enum: + - "spi-mode0" + - "spi-mode3" + default: "spi-mode0" + description: | + Clock mode when #CS is idle/inactive + + - Mode0: #CLK is low when #CS is inactive + - Mode3: #CLK is high when #CS is inactive + + Mode0 is selected by default as it should be supported by all memory devices. + + addr-range: + type: string + enum: + - "addr-range-24bit" + - "addr-range-32bit" + default: "addr-range-24bit" + description: | + Address size to use in auto mode. In 24-bit mode up to 16MB can be + accessed whilst in 32-bit mode up to 32MB can be accessed which is + the max. address space supported by QSPICx. Default value is 24-bit + mode which is supported by all memory devices. + + clock-div: + type: int + description: | + Clock divider for QSPIC2 controller. The clock path of + this block is always DIV1 which reflects the current + system clock. + + tcem-max-us: + type: int + description: | + If a non zero value is applied, then Tcem should be taken into + consideration by QSPIC2 so that it can split a burst read/write + access in case the total time exceeds the defined value + (at the cost of extra cycles required for re-sending the instruction, + address and dummy bytes, if any). This setting is meaningful only if + is-ram is present. This value reflects the max. time in microseconds + the #CS line can be driven low in a write/read burst access + (required for the auto-refresh mechanism, when supported). + + dummy-bytes-count: + type: string + required: true + enum: + - "dummy-bytes-count0" + - "dummy-bytes-count1" + - "dummy-bytes-count2" + - "dummy-bytes-count4" + description: | + Number of dummy bytes to send for single/burst read access in auto mode. + + extra-byte-enable: + type: boolean + description: | + If present, the extra byte will be sent after the dummy bytes, if any. + This should be useful if 3 dummy bytes are required. In such a case, + dummy-bytes-count should be set to 2. + + extra-byte: + type: int + description: | + Extra byte to be sent, if extra-byte-enable is present. + + rx-addr-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the address phase for single/burst + read accesses in auto mode. Default value is single mode which should be + supported by all memory devices. + + rx-inst-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the instruction phase for single/burst + read accesses in auto mode. Default value is single mode which should be + supported by all memory devices. + + rx-data-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the data phase for single/burst + read accesses in auto mode. Default value is single mode which should + be supported by all memory devices. + + rx-dummy-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the dummy bytes phase for single/burst + read accesses in auto mode. The single mode should be supported by all + memory devices. + + rx-extra-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + description: | + Describes the mode of SPI bus during the extra byte phase for single/burst + read accesses in auto mode. Default value is single mode which should be + supported by all memory devices. + + tx-addr-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the address phase for single/burst + write accesses in auto mode. Default value is single mode which should + be supported by all memory devices. + + tx-inst-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the instruction phase for single/burst + write accesses in auto mode. The single mode should be supported by all + memory devices. + + tx-data-mode: + type: string + enum: + - "single-spi" + - "dual-spi" + - "quad-spi" + default: "single-spi" + description: | + Describes the mode of SPI bus during the data phase for single/burst + write accesses in auto mode. Default value is single mode which should + be supported by all memory devices.