aboutsummaryrefslogblamecommitdiff
path: root/sw/novena-eim.c
blob: 1effff147720458b5385e0ba3b32db29baf4009b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                                 
                    


























                                                                                 
                                                                                













































































                                                                                                                                                    





















                                                                                       

                                                             





















                                                                                       










                                                                                 
                                                        





















                                                                  
                                                         



















                                                                                 





                                                     
         


                                                    


























































































                                                                                                                                         








                                                             



                                                                                 
                                                  


                                                                                 
                                                              

                                        
                                                 


                                                                                 
                                                 


                                                                                 
                                                              

                                         
                                                 




















































                                                                                                         
//------------------------------------------------------------------------------
// novena-eim.c
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// 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 long		mem_page_size	= 0;
static int		mem_dev_fd		= -1;
static void *	mem_map_ptr		= MAP_FAILED;
static off_t	mem_base_addr	= 0;


//------------------------------------------------------------------------------
int eim_setup()
//------------------------------------------------------------------------------
{
		// register cleanup function
	int ok = atexit(_eim_cleanup);
	if (ok != 0)
	{	printf("ERROR: atexit() failed.\n");
		return -1;
	}
	
		// determine memory page size to use in mmap()
	mem_page_size = sysconf(_SC_PAGESIZE);
	if (mem_page_size < 1)
	{	printf("ERROR: sysconf(_SC_PAGESIZE) == %ld\n", mem_page_size);
		return -1;
	}
	
		// 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;
}


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

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


//------------------------------------------------------------------------------
void _eim_setup_iomuxc()
//------------------------------------------------------------------------------
{
		// 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);
}


//------------------------------------------------------------------------------
void _eim_setup_ccm()
//------------------------------------------------------------------------------
{
		// 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);
}


//------------------------------------------------------------------------------
void _eim_setup_eim()
//------------------------------------------------------------------------------
{
		// 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));	
}


//------------------------------------------------------------------------------
off_t _eim_calc_offset(off_t offset)
//------------------------------------------------------------------------------
{
		// make sure that memory is mapped
	if (mem_map_ptr == MAP_FAILED) _eim_remap_mem(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);
	
		// 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);
}


//------------------------------------------------------------------------------
void _eim_remap_mem(off_t offset)
//------------------------------------------------------------------------------
{
		// unmap old memory page if needed
	if (mem_map_ptr != MAP_FAILED)
	{	int ok = munmap(mem_map_ptr, mem_page_size);
		if (ok != 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
//------------------------------------------------------------------------------