diff --git a/boards/atxmega-a1u-xpro/include/periph_conf.h b/boards/atxmega-a1u-xpro/include/periph_conf.h index 66c1c283ff..68926e4269 100644 --- a/boards/atxmega-a1u-xpro/include/periph_conf.h +++ b/boards/atxmega-a1u-xpro/include/periph_conf.h @@ -130,6 +130,18 @@ static const spi_conf_t spi_config[] = { #define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ +/** + * @name EBI configuration + * + * For more information, see ebi_conf_t structure. + * + * @{ + */ +static const ebi_conf_t ebi_config = { + 0 +}; +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/atxmega/Kconfig b/cpu/atxmega/Kconfig index 7897231d3e..2878c2d329 100644 --- a/cpu/atxmega/Kconfig +++ b/cpu/atxmega/Kconfig @@ -26,6 +26,7 @@ config CPU_COMMON_ATXMEGA config CPU_CORE_ATXMEGA_A1 bool select CPU_COMMON_ATXMEGA + select HAS_ATXMEGA_EBI config CPU_CORE_ATXMEGA_A3 bool @@ -76,6 +77,11 @@ source "$(RIOTCPU)/atxmega/Kconfig.XMEGAE" config HAS_CPU_ATXMEGA bool +config HAS_ATXMEGA_EBI + bool + help + Indicates that the External Bus Interface is present. + config HAS_PERIPH_NVM bool help diff --git a/cpu/atxmega/Makefile b/cpu/atxmega/Makefile index 4e5fcf7a8c..a4609f15d9 100644 --- a/cpu/atxmega/Makefile +++ b/cpu/atxmega/Makefile @@ -4,4 +4,8 @@ MODULE = cpu # add a list of subdirectories, that should also be build DIRS = periph $(RIOTCPU)/avr8_common/ +ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-))) + DIRS += ebi +endif + include $(RIOTBASE)/Makefile.base diff --git a/cpu/atxmega/Makefile.dep b/cpu/atxmega/Makefile.dep index 562b7c960d..b9244839c9 100644 --- a/cpu/atxmega/Makefile.dep +++ b/cpu/atxmega/Makefile.dep @@ -8,4 +8,8 @@ ifeq (,$(filter cpuid,$(USEMODULE))) USEMODULE += periph_nvm endif +ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-))) + USEMODULE += atxmega_ebi +endif + include $(RIOTCPU)/avr8_common/Makefile.dep diff --git a/cpu/atxmega/Makefile.features b/cpu/atxmega/Makefile.features index 3bc4269134..5543a6d28c 100644 --- a/cpu/atxmega/Makefile.features +++ b/cpu/atxmega/Makefile.features @@ -3,6 +3,10 @@ include $(RIOTCPU)/avr8_common/Makefile.features # common feature are defined in avr8_common/Makefile.features # Only add Additional features +ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-))) + FEATURES_PROVIDED += atxmega_ebi +endif + FEATURES_PROVIDED += cpu_core_atxmega FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio periph_gpio_irq diff --git a/cpu/atxmega/Makefile.include b/cpu/atxmega/Makefile.include index e8365a18e4..5448ab0c4a 100644 --- a/cpu/atxmega/Makefile.include +++ b/cpu/atxmega/Makefile.include @@ -1,5 +1,8 @@ export CPU_ATXMEGA 1 +# should expand RAM ? +EXP_RAM = 0 + # CPU ROM/RAM ifneq (,$(findstring atxmega8,$(CPU_MODEL))) RAM_LEN = 1K @@ -14,13 +17,13 @@ ifneq (,$(findstring atxmega32,$(CPU_MODEL))) ROM_LEN = 32K endif ifneq (,$(findstring atxmega64,$(CPU_MODEL))) - RAM_LEN ?= 4K + RAM_LEN = 4K ROM_LEN = 64K endif -#ifneq (,$(findstring atxmega128,$(CPU_MODEL))) - RAM_LEN ?= 8K +ifneq (,$(findstring atxmega128,$(CPU_MODEL))) + RAM_LEN = 8K ROM_LEN = 128K -#endif +endif ifneq (,$(findstring atxmega192,$(CPU_MODEL))) RAM_LEN = 16K ROM_LEN = 192K @@ -34,5 +37,14 @@ ifneq (,$(findstring atxmega384,$(CPU_MODEL))) ROM_LEN = 384K endif +ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-))) + ifeq ($(EXP_RAM),1) + CFLAGS += -DRAM_LEN=$(RAM_LEN)-1 + LDSCRIPT_EXTRA = -Wl,--defsym=__heap_end=0x800000+$(RAM_LEN)-1 + endif +endif + # CPU depends on the avr8 common module, so include it include $(RIOTCPU)/avr8_common/Makefile.include + +PSEUDOMODULE += atxmega_ebi diff --git a/cpu/atxmega/ebi/Makefile b/cpu/atxmega/ebi/Makefile new file mode 100644 index 0000000000..1252119f32 --- /dev/null +++ b/cpu/atxmega/ebi/Makefile @@ -0,0 +1,4 @@ +# define the module that is build +MODULE = atxmega_ebi + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/atxmega/ebi/ebi.c b/cpu/atxmega/ebi/ebi.c new file mode 100644 index 0000000000..caeb0ca1a7 --- /dev/null +++ b/cpu/atxmega/ebi/ebi.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2021 Gerson Fernando Budke + * + * 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_atxmega + * @ingroup cpu_atxmega_periph + * @{ + * + * @file + * @brief Low-level EBI (External BUS Interface) driver implementation + * + * @author Gerson Fernando Budke + * https://www.avrfreaks.net/forum/xmega-ebi-and-sram + * https://www.avrfreaks.net/forum/xmega-au-four-port-ebi + * https://community.atmel.com/forum/location-variable-specified-address + * @} + */ +#include + +#include "assert.h" +#include "periph_conf.h" +#include "cpu_pm.h" +#include "cpu_ebi.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +void ebi_init(void) __attribute__((naked, section(".init1"), used)); + +/** + * @brief Set up the I/O ports for use by the EBI. + * + * @note In SDRAM mode the \a sram_ale and \a lpc_ale parameters are ignored + * by the hardware. + */ +void ebi_init(void) +{ + EBI_CS_t *cs; + uint8_t mode; + uint8_t sd_ctrl = 0; + uint8_t i; + uint8_t expand_sram = 0; + + /* + * This is a mandatory configuration. Whowever, to complete disable module + * just configure it as: + * + * static const ebi_conf_t ebi_config = { 0 }; + * + * or, for a temporary disable, set addr_bits to 0 at periph_conf.h: + * + * .addr_bits = 0, + */ + if (ebi_config.addr_bits == 0) { + return; + } + + /* + * Set address and control lines as outputs, and active-low control lines + * initially high. + */ + if (ebi_config.flags & EBI_PORT_SDRAM) { + /* With SDRAM, the configuration is fairly fixed. */ + PORTH.OUT = 0x0f; + PORTH.DIR = 0xff; + PORTJ.DIR = 0xf0; + PORTK.DIR = 0xff; + } else { + uint8_t ale_mask = ebi_config.sram_ale | ebi_config.lpc_ale; + uint8_t port_mask; + + /* + * Set PORTH initial state, set WE and CAS/RE high by default. + * Set chip selects high by default if enabled. + */ + port_mask = 0x03; + if (ebi_config.flags & EBI_PORT_CS0) { + port_mask |= 0x10; + } + if (ebi_config.flags & EBI_PORT_CS1) { + port_mask |= 0x20; + } + if (ebi_config.flags & EBI_PORT_CS2) { + port_mask |= 0x40; + } + if (ebi_config.flags & EBI_PORT_CS3) { + port_mask |= 0x80; + } + PORTH.OUT = port_mask; + + /* + * Set PORTH direction, enable WE, CAS/RE and RAS/ALE1 to + * output by default. Set chip select direction if enabled. + */ + port_mask = 0x07; + + /* If two latches are in use, enable the ALE2 pin as well. */ + if (ale_mask & 0x02) { + port_mask |= 0x08; + } + if (ebi_config.flags & EBI_PORT_CS0 || ebi_config.addr_bits > 16) { + port_mask |= 0x10; + } + if (ebi_config.flags & EBI_PORT_CS1 || ebi_config.addr_bits > 17) { + port_mask |= 0x20; + } + if (ebi_config.flags & EBI_PORT_CS2 || ebi_config.addr_bits > 18) { + port_mask |= 0x40; + } + if (ebi_config.flags & EBI_PORT_CS3 || ebi_config.addr_bits > 19) { + port_mask |= 0x80; + } + PORTH.DIR = port_mask; + + /* + * PORTJ is always used for data, direction and value is controlled by + * the EBI module. + */ + + /* PORTK is only used in 3-port mode */ + if (ebi_config.flags & EBI_PORT_3PORT) { + port_mask = 0x00; + + if (ebi_config.flags & EBI_PORT_SRAM) { + /* + * Bits 0..7 go here, so if we have 8 lines or more, enable all + * lines. Otherwise, enable as many as we need. + */ + if (ebi_config.addr_bits < 8) { + port_mask = (1 << ebi_config.addr_bits) - 1; + } + else { + port_mask = 0xff; + } + } + else { + /* + * Bits 8..15 go here, so if we have less than 16 address lines, + * disable the ones that we don't need. If we have 8 lines or + * less, disable all address lines on this port. + */ + if (ebi_config.addr_bits <= 8) { + port_mask = 0x00; + } + else if (ebi_config.addr_bits < 16) { + port_mask = (1 << (ebi_config.addr_bits - 8)) - 1; + } + else { + port_mask = 0xff; + } + } + + PORTK.DIR = port_mask; + } + } + + if (ebi_config.flags & EBI_PORT_3PORT) { + mode = EBI_IFMODE_3PORT_gc; + } + else { + mode = EBI_IFMODE_2PORT_gc; + } + + if (ebi_config.sram_ale == 1) { + mode |= EBI_SRMODE_ALE1_gc; + } + else if (ebi_config.sram_ale == 2) { + mode |= EBI_SRMODE_ALE12_gc; + } + else { + mode |= EBI_SRMODE_NOALE_gc; + } + + if (ebi_config.lpc_ale > 0) { + mode |= (ebi_config.lpc_ale << EBI_LPCMODE_gp); + } + + if (ebi_config.sdram.cas_latency == EBI_SDRAM_CAS_LAT_3CLK) { + sd_ctrl |= EBI_SDCAS_bm; + } + + if (ebi_config.sdram.row_bits == EBI_SDRAM_ROW_BITS_12) { + sd_ctrl |= EBI_SDROW_bm; + } + + /* Enable EBI periph clock */ + PR.PRGEN &= ~PR_EBI_bm; + + /* 8-bit SDRAM requires 4-port EBI, which we don't have. */ + EBI.CTRL = EBI_SDDATAW_4BIT_gc + | mode; + EBI.SDRAMCTRLA = sd_ctrl + | ebi_config.sdram.column_bits; + EBI.SDRAMCTRLB = ebi_config.sdram.ld_mode_dly + | ebi_config.sdram.row_cycle_dly + | ebi_config.sdram.row_prechage_dly; + EBI.SDRAMCTRLC = ebi_config.sdram.write_recovery_dly + | ebi_config.sdram.exit_self_rfsh_dly + | ebi_config.sdram.row_to_column_dly; + EBI.REFRESH = ebi_config.sdram.refresh_period & 0x0FFF; + EBI.INITDLY = ebi_config.sdram.init_dly & 0x3FFF; + + /* IRQ are disabled here */ + cs = (EBI_CS_t *)&EBI.CS0; + for (i = 0; i < PERIPH_EBI_MAX_CS; i++) { + if (ebi_config.cs[i].mode != EBI_CS_MODE_DISABLED_gc && + ebi_config.cs[i].mode != EBI_CS_MODE_SDRAM_gc) { + + /* Configure */ + cs[i].CTRLA = ebi_config.cs[i].space; + cs[i].CTRLB = ebi_config.cs[i].wait; + cs[i].BASEADDR = ((ebi_config.cs[i].address >> 8) & 0xfff0); + + /* Enable */ + cs[i].CTRLA = ebi_config.cs[i].space | ebi_config.cs[i].mode; + + if (ebi_config.cs[i].address == 0) { + expand_sram = 1; + } + } + } + + /** + * Only CS[3] can be configured as SDRAM. + * CS structure is little bit different too. + */ + if (ebi_config.cs[3].mode == EBI_CS_MODE_SDRAM_gc) { + cs[3].CTRLA = ebi_config.cs[3].space; + cs[3].CTRLB = ebi_config.sdram.mode + | (ebi_config.sdram.refresh ? EBI_CS_SDSREN_bm : 0); + cs[3].BASEADDR = ((ebi_config.cs[3].address >> 8) & 0xfff0); + + cs[3].CTRLA = ebi_config.cs[3].space | ebi_config.cs[3].mode; + + if (ebi_config.cs[3].address == 0) { + expand_sram = 1; + } + + while (!(cs[3].CTRLB & EBI_CS_SDINITDONE_bm)) {}; + } + + if (expand_sram > 0) { + /** + * @brief Set new Stack Pointer + */ + __asm__ volatile ( + "out __SP_L__, %A[stack] \n\t" + "out __SP_H__, %B[stack] \n\t" + : /* no output */ + : [stack] "r"(RAM_LEN) + : "memory" + ); + }; +} + +uint16_t hugemem_read16(const hugemem_ptr_t from) +{ + uint16_t value; + + __asm__ volatile ( + "movw r30, %A[from] \n\t" + "out %[rampz], %C[from] \n\t" + "ld %A[dest], Z+ \n\t" + "ld %B[dest], Z \n\t" + "out %[rampz], __zero_reg__ \n\t" + : [dest] "=r"(value) + : [from] "r"(from), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); + + return value; +} + +void hugemem_write16(hugemem_ptr_t to, uint16_t val) +{ + __asm__ volatile ( + "movw r30, %A[to] \n\t" + "out %[rampz], %C[to] \n\t" + "st Z+, %A[val] \n\t" + "st Z, %B[val] \n\t" + "out %[rampz], __zero_reg__ \n\t" + : /* no output */ + : [to] "r"(to), + [val] "r"(val), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); +} + +uint32_t hugemem_read32(const hugemem_ptr_t from) +{ + uint32_t value; + + __asm__ volatile ( + "movw r30, %A[from] \n\t" + "out %[rampz], %C[from] \n\t" + "ld %A[dest], Z+ \n\t" + "ld %B[dest], Z+ \n\t" + "ld %C[dest], Z+ \n\t" + "ld %D[dest], Z \n\t" + "out %[rampz], __zero_reg__ \n\t" + : [dest] "=r"(value) + : [from] "r"(from), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); + + return value; +} + +void hugemem_write32(hugemem_ptr_t to, uint32_t val) +{ + __asm__ volatile ( + "movw r30, %A[to] \n\t" + "out %[rampz], %C[to] \n\t" + "st Z+, %A[val] \n\t" + "st Z+, %B[val] \n\t" + "st Z+, %C[val] \n\t" + "st Z, %D[val] \n\t" + "out %[rampz], __zero_reg__ \n\t" + : /* no output */ + : [to] "r"(to), + [val] "r"(val), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); +} + +void hugemem_read_block(void *to, const hugemem_ptr_t from, size_t size) +{ + if (size > 0) { + __asm__ volatile ( + "movw r30, %A[from] \n\t" + "out %[rampz], %C[from] \n\t" + "get_%=: \n\t" + "ld __tmp_reg__, Z+ \n\t" + "st X+, __tmp_reg__ \n\t" + "sbiw %A[size], 1 \n\t" + "brne get_%= \n\t" + "out %[rampz], __zero_reg__ \n\t" + : [to] "+x"(to), + [size] "+w"(size) + : [from] "r"(from), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); + } +} + +void hugemem_write_block(hugemem_ptr_t to, const void *from, size_t size) +{ + if (size > 0) { + __asm__ volatile ( + "movw r30, %A[from] \n\t" + "out %[rampz], %C[from] \n\t" + "put_%=: \n\t" + "ld __tmp_reg__, X+ \n\t" + "st Z+, __tmp_reg__ \n\t" + "sbiw %A[size], 1 \n\t" + "brne put_%= \n\t" + "out %[rampz], __zero_reg__ \n\t" + : [from] "+x"(from), + [size] "+w"(size) + : [to] "r"(to), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); + } +} diff --git a/cpu/atxmega/include/cpu_ebi.h b/cpu/atxmega/include/cpu_ebi.h new file mode 100644 index 0000000000..159df70dc3 --- /dev/null +++ b/cpu/atxmega/include/cpu_ebi.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2021 Gerson Fernando Budke + * + * 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_atxmega + * @{ + * + * @file + * @brief External Bus Interface API + * + * @author Gerson Fernando Budke + */ + +#include "periph_cpu.h" + +#ifndef CPU_EBI_H +#define CPU_EBI_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t hugemem_ptr_t; + +#define HUGEMEM_NULL 0 + +void ebi_init(void); + +/** + * @brief Load byte register from external memory pointer + * + * @param from 24-bit external memory address + * @return uint8_t 8-bit value + */ +static inline uint8_t hugemem_read8(const hugemem_ptr_t from) +{ + uint8_t value; + + __asm__ volatile ( + /* place 24 bit address into r30, r31, RAMPZ + * r31:r30 <- %B[from]:%A[from] + * RAMPZ <- %C[from] + */ + "movw r30, %A[from] \n\t" + "out %[rampz], %C[from] \n\t" + /* read byte from 24 bit address + * register <- *(RAMPZ + Z) + */ + "ld %[dest], Z \n\t" + /* clear ramp */ + "out %[rampz], __zero_reg__ \n\t" + : [dest] "=r"(value) + : [from] "r"(from), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); + + return value; +} + +/** + * @brief Store byte register to external memory pointer + * + * @param to 24-bit external memory address + * @param val 8-bit value + */ +static inline void hugemem_write8(hugemem_ptr_t to, uint8_t val) +{ + __asm__ volatile ( + /* place 24 bit address into r30, r31, RAMPZ + * r31:r30 <- %B[from]:%A[from] + * RAMPZ <- %C[from] + */ + "movw r30, %A[to] \n\t" + "out %[rampz], %C[to] \n\t" + /* write byte to 24 bit address + * (RAMPZ + Z) <- register + */ + "st Z, %[val] \n\t" + /* clear ramp */ + "out %[rampz], __zero_reg__ \n\t" + : + : [to] "r"(to), + [val] "r"(val), + [rampz] "i"(&RAMPZ) + : "r30", "r31" + ); +} + +/** + * @brief Load register pair from external memory pointer + * + * @param from 24-bit external memory address + * @return uint16_t 8-bit value + */ +uint16_t hugemem_read16(const hugemem_ptr_t from); + +/** + * @brief Store register pair to external memory pointer + * + * @param to 24-bit external memory address + * @param val 16-bit value + */ +void hugemem_write16(hugemem_ptr_t to, uint16_t val); + +/** + * @brief Load two register pair from external memory pointer + * + * @param from 24-bit external memory address + * @return uint8_t 32-bit value + */ +uint32_t hugemem_read32(const hugemem_ptr_t from); + +/** + * @brief Store two register pair to external memory pointer + * + * @param to 24-bit external memory address + * @param val 32-bit value + */ +void hugemem_write32(hugemem_ptr_t to, uint32_t val); + +/** + * @brief Read byte stream from external memory to internal memory + * + * @param to intenal memory pointer + * @param from 24-bit external memory pointer + * @param size number of bytes to read + * + * @note The address range to copy from is within 64 kB boundary + */ +void hugemem_read_block(void *to, const hugemem_ptr_t from, size_t size); + +/** + * @brief Write byte stream from internal memory to external memory + * + * @param to 24-bit external memory pointer + * @param from intenal memory pointer + * @param size number of bytes to write + * + * @note The address range to copy to is within 64 kB boundary + */ +void hugemem_write_block(hugemem_ptr_t to, const void *from, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_EBI_H */ +/** @} */ diff --git a/cpu/atxmega/include/periph_cpu.h b/cpu/atxmega/include/periph_cpu.h index 901a2326f1..73f7d88954 100644 --- a/cpu/atxmega/include/periph_cpu.h +++ b/cpu/atxmega/include/periph_cpu.h @@ -354,6 +354,236 @@ typedef enum { } spi_clk_t; /** @} */ +#if defined(__AVR_ATxmega64A1__) || \ + defined(__AVR_ATxmega128A1__) || \ + defined(__AVR_ATxmega64A1U__) || \ + defined(__AVR_ATxmega128A1U__) || \ + defined(Doxygen) +/** + * @brief EBI (External Bus Interface) + * {@ + */ + +/** + * @brief EBI Low Pin Count (LPC) Mode Address Latch Enable (ALE) config + */ +typedef enum { + EBI_LPC_MODE_ALE1 = 0x01, /**< Data multiplexed with Address byte 0 */ + EBI_LPC_MODE_ALE12 = 0x03, /**< Data multiplexed with Address byte 0 and 1 */ +} ebi_lpc_mode_t; + +/** + * @brief EBI Port Access Flags + * + * Indicate what should be configured + */ +typedef enum { + EBI_PORT_3PORT = 0x01, /**< Three Port Config */ + EBI_PORT_SDRAM = 0x02, /**< SDRAM Port Config */ + EBI_PORT_SRAM = 0x04, /**< SRAM Port Config */ + EBI_PORT_LPC = 0x08, /**< Low Pin Count Port Config */ + EBI_PORT_CS0 = 0x10, /**< Chip Select 0 Config */ + EBI_PORT_CS1 = 0x20, /**< Chip Select 1 Config */ + EBI_PORT_CS2 = 0x40, /**< Chip Select 2 Config */ + EBI_PORT_CS3 = 0x80, /**< Chip Select 3 Config */ + EBI_PORT_CS_ALL = 0xF0, /**< Chip Select 0-3 Config */ +} ebi_port_mask_t; + +/** + * @brief SDRAM Column Address Strobe latency + */ +typedef enum { + EBI_SDRAM_CAS_LAT_2CLK = 0x00, /**< 2 Clk PER2 cycles delay */ + EBI_SDRAM_CAS_LAT_3CLK = 0x01, /**< 3 Clk PER2 cycles delay */ +} ebi_sdram_cas_latency_t; + +/** + * @brief SDRAM number of Row Bits + */ +typedef enum { + EBI_SDRAM_ROW_BITS_11 = 0x00, /**< 11 row bits */ + EBI_SDRAM_ROW_BITS_12 = 0x01, /**< 12 row bits */ +} ebi_sdram_row_bits_t; + +/** + * @brief EBI maximum Chip Select + */ +#define PERIPH_EBI_MAX_CS (4) + +/** + * @brief EBI SDRAM Chip Select + */ +#define PERIPH_EBI_SDRAM_CS (3) + +/** + * @brief EBI Chip Select configuration structure + */ +typedef struct { + EBI_CS_MODE_t mode; /**< Chip Select address mode */ +#if defined (__AVR_ATxmega64A1U__) || defined (__AVR_ATxmega128A1U__) + EBI_CS_ASPACE_t space; /**< Chip Select address space */ +#else + EBI_CS_ASIZE_t space; /**< Chip Select address space */ +#endif + EBI_CS_SRWS_t wait; /**< SRAM Wait State Selection */ + uint32_t address; /**< Chip Select Base Address */ +} ebi_cs_t; + +/** + * @brief EBI SDRAM configuration structure + */ +typedef struct { + uint8_t refresh; /**< Self-Refresh Enabled */ + uint16_t refresh_period; /**< microseconds */ + uint16_t init_dly; /**< microseconds */ + EBI_CS_SDMODE_t mode; /**< Access Mode */ + ebi_sdram_cas_latency_t cas_latency; /**< CAS Latency */ + ebi_sdram_row_bits_t row_bits; /**< ROW bits */ + EBI_SDCOL_t column_bits; /**< COLUMN bits */ + EBI_MRDLY_t ld_mode_dly; /**< Number of Clk PER2 cycles */ + EBI_ROWCYCDLY_t row_cycle_dly; /**< Number of Clk PER2 cycles */ + EBI_RPDLY_t row_prechage_dly; /**< Number of Clk PER2 cycles */ + EBI_WRDLY_t write_recovery_dly; /**< Number of Clk PER2 cycles */ + EBI_ESRDLY_t exit_self_rfsh_dly; /**< Number of Clk PER2 cycles */ + EBI_ROWCOLDLY_t row_to_column_dly; /**< Number of Clk PER2 cycles */ +} ebi_sd_t; + +/** + * @brief EBI configuration structure + * + * The ebi_conf_t define the whole external memory that ATxmega A1 can address. + * It is recommended read all EBI chapter from XMEGA-AU manual. + * + * The external address space can be used to address external peripherals and + * expand SRAM. The ebi driver provide methods to read/write in external + * address space. The SRAM can be expanded up to 64k when one chip select + * address have value equal to zero. To allow expand external RAM both + * @b RAM_LEN and @b EXP_RAM variables should be override at board + * makefile.include file. + * + * When expanding RAM for use with RIOT-OS, the memory address must be aligned + * at external memory size boundary, respecting power of 2. In this case, to + * add 32K memory on the system, the chip select address should be set to 0, + * or 32k, or 64 etc. This means that when the board have external memory and + * user wants to map part of that memory to expand RAM, both internal and + * external memories must have same start address @b A, inclusive, see image + * for details. This is necessary to make sure RIOT-OS have a contiguous address + * space. The drawback is that first @b Xk external memory will be lost. + * Assuming internal top address memory is @b B and external top address memory + * is @b C. The XMEGA will automatically access internal memory <= B + * when address collide with external memory. At address >= B + 1, XMEGA + * will access memory thru EBI. Also, note that @b RAM_LEN must be a power + * of 2, so it can't e.g. be 48k. + * + * C ------ + * | | + * | | + * | | + * B -----| | + * | | | + * | | | + * A ----------- + * + * @note To avoid parser problems, @b RAM_LEN must be defined as decimal value. + * + * Example: Add 256K of external RAM + * + * The max addressable RAM by SP is 64K due to limit of 16 bits. In this case, + * RAM will be 64K. The remaining RAM can be addressed only by ebi_mem methods + * and GCC doesn't see it. + * + * At board/periph_conf.h: + * + * static const ebi_conf_t ebi_config = { + * ... + * .cs = { + * { EBI_CS_MODE_DISABLED_gc, + * 0, + * EBI_CS_SRWS_0CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_DISABLED_gc, + * 0, + * EBI_CS_SRWS_0CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_LPC_gc, + * EBI_CS_ASPACE_256KB_gc, + * EBI_CS_SRWS_1CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_DISABLED_gc, + * 0, + * EBI_CS_SRWS_0CLK_gc, + * 0x0UL, + * }, + * }, + * }; + * + * At board/Makefile.include: + * override RAM_LEN = 65536 + * override EXP_RAM = 1 + * + * Example: Add 32K of external RAM and a LCD + * + * At board/periph_conf.h: + * + * static const ebi_conf_t ebi_config = { + * ... + * .cs = { + * { EBI_CS_MODE_DISABLED_gc, + * 0, + * EBI_CS_SRWS_0CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_DISABLED_gc, + * 0, + * EBI_CS_SRWS_0CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_LPC_gc, + * EBI_CS_ASPACE_32KB_gc, + * EBI_CS_SRWS_1CLK_gc, + * 0x0UL, + * }, + * { EBI_CS_MODE_LPC_gc, + * EBI_CS_ASPACE_256B_gc, + * EBI_CS_SRWS_5CLK_gc, + * 0x100000UL, + * }, + * }, + * }; + * + * At board/Makefile.include: + * override RAM_LEN = 32768 + * override EXP_RAM = 1 + * + * This data structure a mandatory configuration for A1 variation. If no + * external memory is used the module can be disabled defining data struct + * with all zeros, as below: + * + * static const ebi_conf_t ebi_config = { 0 }; + * + * or, for a temporary disable, set addr_bits to 0 at periph_conf.h: + * + * static const ebi_conf_t ebi_config = { + * .addr_bits = 0, + * ... + * }; + * + */ +typedef struct { + uint8_t addr_bits; /**< EBI port address lines */ + ebi_port_mask_t flags; /**< EBI port flags */ + uint8_t sram_ale; /**< Number of ALE for SRAM mode */ + uint8_t lpc_ale; /**< Number of ALE for LPC mode */ + ebi_sd_t sdram; /**< SDRAM configuration */ + ebi_cs_t cs[PERIPH_EBI_MAX_CS]; /**< Chip Select configuration */ +} ebi_conf_t; +/** @} */ + +#endif /* __AVR_ATxmegaxxxA1x__ */ + #ifdef __cplusplus } #endif diff --git a/cpu/atxmega/periph/pm.c b/cpu/atxmega/periph/pm.c index 2b2d75a4b8..106fc5b8c5 100644 --- a/cpu/atxmega/periph/pm.c +++ b/cpu/atxmega/periph/pm.c @@ -135,4 +135,14 @@ void pm_periph_power_off(void) for (i = 0; i <= 7; i++) { reg[i] = 0xff; } + + /* EBI Must be always enabled when configured */ +#if defined (__AVR_ATxmega64A1__) || \ + defined (__AVR_ATxmega64A1U__) || \ + defined (__AVR_ATxmega128A1__) || \ + defined (__AVR_ATxmega128A1U__) + if (ebi_config.addr_bits > 0) { + reg[0] &= ~PR_EBI_bm; + } +#endif } diff --git a/makefiles/arch/avr8.inc.mk b/makefiles/arch/avr8.inc.mk index 0869224145..81acfbd1f8 100644 --- a/makefiles/arch/avr8.inc.mk +++ b/makefiles/arch/avr8.inc.mk @@ -27,6 +27,7 @@ $(if $(ROM_LEN),,$(error ROM_LEN is not defined)) $(if $(RAM_LEN),,$(error RAM_LEN is not defined)) LINKFLAGS += $(LINKFLAGPREFIX)--defsym=__TEXT_REGION_LENGTH__=$(ROM_LEN)$(if $(ROM_RESERVED),-$(ROM_RESERVED)) LINKFLAGS += $(LINKFLAGPREFIX)--defsym=__DATA_REGION_LENGTH__=$(RAM_LEN) +LINKFLAGS += $(LDSCRIPT_EXTRA) ifeq ($(LTO),1) # avr-gcc <4.8.3 has a bug when using LTO which causes a warning to be printed always: