Skip to main content

Basic Usage of the External EIM Bus on Colibri iMX6

This article wants to demonstrate the basic register setups to be able to access the external system bus (EIM) of the Colibri iMX6.

  • The EIM will be setup to operate in multiplexed, asynchronous mode
  • You will need to adjust timing parameters and other configurations, in order to match your requirements.
    Please refer to the iMX6 reference manual for details about the register settings.

/// @file iMX6_EIM_Demo.c
/// @copyright Copyright (c) 2017 Toradex AG
/// @brief Basic demonstration how how to use the EIM bus of the
/// Colibri iM6. \n
/// The Bus is configured as follows:
/// * Multiplexed Mode
/// * 8 combined Address / Data Signals
/// * use Chip select nCS0 (SODIMM 105)
/// Note: Stepping through the code with the debugger might
/// freeze the system, because the debugger tries to read
/// large address ranges around the mapped registers.
///
/// @test Tested on: Colibri iMX6DL
/// Windows Embedded Compact 7 (V1.3b4)
/// Visual Studio 2008
/// Toradex CE Libraries V2.0

#include <windows.h>
#include "mapmem.h"
#include "clk_imx6.h"
#include "gpio_imx6.h"

#define CCM_BASE_ADDRESS 0x020C4000
#define EIM_BASE_ADDRESS 0x021B8000
#define EIMBUS_BASE_ADDRESS 0x08000000

// iMX6 CCM
typedef struct
{
DWORD CCM_CCR; // CCM Control Register
DWORD CCM_CCDR; // CCM Control Divider Register
DWORD CCM_CSR; // CCM Status Register
DWORD CCM_CCSR; // CCM Clock Switcher Register
DWORD CCM_CACRR; // CCM Arm Clock Root Register
DWORD CCM_CBCDR; // CCM Bus Clock Divider Register
DWORD CCM_CBCMR; // CCM Bus Clock Multiplexer Register
DWORD CCM_CSCMR1; // CCM Serial Clock Multiplexer Register 1
DWORD CCM_CSCMR2; // CCM Serial Clock Multiplexer Register 2
DWORD CCM_CSCDR1; // CCM Serial Clock Divider Register 1
DWORD CCM_CS1CDR; // CCM SSI1 Clock Divider Register
DWORD CCM_CS2CDR; // CCM SSI2 Clock Divider Register
DWORD CCM_CDCDR; // CCM D1 Clock Divider Register
DWORD CCM_CHSCCDR; // CCM HSC Clock Divider Register
DWORD CCM_CSCDR2; // CCM Serial Clock Divider Register 2
DWORD CCM_CSCDR3; // CCM Serial Clock Divider Register 3
DWORD rsvd_020c4040;
DWORD rsvd_020c4044;
DWORD CCM_CDHIPR; // CCM Divider Handshake In-Process Register
DWORD rsvd_020c404c;
DWORD rsvd_020c4050;
DWORD CCM_CLPCR; // CCM Low Power Control Register
DWORD CCM_CISR; // CCM Interrupt Status Register
DWORD CCM_CIMR; // CCM Interrupt Mask Register
DWORD CCM_CCOSR; // CCM Clock Output Source Register
DWORD CCM_CGPR; // CCM General Purpose Register
DWORD CCM_CCGR0; // CCM Clock Gating Register 0
DWORD CCM_CCGR1; // CCM Clock Gating Register 1
DWORD CCM_CCGR2; // CCM Clock Gating Register 2
DWORD CCM_CCGR3; // CCM Clock Gating Register 3
DWORD CCM_CCGR4; // CCM Clock Gating Register 4
DWORD CCM_CCGR5; // CCM Clock Gating Register 5
DWORD CCM_CCGR6; // CCM Clock Gating Register 6
DWORD rsvd_020c4084;
DWORD CCM_CMEOR; // CCM Module Enable Overide Register
} CCM_REGS, *ptCCM_REGS;


// iMX6 EIM
typedef struct
{
DWORD EIM_CS0GCR1; // 021B_8000 Chip Select n General Configuration Register 1
DWORD EIM_CS0GCR2; // 021B_8004 Chip Select n General Configuration Register 2
DWORD EIM_CS0RCR1; // 021B_8008 Chip Select n Read Configuration Register 1
DWORD EIM_CS0RCR2; // 021B_800c Chip Select n Read Configuration Register 2
DWORD EIM_CS0WCR1; // 021B_8010 Chip Select n Write Configuration Register 1
DWORD EIM_CS0WCR2; // 021B_8010 Chip Select n Write Configuration Register 2
DWORD EIM_CS1GCR1; // 021B_8018 Chip Select n General Configuration Register 1
DWORD EIM_CS1GCR2; // 021B_801c Chip Select n General Configuration Register 2
DWORD EIM_CS1RCR1; // 021B_8020 Chip Select n Read Configuration Register 1
DWORD EIM_CS1RCR2; // 021B_8020 Chip Select n Read Configuration Register 2
DWORD EIM_CS1WCR1; // 021B_8028 Chip Select n Write Configuration Register 1
DWORD EIM_CS1WCR2; // 021B_802c Chip Select n Write Configuration Register 2
DWORD EIM_CS2GCR1; // 021B_8030 Chip Select n General Configuration Register 1
DWORD EIM_CS2GCR2; // 021B_8030 Chip Select n General Configuration Register 2
DWORD EIM_CS2RCR1; // 021B_8038 Chip Select n Read Configuration Register 1
DWORD EIM_CS2RCR2; // 021B_803c Chip Select n Read Configuration Register 2
DWORD EIM_CS2WCR1; // 021B_8040 Chip Select n Write Configuration Register 1
DWORD EIM_CS2WCR2; // 021B_8040 Chip Select n Write Configuration Register 2
DWORD EIM_CS3GCR1; // 021B_8048 Chip Select n General Configuration Register 1
DWORD EIM_CS3GCR2; // 021B_804c Chip Select n General Configuration Register 2
DWORD EIM_CS3RCR1; // 021B_8050 Chip Select n Read Configuration Register 1
DWORD EIM_CS3RCR2; // 021B_8050 Chip Select n Read Configuration Register 2
DWORD EIM_CS3WCR1; // 021B_8058 Chip Select n Write Configuration Register 1
DWORD EIM_CS3WCR2; // 021B_805c Chip Select n Write Configuration Register 2
DWORD EIM_WCR; // 021B_8090 EIM Configuration Register
DWORD EIM_WIAR; // 021B_8094 EIM IP Access Register
DWORD EIM_EAR; // 021B_8098 Error Address Register
} EIM_REGS, *ptEIM_REGS;

