aboutsummaryrefslogblamecommitdiff
path: root/sw/test-adder/novena-eim.c
blob: 2908f5d68912673302db9365cf63ee2de8913b77 (plain) (tree)











































                                                                                
                   
                     
 





                                                                                
                                    



















                                                                                

              






                                                  


                                                              

                  
                                 











































































                                                                                                                   





















                                                                          

                                                





















                                                                          










                                                                                
                                                   





















                                                         
                                                    



















                                                                                





                                                
 


                                                


























































































                                                                                   








                                                  



                                                                                
                                                 


                                                                                
                                                         

                           
                                          



                                                                                
                                                


                                                                                
                                                         

                            
                                          























































                                                                                
/*
 * novena-eim.c
 * ------------
 * This module contains the user interface to the Novena EIM bus.
 *
 * Author: Pavel Shatov
 * Copyright (c) 2015, NORDUnet A/S All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>

#include "novena-eim.h"


//------------------------------------------------------------------------------
// Variables
//------------------------------------------------------------------------------
static size_t   mem_page_size   = 0;
static int      mem_dev_fd      = -1;
static void *   mem_map_ptr     = MAP_FAILED;
static off_t    mem_base_addr   = 0;


//------------------------------------------------------------------------------
// Prototypes
//------------------------------------------------------------------------------
static void  _eim_setup_iomuxc  (void);
static void  _eim_setup_ccm     (void);
static void  _eim_setup_eim     (void);
static void  _eim_cleanup       (void);
static off_t _eim_calc_offset   (off_t);
static void  _eim_remap_mem     (off_t);


//------------------------------------------------------------------------------
int eim_setup(void)
//------------------------------------------------------------------------------
{
    long size;

    // register cleanup function
    if (atexit(_eim_cleanup) != 0) {
        printf("ERROR: atexit() failed.\n");
        return -1;
    }

    // determine memory page size to use in mmap()
    size = sysconf(_SC_PAGESIZE);
    if (size < 1) {
        printf("ERROR: sysconf(_SC_PAGESIZE) == %ld\n", size);
        return -1;
    }
    mem_page_size = (size_t)size;

    // try to open memory device
    mem_dev_fd = open(MEMORY_DEVICE, O_RDWR | O_SYNC);
    if (mem_dev_fd == -1) {
        printf("ERROR: open(%s) failed.\n", MEMORY_DEVICE);
        return -1;
    }

    /* Several blocks in the CPU have common pins, we can use I/O MUX
     * Controller to configure what block will actually use I/O pins. We wait
     * EIM module to be able to communicate with the on-board FPGA. Let's
     * configure IOMUXC accordingly.
     */
    _eim_setup_iomuxc();

    /* We need to enable clocking of EIM block in order to be able to use it.
     * Let's configure Clock Controller Module accordingly.
     */
    _eim_setup_ccm();

    /* We need to properly configure EIM mode and all the corresponding
     * parameters. That's a lot of code, let's do it now.
     */
    _eim_setup_eim();

    // done
    return 1;
}


//------------------------------------------------------------------------------
static void _eim_cleanup(void)
//------------------------------------------------------------------------------
{
    // unmap memory if needed
    if (mem_map_ptr != MAP_FAILED) {
        if (munmap(mem_map_ptr, mem_page_size) != 0)
            printf("WARNING: munmap() failed.\n");
    }

    // close memory device if needed
    if (mem_dev_fd != -1) {
        if (close(mem_dev_fd) != 0)
            printf("WARNING: close() failed.\n");
    }
}


