Browse Source
Silabs siwx91x includes a memory controller for (Quad-)SPI PSRAM. It allows the application to use the PSRAM as if it was any other RAM. Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>pull/87786/head
9 changed files with 197 additions and 2 deletions
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
# Copyright (c) 2025 Silicon Laboratories Inc. |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
config MEMC_SILABS_SIWX91X_QSPI |
||||
bool "Silabs SiWx91x QSPI memory controller" |
||||
default y |
||||
depends on DT_HAS_SILABS_SIWX91X_QSPI_MEMORY_ENABLED |
||||
select PINCTRL |
||||
help |
||||
Enable Silabs SiWx91x QSPI (Quad Serial Peripheral Interface) memory |
||||
controller. |
||||
|
||||
If you want to rely on the linker to place symbols in this memory |
||||
(using`zephyr_code_relocate() or Z_GENERIC_SECTION()), you have to |
||||
ensure this driver in initialized before KERNEL_INIT_PRIORITY_OBJECTS |
||||
(=30). In addition, this driver may depends on a clock. Then, you have |
||||
to ensure the clock will be started before this driver (see |
||||
CLOCK_CONTROL_INIT_PRIORITY) |
@ -0,0 +1,122 @@
@@ -0,0 +1,122 @@
|
||||
/* Copyright (c) 2025 Silicon Laboratories Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
#define DT_DRV_COMPAT silabs_siwx91x_qspi_memory |
||||
#include <zephyr/logging/log.h> |
||||
#include <zephyr/drivers/clock_control.h> |
||||
#include <zephyr/drivers/pinctrl.h> |
||||
|
||||
#include <zephyr/sys/util.h> |
||||
#include <zephyr/device.h> |
||||
#include <zephyr/kernel.h> |
||||
|
||||
#include "rsi_qspi_proto.h" |
||||
#include "sl_si91x_psram_handle.h" |
||||
|
||||
LOG_MODULE_REGISTER(siwx91x_memc, CONFIG_MEMC_LOG_LEVEL); |
||||
|
||||
struct siwx91x_memc_config { |
||||
qspi_reg_t *reg; |
||||
const struct device *clock_dev; |
||||
clock_control_subsys_t clock_subsys; |
||||
const struct pinctrl_dev_config *pincfg; |
||||
}; |
||||
|
||||
static int siwx91x_memc_init(const struct device *dev) |
||||
{ |
||||
const struct siwx91x_memc_config *config = dev->config; |
||||
int ret; |
||||
|
||||
/* Memory controller is automatically setup by the siwx91x bootloader,
|
||||
* so we have to uninitialize it before to change the configuration |
||||
*/ |
||||
ret = sl_si91x_psram_uninit(); |
||||
if (ret) { |
||||
return -EIO; |
||||
} |
||||
|
||||
ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); |
||||
if (ret) { |
||||
return -EIO; |
||||
} |
||||
if (config->clock_dev) { |
||||
ret = device_is_ready(config->clock_dev); |
||||
if (!ret) { |
||||
return -EINVAL; |
||||
} |
||||
ret = clock_control_on(config->clock_dev, config->clock_subsys); |
||||
if (ret && ret != -EALREADY && ret != -ENOSYS) { |
||||
return ret; |
||||
} |
||||
} |
||||
|
||||
ret = sl_si91x_psram_init(); |
||||
if (ret) { |
||||
LOG_ERR("sl_si91x_psram_init() returned %d", ret); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
PINCTRL_DT_INST_DEFINE(0); |
||||
static const struct siwx91x_memc_config siwx91x_memc_config = { |
||||
.reg = (void *)DT_INST_REG_ADDR(0), |
||||
.clock_dev = DEVICE_DT_GET_OR_NULL(DT_INST_CLOCKS_CTLR(0)), |
||||
.clock_subsys = (void *)DT_INST_PHA_OR(0, clocks, clkid, NULL), |
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
||||
}; |
||||
/* Required to properly initialize ,deviceID */ |
||||
static const uint8_t devid[] = DT_INST_PROP(0, device_id); |
||||
|
||||
/* PSRAM_Device is directly referenced by sl_si91x_psram_init() */ |
||||
struct sl_psram_info_type_t PSRAM_Device = { |
||||
.deviceID.MFID = devid[0], |
||||
.deviceID.KGD = devid[1], |
||||
.deviceID.EID = { devid[2], devid[3], devid[4], devid[5], devid[6], devid[7] }, |
||||
/* FIXME: Currently, the Chip Select (.cs_no property) and the RAM start
|
||||
* address are hard coded. The hardware also support Chip Select == 1, |
||||
* then RAM start address will be 0xb000000. |
||||
*/ |
||||
.devDensity = DT_REG_SIZE(DT_INST_CHILD(0, psram_a000000)), |
||||
.normalReadMAXFrequency = DT_INST_PROP(0, normal_freq), |
||||
.fastReadMAXFrequency = DT_INST_PROP(0, fast_freq), |
||||
.rwType = QUAD_RW, |
||||
.defaultBurstWrapSize = 1024, |
||||
.toggleBurstWrapSize = 0, |
||||
.spi_config.spi_config_2.auto_mode = 1, |
||||
/* FIXME: user may want to customize these values */ |
||||
.spi_config.spi_config_1.read_cmd = 0xEB, |
||||
.spi_config.spi_config_3.wr_cmd = 0x38, |
||||
.spi_config.spi_config_1.extra_byte_mode = QUAD_MODE, |
||||
.spi_config.spi_config_1.dummy_mode = QUAD_MODE, |
||||
.spi_config.spi_config_1.addr_mode = QUAD_MODE, |
||||
.spi_config.spi_config_1.data_mode = QUAD_MODE, |
||||
.spi_config.spi_config_1.inst_mode = QUAD_MODE, |
||||
.spi_config.spi_config_3.wr_addr_mode = QUAD_MODE, |
||||
.spi_config.spi_config_3.wr_data_mode = QUAD_MODE, |
||||
.spi_config.spi_config_3.wr_inst_mode = QUAD_MODE, |
||||
.spi_config.spi_config_2.wrap_len_in_bytes = NO_WRAP, |
||||
.spi_config.spi_config_2.swap_en = 1, |
||||
.spi_config.spi_config_2.addr_width = 3, /* 24 bits */ |
||||
.spi_config.spi_config_2.cs_no = 0, |
||||
.spi_config.spi_config_4.secondary_csn = 1, |
||||
.spi_config.spi_config_2.neg_edge_sampling = 1, |
||||
.spi_config.spi_config_1.no_of_dummy_bytes = 3, |
||||
.spi_config.spi_config_1.dummy_W_or_R = DUMMY_READS, |
||||
.spi_config.spi_config_2.full_duplex = IGNORE_FULL_DUPLEX, |
||||
.spi_config.spi_config_2.qspi_clk_en = QSPI_FULL_TIME_CLK, |
||||
.spi_config.spi_config_1.flash_type = 0xf, |
||||
.spi_config.spi_config_3.dummys_4_jump = 1, |
||||
.spi_config.spi_config_4.valid_prot_bits = 4, |
||||
.spi_config.spi_config_1.d3d2_data = 0x03, |
||||
.spi_config.spi_config_5.d7_d4_data = 0x0f, |
||||
}; |
||||
/* PSRAMSecureSegments is directly referenced by sl_si91x_psram_init() */ |
||||
struct PSRAMSecureSegmentType PSRAMSecureSegments[MAX_SEC_SEGMENTS] = { |
||||
[0].segmentEnable = 1, |
||||
[0].lowerBoundary = 0x00000, |
||||
[0].higherBoundary = 0x0ffff, |
||||
}; |
||||
DEVICE_DT_INST_DEFINE(0, siwx91x_memc_init, NULL, NULL, &siwx91x_memc_config, |
||||
PRE_KERNEL_1, CONFIG_MEMC_INIT_PRIORITY, NULL); |
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
description: | |
||||
Silicon Labs QSPI (Quad Serial Protocol Interface) memory controller. |
||||
|
||||
This driver expects a children node compatible with "zephyr,memory-region". |
||||
The size of the PSRAM is taken from this node. It also used by Zephyr to |
||||
declare the area in the linker script. |
||||
|
||||
Note after the chip reset, the bootloader may automatically configure the |
||||
Memory Controller. So, the user may be able to properly use the PSRAM without |
||||
this driver (in this case, only the "zephyr,memory-region" node is required). |
||||
|
||||
This driver allows for non default configurations. In this case, if the users |
||||
want to take advantage of the automatic data relocation of Zephyr, they have |
||||
to tune the MEMC_INIT_PRIORITY value to initialize this driver before the |
||||
initialization of the kernel resources. Then, they will probably want to also |
||||
initialize the clock driver before this driver. |
||||
|
||||
Otherwise, it is also possible to start this driver later in the boot |
||||
sequence, but the user will have to manage the memory region manually. |
||||
|
||||
include: [base.yaml, pinctrl-device.yaml] |
||||
|
||||
compatible: "silabs,siwx91x-qspi-memory" |
||||
|
||||
properties: |
||||
reg: |
||||
required: true |
||||
|
||||
device-id: |
||||
type: uint8-array |
||||
required: true |
||||
description: | |
||||
Value returned by the chip on READ_ID command. An array of 8 bytes is |
||||
expected. However only the two first bytes (should be "Manufacturer ID" |
||||
and "Known Good Die") are used. The driver won't continue if the value |
||||
returned by the chip does not match this field. |
||||
|
||||
normal-freq: |
||||
type: int |
||||
required: true |
||||
|
||||
fast-freq: |
||||
type: int |
||||
required: true |
Loading…
Reference in new issue