Search by Tags

Basic Usage of the External EIM Bus on Colibri iMX6

 

Article updated at 05 Mar 2018

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;
}