//------------------------------------------------------------------------------
static void _eim_setup_iomuxc(void)
//------------------------------------------------------------------------------
{
    // create structures
    struct IOMUXC_SW_MUX_CTL_PAD_EIM    reg_mux;                // mux control register
    struct IOMUXC_SW_PAD_CTL_PAD_EIM    reg_pad;                // pad control register

    // setup mux control register
    reg_mux.mux_mode        = IOMUXC_MUX_MODE_ALT0;             // ALT0 mode must be used for EIM
    reg_mux.sion            = 0;                                // forced input not needed
    reg_mux.reserved_3      = 0;                                // must be 0
    reg_mux.reserved_31_5   = 0;                                // must be 0

    // setup pad control register
    reg_pad.sre             = IOMUXC_PAD_CTL_SRE_FAST;          // fast slew rate
    reg_pad.dse             = IOMUXC_PAD_CTL_DSE_33_OHM;        // highest drive strength
    reg_pad.speed           = IOMUXC_PAD_CTL_SPEED_MEDIUM_10;   // medium speed
    reg_pad.ode             = IOMUXC_PAD_CTL_ODE_DISABLED;      // open drain not needed
    reg_pad.pke             = IOMUXC_PAD_CTL_PKE_DISABLED;      // neither pull nor keeper are needed
    reg_pad.pue             = IOMUXC_PAD_CTL_PUE_PULL;          // doesn't matter actually, because PKE is disabled
    reg_pad.pus             = IOMUXC_PAD_CTL_PUS_100K_OHM_PU;   // doesn't matter actually, because PKE is disabled
    reg_pad.hys             = IOMUXC_PAD_CTL_HYS_DISABLED;      // use CMOS, not Schmitt trigger input
    reg_pad.reserved_2_1    = 0;                                // must be 0
    reg_pad.reserved_10_8   = 0;                                // must be 0
    reg_pad.reserved_31_17  = 0;                                // must be 0

    // all the pins must be configured to use the same ALT0 mode
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_CS0_B,   (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_OE_B,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_RW,      (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_LBA_B,   (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD00,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD01,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD02,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD03,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD04,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD05,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD06,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD07,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD08,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD09,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD10,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD11,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD12,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD13,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD14,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_AD15,    (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_WAIT_B,  (uint32_t *)&reg_mux);
    eim_write_32(IOMUXC_SW_MUX_CTL_PAD_EIM_BCLK,    (uint32_t *)&reg_mux);

    // we need to configure all the I/O pads too
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_CS0_B,   (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_OE_B,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_RW,      (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_LBA_B,   (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD00,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD01,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD02,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD03,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD04,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD05,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD06,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD07,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD08,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD09,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD10,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD11,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD12,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD13,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD14,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_AD15,    (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_WAIT_B,  (uint32_t *)&reg_pad);
    eim_write_32(IOMUXC_SW_PAD_CTL_PAD_EIM_BCLK,    (uint32_t *)&reg_pad);
}


//------------------------------------------------------------------------------
static void _eim_setup_ccm(void)
//------------------------------------------------------------------------------
{
    // create structure
    struct CCM_CCGR6 ccm_ccgr6;

    // read register
    eim_read_32(CCM_CCGR6, (uint32_t *)&ccm_ccgr6);

    // modify register
    ccm_ccgr6.cg0_usboh3        = CCM_CGR_ON_EXCEPT_STOP;
    ccm_ccgr6.cg1_usdhc1        = CCM_CGR_OFF;
    ccm_ccgr6.cg2_usdhc2        = CCM_CGR_ON_EXCEPT_STOP;
    ccm_ccgr6.cg3_usdhc3        = CCM_CGR_ON_EXCEPT_STOP;

    ccm_ccgr6.cg3_usdhc4        = CCM_CGR_OFF;
    ccm_ccgr6.cg5_eim_slow      = CCM_CGR_ON_EXCEPT_STOP;
    ccm_ccgr6.cg6_vdoaxiclk     = CCM_CGR_OFF;
    ccm_ccgr6.cg7_vpu           = CCM_CGR_OFF;

    ccm_ccgr6.cg8_reserved      = 0;
    ccm_ccgr6.cg9_reserved      = 0;
    ccm_ccgr6.cg10_reserved     = 0;
    ccm_ccgr6.cg11_reserved     = 0;
    ccm_ccgr6.cg12_reserved     = 0;
    ccm_ccgr6.cg13_reserved     = 0;
    ccm_ccgr6.cg14_reserved     = 0;
    ccm_ccgr6.cg15_reserved     = 0;

    // write register
    eim_write_32(CCM_CCGR6, (uint32_t *)&ccm_ccgr6);
}


//------------------------------------------------------------------------------
static void _eim_setup_eim(void)
//------------------------------------------------------------------------------
{
    // create structures
    struct EIM_CS_GCR1  gcr1;
    struct EIM_CS_GCR2  gcr2;
    struct EIM_CS_RCR1  rcr1;
    struct EIM_CS_RCR2  rcr2;
    struct EIM_CS_WCR1  wcr1;
    struct EIM_CS_WCR2  wcr2;

    struct EIM_WCR      wcr;
    struct EIM_WIAR     wiar;
    //struct EIM_EAR      ear;

    // read all the registers
    eim_read_32(EIM_CS0GCR1, (uint32_t *)&gcr1);
    eim_read_32(EIM_CS0GCR2, (uint32_t *)&gcr2);
    eim_read_32(EIM_CS0RCR1, (uint32_t *)&rcr1);
    eim_read_32(EIM_CS0RCR2, (uint32_t *)&rcr2);
    eim_read_32(EIM_CS0WCR1, (uint32_t *)&wcr1);
    eim_read_32(EIM_CS0WCR2, (uint32_t *)&wcr2);

    eim_read_32(EIM_WCR,    (uint32_t *)&wcr);
    eim_read_32(EIM_WIAR,   (uint32_t *)&wiar);
    //eim_read_32(EIM_EAR,    (uint32_t *)&ear);

    // manipulate registers as needed
    gcr1.csen               = 1;    // chip select is enabled                   |
    gcr1.swr                = 1;    // write is sync                            |
    gcr1.srd                = 1;    // read is sync                             |
    gcr1.mum                = 1;    // address and data are multiplexed         |
    gcr1.wfl                = 0;    // write latency is not fixed               |
    gcr1.rfl                = 0;    // read latency is not fixed                |
    gcr1.cre                = 0;    // CRE signal not needed                    |
    //gcr1.crep             = x;    // don't care, CRE not used                 |
    gcr1.bl                 = 4;    // burst length                             | ?
    gcr1.wc                 = 0;    // write is not continuous                  | ?
    gcr1.bcd                = 3;    // BCLK divisor is 3+1=4                    |
    gcr1.bcs                = 1;    // delay from ~CS to BCLK is 1 cycle        |
    gcr1.dsz                = 1;    // 16 bits per databeat at DATA[15:0]       |
    gcr1.sp                 = 0;    // supervisor protection is disabled        |
    gcr1.csrec              = 1;    // ~CS recovery is 1 cycle                  |
    gcr1.aus                = 1;    // address is not shifted                   |
    gcr1.gbc                = 1;    // ~CS gap is 1 cycle                       |
    gcr1.wp                 = 0;    // write protection is not enabled          |
    //gcr1.psz              = x;    // don't care, page mode is not used        |

    gcr2.adh                = 0;    // address hold duration is 1 cycle         |
    //gcr2.daps             = x;    // don't care, DTACK is not used            |
    gcr2.dae                = 0;    // DTACK is not used                        |
    //gcr2.dap              = x;    // don't care, DTACK is not used            |
    gcr2.mux16_byp_grant    = 1;    // enable grant mechanism                   | ?
    gcr2.reserved_3_2       = 0;    // must be 0                                |
    gcr2.reserved_11_10     = 0;    // must be 0                                |
    gcr2.reserved_31_13     = 0;    // must be 0                                |

    //rcr1.rcsn             = x;    // don't care in sync mode                  |
    rcr1.rcsa               = 0;    // no delay for ~CS needed                  |
    //rcr1.oen              = x;    // don't care in sync mode                  |
    rcr1.oea                = 0;    // no delay for ~OE needed                  |
    rcr1.radvn              = 0;    // no delay for ~LBA needed                 |
    rcr1.ral                = 0;    // clear ~LBA when needed                   |
    rcr1.radva              = 0;    // no delay for ~LBA needed                 |
    rcr1.rwsc               = 1;    // one wait state                           |
    rcr1.reserved_3         = 0;    // must be 0                                |
    rcr1.reserved_7         = 0;    // must be 0                                |
    rcr1.reserved_11        = 0;    // must be 0                                |
    rcr1.reserved_15        = 0;    // must be 0                                |
    rcr1.reserved_23        = 0;    // must be 0                                |
    rcr1.reserved_31_30     = 0;    // must be 0                                |

    //rcr2.rben             = x;    // don't care in sync mode                  |
    rcr2.rbe                = 0;    // BE is disabled                           |
    //rcr2.rbea             = x;    // don't care when BE is not used           |
    rcr2.rl                 = 0;    // read latency is 0                        | ?
    //rcr2.pat              = x;    // don't care when page read is not used    |
    rcr2.apr                = 0;    // page read mode is not used               |
    rcr2.reserved_7         = 0;    // must be 0                                |
    rcr2.reserved_11_10     = 0;    // must be 0                                |
    rcr2.reserved_31_16     = 0;    // must be 0                                |

    //wcr1.wcsn             = x;    // don't care in sync mode                  |
    wcr1.wcsa               = 0;    // no delay for ~CS needed                  |
    //wcr1.wen              = x;    // don't care in sync mode                  |
    wcr1.wea                = 0;    // no delay for ~WR_N needed                |
    //wcr1.wben             = x;    // don't care in sync mode                  |
    //wcr1.wbea             = x;    // don't care in sync mode                  |
    wcr1.wadvn              = 0;    // no delay for ~LBA needed                 |
    wcr1.wadva              = 0;    // no delay for ~LBA needed                 |
    wcr1.wwsc               = 1;    // no wait state in needed                  |
    wcr1.wbed               = 1;    // BE is disabled                           |
    wcr1.wal                = 0;    // clear ~LBA when needed                   |

    wcr2.wbcdd              = 0;    // write clock division is not needed       |
    wcr2.reserved_31_1      = 0;    // must be 0                                |

    wcr.bcm                 = 0;    // clock is only active during access       |
    //wcr.gbcd              = x;    // don't care when BCM=0                    |
    wcr.inten               = 0;    // interrupt is not used                    |
    //wcr.intpol            = x;    // don't care when interrupt is not used    |
    wcr.wdog_en             = 1;    // watchdog is enabled                      |
    wcr.wdog_limit          = 00;   // timeout is 128 BCLK cycles               |
    wcr.reserved_3          = 0;    // must be 0                                |
    wcr.reserved_7_6        = 0;    // must be 0                                |
    wcr.reserved_31_11      = 0;    // must be 0                                |

    wiar.ips_req            = 0;    // IPS not needed                           |
    wiar.ips_ack            = 0;    // IPS not needed                           |
    //wiar.irq              = x;    // don't touch                              |
    //wiar.errst            = x;    // don't touch                              |
    wiar.aclk_en            = 1;    // clock is enabled                         |
    wiar.reserved_31_5      = 0;    // must be 0                                |

    //ear.error_addr        = x;    // read-only                                |

    // write modified registers
    eim_write_32(EIM_CS0GCR1, (uint32_t *)&gcr1);
    eim_write_32(EIM_CS0GCR2, (uint32_t *)&gcr2);
    eim_write_32(EIM_CS0RCR1, (uint32_t *)&rcr1);
    eim_write_32(EIM_CS0RCR2, (uint32_t *)&rcr2);
    eim_write_32(EIM_CS0WCR1, (uint32_t *)&wcr1);
    eim_write_32(EIM_CS0WCR2, (uint32_t *)&wcr2);
    eim_write_32(EIM_WCR,     (uint32_t *)&wcr);
    eim_write_32(EIM_WIAR,    (uint32_t *)&wiar);
    //eim_write_32(EIM_EAR,     (uint32_t *)&ear);
}


//------------------------------------------------------------------------------
void eim_write_32(off_t offset, uint32_t *pvalue)
//------------------------------------------------------------------------------
{
    // calculate memory offset
    uint32_t *ptr = (uint32_t *)_eim_calc_offset(offset);

    // write data to memory
    memcpy(ptr, pvalue, sizeof(uint32_t));
}


//------------------------------------------------------------------------------
void eim_read_32(off_t offset, uint32_t *pvalue)
//------------------------------------------------------------------------------
{
    // calculate memory offset
    uint32_t *ptr = (uint32_t *)_eim_calc_offset(offset);

    // read data from memory
    memcpy(pvalue, ptr, sizeof(uint32_t));
}


//------------------------------------------------------------------------------
static off_t _eim_calc_offset(off_t offset)
//------------------------------------------------------------------------------
{
    // calculate starting and ending addresses of currently mapped page
    off_t offset_low    = mem_base_addr;
    off_t offset_high   = mem_base_addr + (mem_page_size - 1);

    // make sure that memory is mapped
    if (mem_map_ptr == MAP_FAILED)
        _eim_remap_mem(offset);

    // check that offset is in currently mapped page, remap new page otherwise
    if ((offset < offset_low) || (offset > offset_high))
        _eim_remap_mem(offset);

    // calculate pointer
    return (off_t)mem_map_ptr + (offset - mem_base_addr);
}


//------------------------------------------------------------------------------
static void _eim_remap_mem(off_t offset)
//------------------------------------------------------------------------------
{
    // unmap old memory page if needed
    if (mem_map_ptr != MAP_FAILED) {
        if (munmap(mem_map_ptr, mem_page_size) != 0) {
            printf("ERROR: munmap() failed.\n");
            exit(EXIT_FAILURE);
        }
    }

    // calculate starting address of new page
    while (offset % mem_page_size)
        offset--;

    // try to map new memory page
    mem_map_ptr = mmap(NULL, mem_page_size, PROT_READ | PROT_WRITE,
                       MAP_SHARED, mem_dev_fd, offset);
    if (mem_map_ptr == MAP_FAILED) {
        printf("ERROR: mmap() failed.\n");
        exit(EXIT_FAILURE);
    }

    // save last mapped page address
    mem_base_addr = offset;
}


//------------------------------------------------------------------------------
// End-of-File
//------------------------------------------------------------------------------