Skip to content

Commit

Permalink
drivers: memc: stm32: initial support for stm32 FMC
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
gmarull authored and carlescufi committed Nov 24, 2020
1 parent cb06f5e commit e671d36
Show file tree
Hide file tree
Showing 16 changed files with 574 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_subdirectory_ifdef(CONFIG_EEPROM eeprom)
add_subdirectory_ifdef(CONFIG_LORA lora)
add_subdirectory_ifdef(CONFIG_PECI peci)
add_subdirectory_ifdef(CONFIG_REGULATOR regulator)
add_subdirectory_ifdef(CONFIG_MEMC memc)

add_subdirectory_ifdef(CONFIG_FLASH_HAS_DRIVER_ENABLED flash)
add_subdirectory_ifdef(CONFIG_SERIAL_HAS_DRIVER serial)
Expand Down
2 changes: 2 additions & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,6 @@ source "drivers/peci/Kconfig"

source "drivers/regulator/Kconfig"

source "drivers/memc/Kconfig"

endmenu
3 changes: 3 additions & 0 deletions drivers/memc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
zephyr_sources_ifdef(CONFIG_MEMC_STM32 memc_stm32.c)
zephyr_sources_ifdef(CONFIG_MEMC_STM32_SDRAM memc_stm32_sdram.c)
zephyr_linker_sources_ifdef(CONFIG_MEMC_STM32_SDRAM SECTIONS memc_stm32_sdram.ld)
25 changes: 25 additions & 0 deletions drivers/memc/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Memory controller configuration options

# Copyright (c) 2020 Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0

menuconfig MEMC
bool "Memory controllers [EXPERIMENTAL]"
help
Add support for memory controllers

if MEMC

module = MEMC
module-str = memc
source "subsys/logging/Kconfig.template.log_config"

config MEMC_INIT_PRIORITY
int "Initialization priority"
default 0
help
Memory controllers initialization priority.

source "drivers/memc/Kconfig.stm32"

endif
19 changes: 19 additions & 0 deletions drivers/memc/Kconfig.stm32
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright (c) 2020 Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0

config MEMC_STM32
bool "Enable STM32 Flexible Memory Controller (FMC)"
depends on SOC_SERIES_STM32H7X
help
Enable STM32 Flexible Memory Controller.

DT_COMPAT_ST_STM32_FMC_SDRAM := st,stm32-fmc-sdram

config MEMC_STM32_SDRAM
bool "Enable STM32 FMC SDRAM controller"
depends on MEMC_STM32
default $(dt_compat_enabled,$(DT_COMPAT_ST_STM32_FMC_SDRAM))
select USE_STM32_LL_FMC
select USE_STM32_HAL_SDRAM
help
Enable STM32 FMC SDRAM controller.
63 changes: 63 additions & 0 deletions drivers/memc/memc_stm32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT st_stm32_fmc

#include <device.h>

#include <drivers/clock_control/stm32_clock_control.h>
#include <pinmux/stm32/pinmux_stm32.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(memc_stm32, CONFIG_MEMC_LOG_LEVEL);

struct memc_stm32_config {
uint32_t fmc;
struct stm32_pclken pclken;
const struct soc_gpio_pinctrl *pinctrl;
size_t pinctrl_len;
};

static int memc_stm32_init(const struct device *dev)
{
const struct memc_stm32_config *config = dev->config;

int r;
const struct device *clk;

/* configure pinmux */
r = stm32_dt_pinctrl_configure(config->pinctrl, config->pinctrl_len,
config->fmc);
if (r < 0) {
LOG_ERR("FMC pinctrl setup failed (%d)", r);
return r;
}

/* enable FMC peripheral clock */
clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
__ASSERT_NO_MSG(clk);

r = clock_control_on(clk, (clock_control_subsys_t *)&config->pclken);
if (r < 0) {
LOG_ERR("Could not initialize FMC clock (%d)", r);
return r;
}

return 0;
}

static const struct soc_gpio_pinctrl pinctrl[] = ST_STM32_DT_INST_PINCTRL(0, 0);

static const struct memc_stm32_config config = {
.fmc = DT_INST_REG_ADDR(0),
.pclken = { .bus = DT_INST_CLOCKS_CELL(0, bus),
.enr = DT_INST_CLOCKS_CELL(0, bits) },
.pinctrl = pinctrl,
.pinctrl_len = ARRAY_SIZE(pinctrl),
};

DEVICE_DEFINE(memc_stm32, DT_INST_LABEL(0), memc_stm32_init, NULL, NULL,
&config, POST_KERNEL, CONFIG_MEMC_INIT_PRIORITY, NULL);
133 changes: 133 additions & 0 deletions drivers/memc/memc_stm32_sdram.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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);
29 changes: 29 additions & 0 deletions drivers/memc/memc_stm32_sdram.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/

#if DT_NODE_HAS_STATUS(DT_NODELABEL(sdram1), okay)
GROUP_START(SDRAM1)

SECTION_PROLOGUE(_STM32_SDRAM1_SECTION_NAME, (NOLOAD),)
{
*(.stm32_sdram1)
*(".stm32_sdram1.*")
} GROUP_LINK_IN(SDRAM1)

GROUP_END(SDRAM1)
#endif

#if DT_NODE_HAS_STATUS(DT_NODELABEL(sdram2), okay)
GROUP_START(SDRAM2)

SECTION_PROLOGUE(_STM32_SDRAM2_SECTION_NAME, (NOLOAD),)
{
*(.stm32_sdram2)
*(".stm32_sdram2.*")
} GROUP_LINK_IN(SDRAM2)

GROUP_END(SDRAM2)
#endif
17 changes: 17 additions & 0 deletions dts/arm/st/h7/stm32h7.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/i2c/i2c.h>
#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/memory-controller/stm32-fmc-sdram.h>

/ {
chosen {
Expand Down Expand Up @@ -698,6 +699,22 @@
<&rcc STM32_CLOCK_BUS_AHB1 0x00020000>;
status = "disabled";
};

fmc: memory-controller@52004000 {
compatible = "st,stm32-fmc";
reg = <0x52004000 0x400>;
clocks = <&rcc STM32_CLOCK_BUS_AHB3 0x00001000>;
label = "STM32_FMC";
status = "disabled";

sdram: sdram {
compatible = "st,stm32-fmc-sdram";
#address-cells = <1>;
#size-cells = <0>;
label = "STM32_FMC_SDRAM";
status = "disabled";
};
};
};
};

Expand Down
Loading

0 comments on commit e671d36

Please sign in to comment.