//-----------------------------------------------------------------------------
// Configure the IOs for the iMX6 EIM bus
/// @param[in] hGpio handle received from Gpio_Init()
/// @retval TRUE Success
/// @retval FALSE Failure
BOOL Eim_ConfigureIos(HANDLE hGpio)
{
int i;
BOOL fSuccess = TRUE;

const uIo eimIo[] =
{
COLIBRI_PIN( 89), // RW
COLIBRI_PIN( 91), // OE
COLIBRI_PIN(105), // CS0_B
COLIBRI_PIN(111), // AD0
COLIBRI_PIN(113), // AD1
COLIBRI_PIN(115), // AD2
COLIBRI_PIN(117), // AD3
COLIBRI_PIN(119), // AD4
COLIBRI_PIN(121), // AD5
COLIBRI_PIN(123), // AD6
COLIBRI_PIN(125) // AD7
};

/// The EIM functionality is on Alternate Function 0 for all EIM pins.
/// (this is different for the iMX7)
for (i = 0; i < _countof(eimIo); i++)
fSuccess &= Imx6Gpio_SetConfigString(hGpio, eimIo[i], NULL, L"AltFn=0", StoreVolatile);

return fSuccess;
}

//*****************************************************************************
/// Main function
/// @param[in] argc number of command line arguments
/// @param[in] argv array containing command line arguments
/// @retval 1 Success
/// @retval 0 Failure
int wmain(int argc, _TCHAR* argv[])
{
BOOL fSuccess = TRUE;
HANDLE hMap;
HANDLE hGpio;

volatile ptEIM_REGS pEimReg;
volatile ptCCM_REGS clkRegs;
volatile DWORD tmp;
volatile BYTE *extBus;
int i;

hMap = Map_Init();

// === Enable EIM Clock
// ACLK_EIM_SLOW_SEL = 00b (derive clock from AXI)
// ACLK_EIM_SLOW_PODF = 111b (divide by 8)
clkRegs = (ptCCM_REGS)Map_MapMemory(CCM_BASE_ADDRESS, 0x1000);
clkRegs->CCM_CSCMR1 = (((clkRegs->CCM_CSCMR1 & ~0x60000000) | 0x03800000) ^ 0x00600000);
// CG5 = 11b (clock always on)
clkRegs->CCM_CCGR6 |= 0x00000c00;

// === Configure EIM Bus mode
pEimReg = (ptEIM_REGS)Map_MapMemory(EIM_BASE_ADDRESS, 0x1000);
pEimReg->EIM_CS0GCR1 = 0x0064a089; // Bit 31-28 8 words page size (for sync. read/write)
// Bit 27 write allowed
// Bit 26-24 Gap between chip selects = 0 (for async.)
// Bit 23 Address shifted according to port size
// Bit 22-20 CS pulse width >= 6 EIM clocks
// Bit 19 User Mode access allowed
// Bit 18-16 8 bit data port resides on DATA[7:0]
// Bit 15-14 Burst Clock Start 0 delay
// Bit 13-12 Burst Clock divisor = 1
// Bit 10-8 Burst Length = 4
// Bit 7-6 CRE signal disabled
// Bit 5 READ monitors WAIT signal
// Bit 4 WRITE monitors WAIT signal
// Bit 3 Multiplexed mode
// Bit 2 Async. mode for WRITE
// Bit 1 Async. Mode for READ
// Bit 0 CS enabled

pEimReg->EIM_CS0GCR2 = 0x00001002; // Bit 12 ignore GRANT signal
// Bit 8 DTACK disable
// Bit 1-0 Address Hold time = 2 cycles

// further timings can be set in the following registers:
//pEimReg->EIM_CS0RCR1 = 0x00002000;
//pEimReg->EIM_CS0RCR2 = 0x00000000;
//pEimReg->EIM_CS0WCR1 = 0x00000400;
//pEimReg->EIM_CS0WCR2 = 0x00000000;


// === configure EIM GPIOs
hGpio = Imx6Gpio_Init(NULL);
fSuccess &= Eim_ConfigureIos(hGpio);
fSuccess &= Imx6Gpio_Deinit(hGpio);

// Access memory at CS0 address.
// The address range for nCS0 is 0x08000000 to 0x0ffeffff (127MB).
// However, we map only 64kB here.
extBus = Map_MapMemory(EIMBUS_BASE_ADDRESS, 0x10000);

// 16 x Write
for (i = 0; i <= 15; i++)
*(extBus + i) = 0x00; ///< write

// 16 x Read
for (i = 0; i <= 15; i++)
tmp = *(extBus + i); ///< read

fSuccess &= Map_Deinit(hMap);

return fSuccess;
}

Send Feedback!