1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/atxmega/ebi/ebi.c
Gerson Fernando Budke 307e8c7a17 cpu/atxmega: Add external bus interface
Introduce XMEGA EBI driver.  This enable EBI for use with all memory
supported by the device and peripherals. It include support to  SRAM,
SDRAM, LCDs or any other external bus access.

Note: This feature only works for A1/A1U series, which are, the series
with EBI hardware.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
2021-08-24 08:35:41 -03:00

376 lines
11 KiB
C

/*
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
*
* 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 <nandojve@gmail.com>
* 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 <avr/io.h>
#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"
);
}
}