mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:12:57 +01:00
cpu/stm32/periph: add FMC support
This commit is contained in:
parent
79198e92b9
commit
37472d54c3
@ -79,4 +79,8 @@ ifneq (,$(filter periph_vbat,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_adc
|
||||
endif
|
||||
|
||||
ifneq (,$(filter periph_fmc_%,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_fmc
|
||||
endif
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
||||
|
370
cpu/stm32/include/periph/cpu_fmc.h
Normal file
370
cpu/stm32/include/periph/cpu_fmc.h
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_stm32_periph_fmc STM32 FMC/FSMC peripheral driver
|
||||
* @ingroup cpu_stm32
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* The `periph_fmc` module implements a driver for STM32 FMC/FSMC peripherals.
|
||||
* It supports
|
||||
* - NOR Flashes,
|
||||
* - PSRAMs/SRAMs,
|
||||
* - SDRAMs, and
|
||||
* - Display Controllers with MCU 8-/16-bit parallel interface.
|
||||
*
|
||||
* NAND Flashes are not yet supported.
|
||||
*
|
||||
* To use the FMC/FSMC, the `periph_fmc` module has to be enabled.
|
||||
* To keep required data structures and resulting code as small as possible,
|
||||
* a couple of pseudomodules are defined that have to be used in addition to
|
||||
* the `periph_fmc` module to enable supported features. These are:
|
||||
*
|
||||
* | Module | Feature |
|
||||
* |:-----------------------|:----------------------------------------|
|
||||
* | `periph_fmc_nor_sram` | enable NOR Flash and PSRAM/SRAM support |
|
||||
* | `periph_fmc_sdram` | enable SDRAM support |
|
||||
* | `periph_fmc_16bit` | enable 16-bit support |
|
||||
* | `periph_fmc_32bit` | enable 32-bit support |
|
||||
* \n
|
||||
*
|
||||
* The board has then to define
|
||||
* - the corresponding features according to the connected external device,
|
||||
* - the peripheral configuration of the FMC/FSMC of type @ref fmc_conf_t,
|
||||
* - the configuration of the FMC banks which describe the connected external
|
||||
* devices.
|
||||
*
|
||||
* As examples for such configurations, see @ref boards_stm32l496g-disco
|
||||
* (FMC with Display and SRAM) or @ref boards_stm32f429i-disc1 (FMC with SDRAM).
|
||||
*
|
||||
* To use the RAM connected to the FMC as heap, the board has also to define
|
||||
* @ref FMC_RAM_ADDR and @ref FMC_RAM_LEN.
|
||||
*
|
||||
* @file
|
||||
* @brief Specific FMC definitions for the STM32
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_FMC_H
|
||||
#define PERIPH_CPU_FMC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/cpu_gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of data pins used
|
||||
*
|
||||
* The number of configured data pins depends on the memory with the maximum
|
||||
* data bus width. The maximum data bus width used has to be specified by the
|
||||
* pseudomodules `periph_fmc_16bit` and `periph_fmc_32bit`. 8 bit data bus
|
||||
* width is the default.
|
||||
*/
|
||||
#if MODULE_PERIPH_FMC_32BIT || DOXYGEN
|
||||
#define FMC_DATA_PIN_NUMOF (32)
|
||||
#elif MODULE_PERIPH_FMC_16BIT
|
||||
#define FMC_DATA_PIN_NUMOF (16)
|
||||
#else
|
||||
#define FMC_DATA_PIN_NUMOF (8)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of address pins used
|
||||
*
|
||||
* The number of configured address pins depends on the memory types used.
|
||||
* NORs, PSRAMs and SRAMs (module `periph_fmc_nor_sram`) use up to 26 address
|
||||
* signals A0...A25 if address/data multiplexing is not used. SDRAMs (module
|
||||
* `periph_fmc_sdram`) use only up to 13 address signals A0...A12.
|
||||
* NANDs (module `periph_fmc_sdram`) don't need separate address signals.
|
||||
* The type of used memories is specified by the corresponding pseudomodules.
|
||||
*/
|
||||
#if MODULE_PERIPH_FMC_NOR_SRAM || DOXYGEN
|
||||
#define FMC_ADDR_PIN_NUMOF (26)
|
||||
#elif MODULE_PERIPH_FMC_SDRAM
|
||||
#define FMC_ADDR_PIN_NUMOF (13)
|
||||
#else
|
||||
#define FMC_ADDR_PIN_NUMOF (0)
|
||||
#endif
|
||||
|
||||
#if DOXYGEN
|
||||
/**
|
||||
* @brief Start address of the heap for the FMC RAM
|
||||
*
|
||||
* This variable has to be defined in `Makefile.include` of the board
|
||||
* definition to use the a RAM connected to the FMC as heap. It has to
|
||||
* correspond to the @ref fmc_bank_conf_t::address in FMC bank configuration.
|
||||
*
|
||||
* @note `FMC_RAM_ADDR` is not a macro and cannot be used in C code.
|
||||
* It is just defined for documentation purpose.
|
||||
*/
|
||||
#define FMC_RAM_ADDR 0x60000000
|
||||
|
||||
/**
|
||||
* @brief Length of the heap for the FMC RAM
|
||||
*
|
||||
* This variable has to be defined in `Makefile.include` of the board
|
||||
* definition to use the a RAM connected to the FMC as heap. It has to
|
||||
* correspond to the @ref fmc_bank_conf_t::size in FMC bank configuration.
|
||||
* Since it is used in linker script, it has to be defined in kByte with
|
||||
* suffix `K`, e.g. `1024K`.
|
||||
*
|
||||
* @note `FMC_RAM_SIZE` is not a macro and cannot be used in C code.
|
||||
* It is just defined for documentation purpose.
|
||||
*/
|
||||
#define FMC_RAM_LEN 1024K
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Type declarations for NOR Flash, PSRAM and SRAM
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Memory access modes for NOR/PSRAM/SRAM in extended mode
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_MODE_A = 0, /**< Access mode A */
|
||||
FMC_MODE_B = 1, /**< Access mode B */
|
||||
FMC_MODE_C = 2, /**< Access mode C */
|
||||
FMC_MODE_D = 3, /**< Access mode D */
|
||||
} fmc_access_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Timing configuration for NOR/PSRAM/SRAM
|
||||
*
|
||||
* Used timing configuration parameters depend on used access mode. Please refer
|
||||
* the reference manual of the respective MCU for details.
|
||||
*/
|
||||
typedef struct {
|
||||
fmc_access_mode_t mode; /**< Access Mode used (only used if
|
||||
@ref fmc_nor_sram_bank_conf_t::ext_mode is true) */
|
||||
uint8_t clk_div; /**< Clock divide ratio, FMC_CLK = HCLK / (DIV + 1) */
|
||||
uint8_t addr_setup; /**< Address setup time [0..15], default 15 */
|
||||
uint8_t addr_hold; /**< Address hold time [0..15], default 15 */
|
||||
uint8_t data_setup; /**< Data setup time [0..15], default 15 */
|
||||
uint8_t data_latency; /**< Data latency for synchronous access [0..15],
|
||||
default 15 (only used in read timing) */
|
||||
uint8_t bus_turnaround; /**< Bus turnaround phase duration [0..15], default 15 */
|
||||
} fmc_nor_sram_timing_t;
|
||||
|
||||
/**
|
||||
* @brief Bank configuration structure for NOR/PSRAM/SRAM
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t sub_bank; /**< Bank1 has 4 subbanks 1..4 */
|
||||
bool mux_enable; /**< Multiplexed address/data signals used
|
||||
(only valid for PSRAMs and NORs */
|
||||
bool wait_enable; /**< Wait signal used for synchronous
|
||||
access */
|
||||
bool ext_mode; /**< Extended mode used (separate read and
|
||||
write timings) */
|
||||
fmc_nor_sram_timing_t r_timing; /**< Read timings (also used for write if
|
||||
@ref fmc_nor_sram_bank_conf_t::ext_mode is false) */
|
||||
fmc_nor_sram_timing_t w_timing; /**< Write timings (only used if
|
||||
@ref fmc_nor_sram_bank_conf_t::ext_mode is true) */
|
||||
} fmc_nor_sram_bank_conf_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Type declarations for SDRAMs
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief SDRAM Burst Length as an exponent of a power of two
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_BURST_LENGTH_1 = 0, /* Burst length is 1 */
|
||||
FMC_BURST_LENGTH_2 = 1, /* Burst length is 2 */
|
||||
FMC_BURST_LENGTH_4 = 2, /* Burst length is 4 */
|
||||
FMC_BURST_LENGTH_8 = 3, /* Burst length is 8 */
|
||||
FMC_BURST_LENGTH_16 = 4, /* Burst length is 16 */
|
||||
FMC_BURST_LENGTH_32 = 5, /* Burst length is 32 */
|
||||
FMC_BURST_LENGTH_64 = 6, /* Burst length is 64 */
|
||||
FMC_BURST_LENGTH_FULL = 7, /* Burst length is full page */
|
||||
} fmc_bust_length_t;
|
||||
|
||||
/**
|
||||
* @brief Timing configuration for SDRAM
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t row_to_col_delay; /**< Row to column delay in SDCLK clock
|
||||
cycles [1..16], delay between Activate
|
||||
and Read/Write command */
|
||||
uint8_t row_precharge; /**< Row precharge delay in SDCLK clock
|
||||
cycles [1..15], delay between Precharge
|
||||
and another command */
|
||||
uint8_t recovery_delay; /**< Recovery delay in SDCLK clock
|
||||
cycles [1..15], delay between Write and
|
||||
Precharge command */
|
||||
uint8_t row_cylce; /**< Row cycle delay in SDCLK clock
|
||||
cycles [1..15], delay between Refresh and
|
||||
Activate command */
|
||||
uint8_t self_refresh; /**< Self refresh time in SDCLK clock
|
||||
cycles [1..15] */
|
||||
uint8_t exit_self_refresh; /**< Exit self-refresh delay in SDCLK clock
|
||||
cycles [1..15], delay between Self-Refresh
|
||||
and Activate command */
|
||||
uint8_t load_mode_register; /**< Load Mode Register to Activate delay in
|
||||
SDCLK clock cycles [1..15], delay between
|
||||
Load Mode Register and Activate command */
|
||||
uint8_t refresh_period; /**< Refresh period in milliseconds */
|
||||
} fmc_sdram_timing_t;
|
||||
|
||||
/**
|
||||
* @brief Bank configuration structure for SDRAM
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t clk_period; /**< CLK period [0,2,3] (0 - disabled, n * HCLK cycles) */
|
||||
uint8_t row_bits; /**< Number row address bits [11..13] */
|
||||
uint8_t col_bits; /**< Number column address bits [8..11]*/
|
||||
uint8_t cas_latency; /**< CAS latency in SDCLK clock cycles [1..3] */
|
||||
uint8_t read_delay; /**< Delay for reading data after CAS latency in HCLKs [0..2] */
|
||||
bool four_banks; /**< SDRAM has four internal banks */
|
||||
bool write_protect; /**< Write protection enabled */
|
||||
bool burst_read; /**< Burst read mode enabled */
|
||||
bool burst_write; /**< Burst write mode enabled */
|
||||
bool burst_interleaved; /**< Burst mode interleaved, otherwise sequential */
|
||||
fmc_bust_length_t burst_len; /**< Burst length as an exponent of a power of two */
|
||||
fmc_sdram_timing_t timing; /**< SDRAM Timing configuration */
|
||||
} fmc_sdram_bank_conf_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Type declarations that are common for all memories
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief FMC GPIO configuration type
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< GPIO pin */
|
||||
gpio_af_t af; /**< Alternate function */
|
||||
} fmc_gpio_t;
|
||||
|
||||
/**
|
||||
* @brief FMC peripheral configuration
|
||||
*
|
||||
* The GPIOs are defined depending on used memory type according to the
|
||||
* FMC pin definition in Table 12 of section 4 in the
|
||||
* [Datasheet for STM32F765xx, STM32F767xx, STM32F768Ax, STM32F769xx]
|
||||
* (https://www.st.com/resource/en/datasheet/stm32f767zi.pdf).
|
||||
* Which memory types are used is defined by the pseudomodules
|
||||
* `periph_fmc_nor_sram`, `periph_fmc_nand` and `periph_fmc_sdram`
|
||||
*
|
||||
* @note For easier handling the configuration structure does not take
|
||||
* multiplexed address/data bits into account.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t bus; /**< AHB/APB bus */
|
||||
uint32_t rcc_mask; /**< Bit in clock enable register */
|
||||
fmc_gpio_t data[FMC_DATA_PIN_NUMOF]; /**< Data pins D0 ... */
|
||||
#if FMC_ADDR_PIN_NUMOF || DOXYGEN
|
||||
fmc_gpio_t addr[FMC_ADDR_PIN_NUMOF]; /**< Address pins A0 ... if any */
|
||||
#endif
|
||||
/* signals used by all kind of memories */
|
||||
fmc_gpio_t nbl0_pin; /**< NBL0 pin */
|
||||
fmc_gpio_t nbl1_pin; /**< NBL1 pin */
|
||||
fmc_gpio_t nbl2_pin; /**< NBL2 pin */
|
||||
fmc_gpio_t nbl3_pin; /**< NBL3 pin */
|
||||
fmc_gpio_t nwait_pin; /**< NWAIT pin */
|
||||
#if MODULE_PERIPH_FMC_NOR_SRAM
|
||||
/* NORs, PSRAMs, and SRAMs use CLK, NOE, NWE, NE, and NADV signals **/
|
||||
fmc_gpio_t clk_pin; /**< CLK pin */
|
||||
fmc_gpio_t noe_pin; /**< NOE pin (output enable / read enable) */
|
||||
fmc_gpio_t nwe_pin; /**< NWE pin (write enable) */
|
||||
fmc_gpio_t ne1_pin; /**< NE1 pin (subbank 1 enable) */
|
||||
fmc_gpio_t ne2_pin; /**< NE2 pin (subbank 2 enable) */
|
||||
fmc_gpio_t ne3_pin; /**< NE3 pin (subbank 3 enable) */
|
||||
fmc_gpio_t ne4_pin; /**< NE4 pin (subbank 4 enable) */
|
||||
fmc_gpio_t nadv_pin; /**< NADV pin (address valid) */
|
||||
#endif /* MODULE_PERIPH_FMC_NORSRAM */
|
||||
#if MODULE_PERIPH_FMC_SDRAM
|
||||
/* SDRAMs use BAx, CLK, RAS, CAS, WE, ... signals **/
|
||||
fmc_gpio_t ba0_pin; /**< BA0 pin */
|
||||
fmc_gpio_t ba1_pin; /**< BA1 pin */
|
||||
fmc_gpio_t sdclk_pin; /**< SDCLK pin */
|
||||
fmc_gpio_t sdnwe_pin; /**< SDWE pin */
|
||||
fmc_gpio_t sdnras_pin; /**< SDNRAS pin */
|
||||
fmc_gpio_t sdncas_pin; /**< SDNCAS pin */
|
||||
fmc_gpio_t sdcke0_pin; /**< SDCKE0 pin (SDRAM bank 5 clock enable) */
|
||||
fmc_gpio_t sdcke1_pin; /**< SDCKE1 pin (SDRAM bank 6 clock enable) */
|
||||
fmc_gpio_t sdne0_pin; /**< SDNE0 pin (SDRAM bank 5 enable) */
|
||||
fmc_gpio_t sdne1_pin; /**< SDNE1 pin (SDRAM bank 6 enable) */
|
||||
#endif /* MODULE_PERIPH_FMC_SDRAM */
|
||||
} fmc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Memory banks
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_BANK_1 = 1, /**< Bank 1 is always available and used for NOR, PSRAM, SRAM */
|
||||
#if defined(FMC_Bank2_3_R_BASE)
|
||||
FMC_BANK_2 = 2, /**< Bank2 is used for NAND if available */
|
||||
#endif
|
||||
#if defined(FMC_Bank2_3_R_BASE) || defined(FMC_Bank3_R_BASE)
|
||||
FMC_BANK_3 = 3, /**< Bank3 is used for NAND if available */
|
||||
#endif
|
||||
#if defined(FMC_Bank4_R_BASE)
|
||||
FMC_BANK_4 = 4, /**< Bank4 is used for PC Card if available */
|
||||
#endif
|
||||
#if defined(FMC_Bank5_6_R_BASE)
|
||||
FMC_BANK_5 = 5, /**< Bank5 is used for SDRAM if available */
|
||||
FMC_BANK_6 = 6, /**< Bank6 is used for SDRAM if available */
|
||||
#endif
|
||||
} fmc_bank_t;
|
||||
|
||||
/**
|
||||
* @brief Memory types supported by the FMC controller
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_SRAM = 0, /**< SRAM */
|
||||
FMC_PSRAM = 1, /**< PSRAM */
|
||||
FMC_NOR = 2, /**< NOR Flash */
|
||||
FMC_NAND = 3, /**< NAND Flash */
|
||||
FMC_SDRAM = 4, /**< SDRAM Controller used */
|
||||
} fmc_mem_type_t;
|
||||
|
||||
/**
|
||||
* @brief Memory data bus widths
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_BUS_WIDTH_8BIT = 0, /**< 8 bit data bus width */
|
||||
FMC_BUS_WIDTH_16BIT = 1, /**< 16 bit data bus width */
|
||||
FMC_BUS_WIDTH_32BIT = 2, /**< 32 bit data bus width */
|
||||
} fmc_bus_width_t;
|
||||
|
||||
/**
|
||||
* @brief Bank configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
fmc_bank_t bank; /**< Bank1 .. Bank4/Bank6 (maximum) */
|
||||
fmc_mem_type_t mem_type; /**< Type of memory */
|
||||
fmc_bus_width_t data_width; /**< Data bus width */
|
||||
uint32_t address; /**< Address of the memory bank */
|
||||
uint32_t size; /**< Size in bytes of the memory bank */
|
||||
union {
|
||||
fmc_nor_sram_bank_conf_t nor_sram; /* Configuration in case of NOR/PSRAM/SRAM */
|
||||
fmc_sdram_bank_conf_t sdram; /* Configuration in case of SDRAM */
|
||||
};
|
||||
} fmc_bank_conf_t;
|
||||
|
||||
typedef uint8_t fmc_bank_id_t; /**< FMC bank identifier */
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH_CPU_FMC_H */
|
||||
/** @} */
|
@ -62,6 +62,7 @@
|
||||
#include "periph/cpu_common.h"
|
||||
#include "periph/cpu_dma.h"
|
||||
#include "periph/cpu_eth.h"
|
||||
#include "periph/cpu_fmc.h"
|
||||
#include "periph/cpu_gpio.h"
|
||||
#include "periph/cpu_gpio_ll.h"
|
||||
#include "periph/cpu_i2c.h"
|
||||
|
575
cpu/stm32/periph/fmc.c
Normal file
575
cpu/stm32/periph/fmc.c
Normal file
@ -0,0 +1,575 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32
|
||||
* @ingroup drivers_periph_fmc
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief FMC peripheral driver implementation
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "pm_layered.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef FMC_BASE
|
||||
#define FMC_BASE (0x60000000UL)
|
||||
#endif
|
||||
|
||||
#if !defined(FMC_Bank1_R_BASE) && defined(FSMC_Bank1_R_BASE)
|
||||
/* FSMC Symbols are defined, mapping is required */
|
||||
|
||||
#define FMC_Bank1_R_BASE FSMC_Bank1_R_BASE /* (FMC_R_BASE + 0x0000UL) */
|
||||
#define FMC_Bank1_TypeDef FSMC_Bank1_TypeDef
|
||||
|
||||
#define FMC_Bank1E_R_BASE FSMC_Bank1E_R_BASE /* (FMC_R_BASE + 0x0104UL) */
|
||||
#define FMC_Bank1E_TypeDef FSMC_Bank1E_TypeDef
|
||||
|
||||
#if defined(FSMC_Bank2_3_R_BASE)
|
||||
#define FMC_Bank2_3_R_BASE FSMC_Bank2_3_R_BASE /* (FMC_R_BASE + 0x0060UL) */
|
||||
#define FMC_Bank2_3_TypeDef FSMC_Bank2_3_TypeDef
|
||||
#endif
|
||||
|
||||
#if defined(FSMC_Bank4_R_BASE)
|
||||
#define FMC_Bank4_R_BASE FSMC_Bank4_R_BASE /* (FMC_R_BASE + 0x00A0UL) */
|
||||
#define FMC_Bank4_TypeDef FSMC_Bank4_TypeDef
|
||||
#endif
|
||||
|
||||
#endif /* !defined(FMC_Bank1_R_BASE) && defined(FSMC_Bank1_R_BASE) */
|
||||
|
||||
#if !defined(FMC_Bank1) && defined(FMC_Bank1_R_BASE)
|
||||
#define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE)
|
||||
#endif
|
||||
#if !defined(FMC_Bank1E) && defined(FMC_Bank1E_R_BASE)
|
||||
#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE)
|
||||
#endif
|
||||
#if !defined(FMC_Bank2_3) && defined(FMC_Bank2_3_R_BASE)
|
||||
#define FMC_Bank2_3 ((FMC_Bank3_TypeDef *) FMC_Bank2_3_R_BASE)
|
||||
#endif
|
||||
#if !defined(FMC_Bank3) && defined(FMC_Bank3_R_BASE)
|
||||
#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE)
|
||||
#endif
|
||||
#if !defined(FMC_Bank4) && defined(FMC_Bank4_R_BASE)
|
||||
#define FMC_Bank4 ((FMC_Bank4_TypeDef *) FMC_Bank3_R_BASE)
|
||||
#endif
|
||||
#if !defined(FMC_Bank5_6) && defined(FMC_Bank5_6_R_BASE)
|
||||
#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE)
|
||||
#endif
|
||||
|
||||
#if defined(FMC_BCR1_MBKEN)
|
||||
/* if CMSIS header define FMC_BCR1_* macros instead of FMC_BCRx_*,
|
||||
* mapping is needed */
|
||||
#define FMC_BCRx_MBKEN FMC_BCR1_MBKEN
|
||||
#define FMC_BCRx_MBKEN_Pos FMC_BCR1_MBKEN_Pos
|
||||
#define FMC_BCRx_MUXEN FMC_BCR1_MUXEN
|
||||
#define FMC_BCRx_MUXEN_Pos FMC_BCR1_MUXEN_Pos
|
||||
#define FMC_BCRx_MTYP_Msk FMC_BCR1_MTYP_Msk
|
||||
#define FMC_BCRx_MTYP_Pos FMC_BCR1_MTYP_Pos
|
||||
#define FMC_BCRx_MWID_Msk FMC_BCR1_MWID_Msk
|
||||
#define FMC_BCRx_MWID_Pos FMC_BCR1_MWID_Pos
|
||||
#define FMC_BCRx_FACCEN FMC_BCR1_FACCEN
|
||||
#define FMC_BCRx_FACCEN_Pos FMC_BCR1_FACCEN_Pos
|
||||
#define FMC_BCRx_WAITEN FMC_BCR1_WAITEN
|
||||
#define FMC_BCRx_WAITEN_Pos FMC_BCR1_WAITEN_Pos
|
||||
#define FMC_BCRx_EXTMOD FMC_BCR1_EXTMOD
|
||||
#define FMC_BCRx_EXTMOD_Pos FMC_BCR1_EXTMOD_Pos
|
||||
|
||||
#define FMC_BTRx_ADDSET_Pos FMC_BTR1_ADDSET_Pos
|
||||
#define FMC_BTRx_ADDHLD_Pos FMC_BTR1_ADDHLD_Pos
|
||||
#define FMC_BTRx_DATAST_Pos FMC_BTR1_DATAST_Pos
|
||||
#define FMC_BTRx_DATLAT_Pos FMC_BTR1_DATLAT_Pos
|
||||
#define FMC_BTRx_BUSTURN_Pos FMC_BTR1_BUSTURN_Pos
|
||||
#define FMC_BTRx_CLKDIV_Pos FMC_BTR1_CLKDIV_Pos
|
||||
#endif /* defined(FMC_BCR1_MBKEN) */
|
||||
|
||||
static void _fmc_init_gpio_common(void)
|
||||
{
|
||||
for (unsigned i = 0; i < FMC_DATA_PIN_NUMOF; i++) {
|
||||
assert(gpio_is_valid(fmc_config.data[i].pin));
|
||||
gpio_init(fmc_config.data[i].pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.data[i].pin, fmc_config.data[i].af);
|
||||
}
|
||||
|
||||
#if FMC_ADDR_PIN_NUMOF
|
||||
for (unsigned i = 0; i < FMC_ADDR_PIN_NUMOF; i++) {
|
||||
if (gpio_is_valid(fmc_config.addr[i].pin)) {
|
||||
gpio_init(fmc_config.addr[i].pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.addr[i].pin, fmc_config.addr[i].af);
|
||||
}
|
||||
}
|
||||
#endif /* FMC_ADDR_PIN_NUMOF */
|
||||
|
||||
if (gpio_is_valid(fmc_config.nbl0_pin.pin)) {
|
||||
gpio_init(fmc_config.nbl0_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nbl0_pin.pin, fmc_config.nbl0_pin.af);
|
||||
}
|
||||
if (gpio_is_valid(fmc_config.nbl1_pin.pin)) {
|
||||
gpio_init(fmc_config.nbl1_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nbl1_pin.pin, fmc_config.nbl1_pin.af);
|
||||
}
|
||||
if (gpio_is_valid(fmc_config.nbl2_pin.pin)) {
|
||||
gpio_init(fmc_config.nbl2_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nbl2_pin.pin, fmc_config.nbl2_pin.af);
|
||||
}
|
||||
if (gpio_is_valid(fmc_config.nbl3_pin.pin)) {
|
||||
gpio_init(fmc_config.nbl3_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nbl3_pin.pin, fmc_config.nbl3_pin.af);
|
||||
}
|
||||
if (gpio_is_valid(fmc_config.nwait_pin.pin)) {
|
||||
gpio_init(fmc_config.nwait_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nwait_pin.pin, fmc_config.nwait_pin.af);
|
||||
}
|
||||
}
|
||||
|
||||
#if MODULE_PERIPH_FMC_NOR_SRAM
|
||||
static void _fmc_init_gpio_nor_sram(uint8_t sub_bank, fmc_mem_type_t type)
|
||||
{
|
||||
/* at least either NOE or NWE has to be valid */
|
||||
assert(gpio_is_valid(fmc_config.noe_pin.pin) ||
|
||||
gpio_is_valid(fmc_config.nwe_pin.pin));
|
||||
|
||||
if (gpio_is_valid(fmc_config.clk_pin.pin)) {
|
||||
gpio_init(fmc_config.clk_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.clk_pin.pin, fmc_config.clk_pin.af);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(fmc_config.noe_pin.pin)) {
|
||||
gpio_init(fmc_config.noe_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.noe_pin.pin, fmc_config.noe_pin.af);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(fmc_config.nwe_pin.pin)) {
|
||||
gpio_init(fmc_config.nwe_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nwe_pin.pin, fmc_config.nwe_pin.af);
|
||||
}
|
||||
|
||||
if (type == FMC_NOR) {
|
||||
/* NADV has to be valid for NOR Flash memories */
|
||||
assert(gpio_is_valid(fmc_config.nadv_pin.pin));
|
||||
gpio_init(fmc_config.nadv_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.nadv_pin.pin, fmc_config.nadv_pin.af);
|
||||
}
|
||||
|
||||
/* the corresponding NE pin has to be valid */
|
||||
switch (sub_bank) {
|
||||
case 1:
|
||||
assert(gpio_is_valid(fmc_config.ne1_pin.pin));
|
||||
gpio_init(fmc_config.ne1_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.ne1_pin.pin, fmc_config.ne1_pin.af);
|
||||
break;
|
||||
case 2:
|
||||
assert(gpio_is_valid(fmc_config.ne2_pin.pin));
|
||||
gpio_init(fmc_config.ne2_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.ne2_pin.pin, fmc_config.ne2_pin.af);
|
||||
break;
|
||||
case 3:
|
||||
assert(gpio_is_valid(fmc_config.ne3_pin.pin));
|
||||
gpio_init(fmc_config.ne3_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.ne3_pin.pin, fmc_config.ne3_pin.af);
|
||||
break;
|
||||
case 4:
|
||||
assert(gpio_is_valid(fmc_config.ne4_pin.pin));
|
||||
gpio_init(fmc_config.ne4_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.ne4_pin.pin, fmc_config.ne4_pin.af);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _fmc_init_nor_sram_bank(const fmc_bank_conf_t *bank_conf)
|
||||
{
|
||||
DEBUG("[%s] subbank %u\n", __func__, bank_conf->nor_sram.sub_bank);
|
||||
|
||||
const fmc_nor_sram_bank_conf_t *conf = &bank_conf->nor_sram;
|
||||
assert((bank_conf->nor_sram.sub_bank) >= 1 &&
|
||||
(bank_conf->nor_sram.sub_bank) <= 4);
|
||||
|
||||
_fmc_init_gpio_nor_sram(conf->sub_bank, bank_conf->mem_type);
|
||||
|
||||
uint32_t *bcr1 = (uint32_t *)(FMC_Bank1_R_BASE);
|
||||
uint32_t *bcr = (uint32_t *)(FMC_Bank1_R_BASE + ((conf->sub_bank - 1) * 8));
|
||||
uint32_t *btr = (uint32_t *)(FMC_Bank1_R_BASE + ((conf->sub_bank - 1) * 8) + 0x04);
|
||||
uint32_t *bwtr = (uint32_t *)(FMC_Bank1E_R_BASE + ((conf->sub_bank - 1) * 8));
|
||||
|
||||
/* disable Memory Bank 1 */
|
||||
*bcr &= ~FMC_BCRx_MBKEN;
|
||||
|
||||
/* set read timing */
|
||||
*btr = conf->r_timing.mode
|
||||
| (conf->r_timing.addr_setup << FMC_BTRx_ADDSET_Pos)
|
||||
| (conf->r_timing.addr_hold << FMC_BTRx_ADDHLD_Pos)
|
||||
| (conf->r_timing.data_setup << FMC_BTRx_DATAST_Pos)
|
||||
| (conf->r_timing.data_latency << FMC_BTRx_DATLAT_Pos)
|
||||
| (conf->r_timing.bus_turnaround << FMC_BTRx_BUSTURN_Pos)
|
||||
| (conf->r_timing.clk_div << FMC_BTRx_CLKDIV_Pos);
|
||||
|
||||
if (conf->mux_enable) {
|
||||
/* enable Extended mode if set and set write timings */
|
||||
*bcr |= FMC_BCRx_EXTMOD;
|
||||
*bwtr = conf->w_timing.mode
|
||||
| (conf->w_timing.addr_setup << FMC_BTRx_ADDSET_Pos)
|
||||
| (conf->w_timing.addr_hold << FMC_BTRx_ADDHLD_Pos)
|
||||
| (conf->w_timing.data_setup << FMC_BTRx_DATAST_Pos)
|
||||
| (conf->w_timing.bus_turnaround << FMC_BTRx_BUSTURN_Pos);
|
||||
}
|
||||
else {
|
||||
*bcr &= ~FMC_BCRx_EXTMOD;
|
||||
}
|
||||
|
||||
#ifdef FMC_BCR1_WFDIS
|
||||
/* disable Write FIFO for all subbanks */
|
||||
*bcr1 |= FMC_BCR1_WFDIS;
|
||||
#else
|
||||
(void)bcr1;
|
||||
#endif
|
||||
|
||||
/* set memory Type */
|
||||
*bcr &= ~FMC_BCRx_MTYP_Msk;
|
||||
*bcr |= bank_conf->mem_type << FMC_BCRx_MTYP_Pos;
|
||||
|
||||
/* set Memory Type to 16 bit (reset default) */
|
||||
*bcr &= ~FMC_BCRx_MWID_Msk;
|
||||
*bcr |= bank_conf->data_width << FMC_BCRx_MWID_Pos;
|
||||
|
||||
/* clear Write Wait, Flash Access and Address/Data Multiplexing */
|
||||
*bcr &= ~(FMC_BCRx_WAITEN | FMC_BCRx_FACCEN | FMC_BCRx_MUXEN);
|
||||
|
||||
/* enable WAIT signal if set */
|
||||
*bcr |= (conf->wait_enable) << FMC_BCRx_WAITEN_Pos;
|
||||
|
||||
/* enable NOR Flash memory access in case of NOR Flash */
|
||||
*bcr |= (bank_conf->mem_type == FMC_NOR) << FMC_BCRx_FACCEN_Pos;
|
||||
|
||||
/* enable MUX in case of PSRAM or NOR if set */
|
||||
*bcr |= (((bank_conf->mem_type == FMC_PSRAM) ||
|
||||
(bank_conf->mem_type == FMC_NOR)) &&
|
||||
conf->mux_enable) << FMC_BCRx_MUXEN_Pos;
|
||||
|
||||
/* leave all other values at reset default 0x000030d2 */
|
||||
/* enable Memory Bank 1 */
|
||||
*bcr |= FMC_BCRx_MBKEN; /* Memory Bank Enable = enabled */
|
||||
|
||||
DEBUG("[%s] FMC BCRx=%08lx BTRx=%08lx BWTR=%08lx\n",
|
||||
__func__, *bcr, *btr, *bwtr);
|
||||
}
|
||||
#endif /* MODULE_PERIPH_FMC_NOR_SRAM */
|
||||
|
||||
#if MODULE_PERIPH_FMC_SDRAM
|
||||
/**
|
||||
* @brief SDRAM Command Mode Register definition
|
||||
*/
|
||||
typedef union {
|
||||
struct __attribute__((__packed__)) {
|
||||
uint32_t command:3; /**< Command mode */
|
||||
uint32_t target:2; /**< Command target */
|
||||
uint32_t auto_refresh:4; /**< Number of auto-refresh cycles */
|
||||
uint32_t mode_reg:13; /**< Mode register */
|
||||
uint32_t reserved:10; /**< reserved bits */
|
||||
};
|
||||
uint32_t value;
|
||||
} fmc_sdram_sdcmr_t;
|
||||
|
||||
/**
|
||||
* @brief SDRAM Mode Register definition
|
||||
*/
|
||||
typedef union __attribute__((__packed__)) {
|
||||
struct {
|
||||
uint16_t burst_len:3; /**< Burst length (0..6: 2^burst_len, 7: full page) */
|
||||
uint16_t burst_type:1; /**< Burst type (0: sequential, 1: interleaved) */
|
||||
uint16_t cas_latency:3; /**< CAS latency (1, 2, 3, other values are reserved) */
|
||||
uint16_t op_mode:2; /**< Operating mode (must be 0) */
|
||||
uint16_t write_burst:1; /**< Write burst mode (0: use read burst length, 1: no bursts) */
|
||||
uint16_t unused:6; /**< unused */
|
||||
};
|
||||
uint16_t value;
|
||||
} fmc_sdram_modereg_t;
|
||||
|
||||
/**
|
||||
* @brief SDRAM Command Mode
|
||||
*/
|
||||
typedef enum {
|
||||
FMC_CMD_NORMAL_MODE = 0, /**< Normal Mode */
|
||||
FMC_CMD_CLK_ENABLE = 1, /**< Clock Configuration Enable */
|
||||
FMC_CMD_PALL = 2, /**< All Bank Precharge command */
|
||||
FMC_CMD_AUTO_REFRESH = 3, /**< Auto-refresh command */
|
||||
FMC_CMD_LOAD_MODE = 4, /**< Load Mode Register */
|
||||
FMC_CMD_SELF_REFRESH = 5, /**< Self-refresh command */
|
||||
FMC_CMD_POWER_DOWN = 6, /**< Power-down command */
|
||||
FMC_CMD_RESERVED = 7, /**< reserved */
|
||||
} fmc_sdram_cmd_t;
|
||||
|
||||
static void _fmc_init_gpio_sdram(fmc_bank_t bank)
|
||||
{
|
||||
/* CLK, RAS, CAS, BA0, BA1 and WE must be valid */
|
||||
assert(gpio_is_valid(fmc_config.sdclk_pin.pin) &&
|
||||
gpio_is_valid(fmc_config.sdnras_pin.pin) &&
|
||||
gpio_is_valid(fmc_config.sdncas_pin.pin) &&
|
||||
gpio_is_valid(fmc_config.sdnwe_pin.pin) &&
|
||||
gpio_is_valid(fmc_config.ba0_pin.pin) &&
|
||||
gpio_is_valid(fmc_config.ba1_pin.pin));
|
||||
|
||||
gpio_init(fmc_config.ba0_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.ba1_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdclk_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdnras_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdncas_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdnwe_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.ba0_pin.pin, fmc_config.ba0_pin.af);
|
||||
gpio_init_af(fmc_config.ba1_pin.pin, fmc_config.ba1_pin.af);
|
||||
gpio_init_af(fmc_config.sdclk_pin.pin, fmc_config.sdclk_pin.af);
|
||||
gpio_init_af(fmc_config.sdnras_pin.pin, fmc_config.sdnras_pin.af);
|
||||
gpio_init_af(fmc_config.sdncas_pin.pin, fmc_config.sdncas_pin.af);
|
||||
gpio_init_af(fmc_config.sdnwe_pin.pin, fmc_config.sdnwe_pin.af);
|
||||
|
||||
/* corresponding NE pin and CKE pin must be valid */
|
||||
if (bank == FMC_BANK_5) {
|
||||
assert(gpio_is_valid(fmc_config.sdne0_pin.pin));
|
||||
assert(gpio_is_valid(fmc_config.sdcke0_pin.pin));
|
||||
gpio_init(fmc_config.sdne0_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdcke0_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.sdne0_pin.pin, fmc_config.sdne0_pin.af);
|
||||
gpio_init_af(fmc_config.sdcke0_pin.pin, fmc_config.sdcke0_pin.af);
|
||||
}
|
||||
else {
|
||||
assert(gpio_is_valid(fmc_config.sdne1_pin.pin));
|
||||
assert(gpio_is_valid(fmc_config.sdcke1_pin.pin));
|
||||
gpio_init(fmc_config.sdne1_pin.pin, GPIO_OUT);
|
||||
gpio_init(fmc_config.sdcke1_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(fmc_config.sdne1_pin.pin, fmc_config.sdne1_pin.af);
|
||||
gpio_init_af(fmc_config.sdcke1_pin.pin, fmc_config.sdcke1_pin.af);
|
||||
}
|
||||
}
|
||||
|
||||
static void _fmc_init_sdram_bank(const fmc_bank_conf_t *bank_conf)
|
||||
{
|
||||
_fmc_init_gpio_sdram(bank_conf->bank);
|
||||
|
||||
const fmc_sdram_bank_conf_t *conf = &bank_conf->sdram;
|
||||
|
||||
uint8_t bank = (bank_conf->bank == FMC_BANK_5) ? 0 : 1;
|
||||
uint32_t sdcr;
|
||||
|
||||
/* following settings are always configured in SDCR1 independent on the
|
||||
* bank and are don't care in SDCR2 */
|
||||
sdcr = FMC_Bank5_6->SDCR[0];
|
||||
|
||||
/* set read delay */
|
||||
assert(conf->read_delay <= 2);
|
||||
sdcr &= ~FMC_SDCR1_RPIPE_Msk;
|
||||
sdcr |= conf->read_delay << FMC_SDCR1_RPIPE_Pos;
|
||||
|
||||
/* clear and set RBURST if enabled */
|
||||
sdcr &= ~FMC_SDCR1_RBURST;
|
||||
sdcr |= conf->burst_read << FMC_SDCR1_RBURST_Pos;
|
||||
|
||||
/* set clock period */
|
||||
assert(conf->clk_period <= 3);
|
||||
sdcr &= ~FMC_SDCR1_SDCLK_Msk;
|
||||
sdcr |= conf->clk_period << FMC_SDCR1_SDCLK_Pos;
|
||||
|
||||
/* write back register SDCR1 */
|
||||
FMC_Bank5_6->SDCR[0] = sdcr;
|
||||
|
||||
/* other settings are done in SDCRx for bannk x */
|
||||
sdcr = FMC_Bank5_6->SDCR[bank];
|
||||
|
||||
/* set number of row and column bits and the data bus width */
|
||||
assert((conf->row_bits >= 11) && (conf->row_bits <= 13));
|
||||
assert((conf->col_bits >= 8) && (conf->col_bits <= 11));
|
||||
sdcr &= ~(FMC_SDCR1_NR_Msk | FMC_SDCR1_NC_Msk | FMC_SDCR1_MWID_Msk);
|
||||
sdcr |= (conf->row_bits - 11) << FMC_SDCR1_NR_Pos;
|
||||
sdcr |= (conf->col_bits - 8) << FMC_SDCR1_NC_Pos;
|
||||
sdcr |= bank_conf->data_width << FMC_SDCR1_MWID_Pos;
|
||||
|
||||
/* set CAS latency */
|
||||
assert((conf->cas_latency >= 1) && (conf->cas_latency <= 3));
|
||||
sdcr &= ~FMC_SDCR1_CAS_Msk;
|
||||
sdcr |= conf->cas_latency << FMC_SDCR1_CAS_Pos;
|
||||
|
||||
/* clear and set NB and WP */
|
||||
sdcr &= ~(FMC_SDCR1_NB | FMC_SDCR1_WP);
|
||||
sdcr |= conf->four_banks << FMC_SDCR1_NB_Pos;
|
||||
sdcr |= conf->write_protect << FMC_SDCR1_WP_Pos;
|
||||
|
||||
/* write back register SDCRx */
|
||||
FMC_Bank5_6->SDCR[bank] = sdcr;
|
||||
|
||||
uint32_t sdtr = 0;
|
||||
|
||||
assert((conf->timing.row_to_col_delay - 1) <= 15);
|
||||
assert((conf->timing.row_precharge - 1) <= 15);
|
||||
assert((conf->timing.recovery_delay - 1) <= 15);
|
||||
assert((conf->timing.row_cylce - 1) <= 15);
|
||||
assert((conf->timing.self_refresh - 1) <= 15);
|
||||
assert((conf->timing.exit_self_refresh - 1) <= 15);
|
||||
assert((conf->timing.load_mode_register - 1) <= 15);
|
||||
|
||||
/* following settings are always configured in SDTR1 independent on the
|
||||
* bank and are don't care in SDTR2 */
|
||||
sdtr = FMC_Bank5_6->SDTR[0];
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TRP;
|
||||
sdtr |= (conf->timing.row_precharge - 1) << FMC_SDTR1_TRP_Pos;
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TRC;
|
||||
sdtr |= (conf->timing.row_cylce - 1) << FMC_SDTR1_TRC_Pos;
|
||||
|
||||
/* write back register SDCRx */
|
||||
FMC_Bank5_6->SDTR[0] = sdtr;
|
||||
|
||||
/* other settings are done in SDTRx for bannk x */
|
||||
sdtr = FMC_Bank5_6->SDTR[0];
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TRCD;
|
||||
sdtr |= (conf->timing.row_to_col_delay - 1) << FMC_SDTR1_TRCD_Pos;
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TWR;
|
||||
sdtr |= (conf->timing.recovery_delay - 1) << FMC_SDTR1_TWR_Pos;
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TRAS;
|
||||
sdtr |= (conf->timing.self_refresh - 1) << FMC_SDTR1_TRAS_Pos;
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TXSR;
|
||||
sdtr |= (conf->timing.exit_self_refresh - 1) << FMC_SDTR1_TXSR_Pos;
|
||||
|
||||
sdtr &= ~FMC_SDTR1_TMRD;
|
||||
sdtr |= (conf->timing.load_mode_register - 1) << FMC_SDTR1_TMRD_Pos;
|
||||
|
||||
/* write register SDTRx */
|
||||
FMC_Bank5_6->SDTR[bank] = sdtr;
|
||||
|
||||
DEBUG("[%s] DMC SDCR%d=%08lx SDTR%d=%08lx SDCMR=%08lx SDRTR=%08lx SDSR=%08lx\n",
|
||||
__func__,
|
||||
bank, FMC_Bank5_6->SDCR[bank],
|
||||
bank, FMC_Bank5_6->SDTR[bank],
|
||||
FMC_Bank5_6->SDCMR, FMC_Bank5_6->SDRTR, FMC_Bank5_6->SDSR);
|
||||
|
||||
/* Initialization sequence according to the reference manual */
|
||||
|
||||
fmc_sdram_sdcmr_t sdcmr = { .target = (bank) ? 0b01 : 0b10,
|
||||
.auto_refresh = 1,
|
||||
.mode_reg = 0 };
|
||||
|
||||
/* enable clock (SDCKE HIGH) */
|
||||
sdcmr.command = FMC_CMD_CLK_ENABLE;
|
||||
FMC_Bank5_6->SDCMR = sdcmr.value;
|
||||
|
||||
/* wait at least 100 us */
|
||||
uint32_t count = 0xffffff;
|
||||
while (count--) {}
|
||||
|
||||
/* issue Precharge All command */
|
||||
sdcmr.command = FMC_CMD_PALL;
|
||||
FMC_Bank5_6->SDCMR = sdcmr.value;
|
||||
|
||||
/* issue the typical number of Auto-refresh commands */
|
||||
sdcmr.command = FMC_CMD_AUTO_REFRESH;
|
||||
sdcmr.auto_refresh = 8;
|
||||
FMC_Bank5_6->SDCMR = sdcmr.value;
|
||||
|
||||
/* load mode register */
|
||||
fmc_sdram_modereg_t modreg = {
|
||||
.burst_len = 0, // conf->burst_len,
|
||||
.burst_type = conf->burst_interleaved,
|
||||
.cas_latency = conf->cas_latency,
|
||||
.op_mode = 0,
|
||||
.write_burst = (conf->burst_write) ? 0 : 1,
|
||||
};
|
||||
sdcmr.command = FMC_CMD_LOAD_MODE;
|
||||
sdcmr.auto_refresh = 1;
|
||||
sdcmr.mode_reg = modreg.value;
|
||||
FMC_Bank5_6->SDCMR = sdcmr.value;
|
||||
|
||||
/* set refresh timer register */
|
||||
uint32_t cycles;
|
||||
cycles = CLOCK_AHB / KHZ(1) / conf->clk_period; /* SDCLK cycles per ms */
|
||||
cycles = cycles * conf->timing.refresh_period; /* SDCLK cycles per refresh period */
|
||||
cycles = cycles / (1 << conf->row_bits); /* SDCLK cycles per row */
|
||||
cycles = ((cycles - 20) > 41) ? cycles - 20 : 41; /* cycles - margin > min 41 */
|
||||
FMC_Bank5_6->SDRTR &= FMC_SDRTR_COUNT_Msk;
|
||||
FMC_Bank5_6->SDRTR |= cycles << FMC_SDRTR_COUNT_Pos;
|
||||
}
|
||||
#endif
|
||||
|
||||
void fmc_init_bank(fmc_bank_id_t bank)
|
||||
{
|
||||
DEBUG("[%s] bank id %u\n", __func__, bank);
|
||||
|
||||
assert(bank < FMC_BANK_NUMOF);
|
||||
|
||||
const fmc_bank_conf_t *conf = &fmc_bank_config[bank];
|
||||
|
||||
/* ensure that configured bank is valid */
|
||||
switch (conf->bank) {
|
||||
case 1: break;
|
||||
#if defined(FMC_Bank2_3_R_BASE)
|
||||
case 2: break;
|
||||
#endif
|
||||
#if defined(FMC_Bank2_3_R_BASE) || defined(FMC_Bank3_R_BASE)
|
||||
case 3: break;
|
||||
#endif
|
||||
#if defined(FMC_Bank4_R_BASE)
|
||||
case 4: break;
|
||||
#endif
|
||||
#if defined(FMC_Bank5_6_R_BASE)
|
||||
case 5:
|
||||
case 6: break;
|
||||
#endif
|
||||
default: assert(false);
|
||||
}
|
||||
|
||||
if (conf->bank == FMC_BANK_1) {
|
||||
#if MODULE_PERIPH_FMC_NOR_SRAM
|
||||
/* bank 1 has to be NOR, PSRAM or SRAM */
|
||||
assert((conf->mem_type == FMC_SRAM) ||
|
||||
(conf->mem_type == FMC_PSRAM) ||
|
||||
(conf->mem_type == FMC_NOR));
|
||||
_fmc_init_nor_sram_bank(conf);
|
||||
#else
|
||||
LOG_WARNING("NOR/PSRAM/SRAM configured but not enabled by feature periph_fmc_sdram\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(FMC_Bank5_6_R_BASE)
|
||||
else if ((conf->bank == FMC_BANK_5) || (conf->bank == FMC_BANK_6)) {
|
||||
#if MODULE_PERIPH_FMC_SDRAM
|
||||
_fmc_init_sdram_bank(conf);
|
||||
#else
|
||||
LOG_WARNING("SDRAM configured but not enabled by feature periph_fmc_nor_sram\n");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
else {
|
||||
LOG_ERROR("[fmc] Bank %u not supported\n", conf->bank);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
void fmc_init(void)
|
||||
{
|
||||
DEBUG("[%s]\n", __func__);
|
||||
|
||||
periph_clk_en(fmc_config.bus, fmc_config.rcc_mask);
|
||||
|
||||
_fmc_init_gpio_common();
|
||||
for (unsigned i = 0; i < FMC_BANK_NUMOF; i++) {
|
||||
fmc_init_bank(i);
|
||||
}
|
||||
}
|
@ -117,6 +117,11 @@ void periph_init(void)
|
||||
ptp_init();
|
||||
#endif
|
||||
|
||||
#if defined(MODULE_PERIPH_INIT_FMC)
|
||||
extern void fmc_init(void);
|
||||
fmc_init();
|
||||
#endif
|
||||
|
||||
#if defined(MODULE_PERIPH_INIT_VBAT)
|
||||
vbat_init();
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user