mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-03 05:43:45 +00:00
This commit adds a new driver category for memory controller peripherals. There is no API involved for now, as it has not been found necessary for first implementation. STM32 Flexible Memory Controller (FMC) is the only controller supported for now. This peripheral allows to access multiple types of external memories, e.g. SDRAM, NAND, NOR Flash... The initial implementation adds support for the SDRAM controller only. The HAL API is used, so the implementation should be portable to other STM32 series. It has only been tested on H7 series, so for now it can only be enabled when working on H7. Linker facilities have also been added in order to allow applications to easily define a variable in SDRAM. Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
134 lines
4.8 KiB
C
134 lines
4.8 KiB
C
/*
|
|
* Copyright (c) 2020 Teslabs Engineering S.L.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT st_stm32_fmc_sdram
|
|
|
|
#include <device.h>
|
|
#include <soc.h>
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(memc_stm32_sdram, CONFIG_MEMC_LOG_LEVEL);
|
|
|
|
/** SDRAM controller register offset. */
|
|
#define SDRAM_OFFSET 0x140U
|
|
|
|
/** FMC SDRAM controller bank configuration fields. */
|
|
struct memc_stm32_sdram_bank_config {
|
|
FMC_SDRAM_InitTypeDef init;
|
|
FMC_SDRAM_TimingTypeDef timing;
|
|
};
|
|
|
|
/** FMC SDRAM controller configuration fields. */
|
|
struct memc_stm32_sdram_config {
|
|
FMC_SDRAM_TypeDef *sdram;
|
|
uint32_t power_up_delay;
|
|
uint8_t num_auto_refresh;
|
|
uint16_t mode_register;
|
|
uint16_t refresh_rate;
|
|
const struct memc_stm32_sdram_bank_config *banks;
|
|
size_t banks_len;
|
|
};
|
|
|
|
static int memc_stm32_sdram_init(const struct device *dev)
|
|
{
|
|
const struct memc_stm32_sdram_config *config = dev->config;
|
|
|
|
SDRAM_HandleTypeDef sdram = { 0 };
|
|
FMC_SDRAM_CommandTypeDef sdram_cmd = { 0 };
|
|
|
|
sdram.Instance = config->sdram;
|
|
|
|
for (size_t i = 0U; i < config->banks_len; i++) {
|
|
sdram.State = HAL_SDRAM_STATE_RESET;
|
|
memcpy(&sdram.Init, &config->banks[i].init, sizeof(sdram.Init));
|
|
|
|
(void)HAL_SDRAM_Init(
|
|
&sdram,
|
|
(FMC_SDRAM_TimingTypeDef *)&config->banks[i].timing);
|
|
}
|
|
|
|
/* SDRAM initialization sequence */
|
|
if (config->banks_len == 2U) {
|
|
sdram_cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1_2;
|
|
} else if (config->banks[0].init.SDBank == FMC_SDRAM_BANK1) {
|
|
sdram_cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
|
|
} else {
|
|
sdram_cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK2;
|
|
}
|
|
|
|
sdram_cmd.AutoRefreshNumber = config->num_auto_refresh;
|
|
sdram_cmd.ModeRegisterDefinition = config->mode_register;
|
|
|
|
/* enable clock */
|
|
sdram_cmd.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
|
|
(void)HAL_SDRAM_SendCommand(&sdram, &sdram_cmd, 0U);
|
|
|
|
k_usleep(config->power_up_delay);
|
|
|
|
/* pre-charge all */
|
|
sdram_cmd.CommandMode = FMC_SDRAM_CMD_PALL;
|
|
(void)HAL_SDRAM_SendCommand(&sdram, &sdram_cmd, 0U);
|
|
|
|
/* auto-refresh */
|
|
sdram_cmd.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
|
|
(void)HAL_SDRAM_SendCommand(&sdram, &sdram_cmd, 0U);
|
|
|
|
/* load mode */
|
|
sdram_cmd.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
|
|
(void)HAL_SDRAM_SendCommand(&sdram, &sdram_cmd, 0U);
|
|
|
|
/* program refresh count */
|
|
(void)HAL_SDRAM_ProgramRefreshRate(&sdram, config->refresh_rate);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** SDRAM bank/s configuration initialization macro. */
|
|
#define BANK_CONFIG(node_id) \
|
|
{ .init = { \
|
|
.SDBank = DT_REG_ADDR(node_id), \
|
|
.ColumnBitsNumber = DT_PROP_BY_IDX(node_id, st_sdram_control, 0), \
|
|
.RowBitsNumber = DT_PROP_BY_IDX(node_id, st_sdram_control, 1), \
|
|
.MemoryDataWidth = DT_PROP_BY_IDX(node_id, st_sdram_control, 2), \
|
|
.InternalBankNumber = DT_PROP_BY_IDX(node_id, st_sdram_control, 3),\
|
|
.CASLatency = DT_PROP_BY_IDX(node_id, st_sdram_control, 4), \
|
|
.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE, \
|
|
.SDClockPeriod = DT_PROP_BY_IDX(node_id, st_sdram_control, 5), \
|
|
.ReadBurst = DT_PROP_BY_IDX(node_id, st_sdram_control, 6), \
|
|
.ReadPipeDelay = DT_PROP_BY_IDX(node_id, st_sdram_control, 7), \
|
|
}, \
|
|
.timing = { \
|
|
.LoadToActiveDelay = DT_PROP_BY_IDX(node_id, st_sdram_timing, 0), \
|
|
.ExitSelfRefreshDelay = \
|
|
DT_PROP_BY_IDX(node_id, st_sdram_timing, 1), \
|
|
.SelfRefreshTime = DT_PROP_BY_IDX(node_id, st_sdram_timing, 2), \
|
|
.RowCycleDelay = DT_PROP_BY_IDX(node_id, st_sdram_timing, 3), \
|
|
.WriteRecoveryTime = DT_PROP_BY_IDX(node_id, st_sdram_timing, 4), \
|
|
.RPDelay = DT_PROP_BY_IDX(node_id, st_sdram_timing, 5), \
|
|
.RCDDelay = DT_PROP_BY_IDX(node_id, st_sdram_timing, 6), \
|
|
} \
|
|
},
|
|
|
|
/** SDRAM bank/s configuration. */
|
|
static const struct memc_stm32_sdram_bank_config bank_config[] = {
|
|
DT_INST_FOREACH_CHILD(0, BANK_CONFIG)
|
|
};
|
|
|
|
/** SDRAM configuration. */
|
|
static const struct memc_stm32_sdram_config config = {
|
|
.sdram = (FMC_SDRAM_TypeDef *)(DT_REG_ADDR(DT_PARENT(DT_DRV_INST(0))) +
|
|
SDRAM_OFFSET),
|
|
.power_up_delay = DT_INST_PROP(0, power_up_delay),
|
|
.num_auto_refresh = DT_INST_PROP(0, num_auto_refresh),
|
|
.mode_register = DT_INST_PROP(0, mode_register),
|
|
.refresh_rate = DT_INST_PROP(0, refresh_rate),
|
|
.banks = bank_config,
|
|
.banks_len = ARRAY_SIZE(bank_config),
|
|
};
|
|
|
|
DEVICE_DEFINE(memc_stm32_sdram, DT_INST_LABEL(0), memc_stm32_sdram_init, NULL,
|
|
NULL, &config, POST_KERNEL, CONFIG_MEMC_INIT_PRIORITY, NULL);
|