#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <arpa/inet.h>

#define	SCLK66MHz	0

#define	DEVNAME		"/dev/mem"
#define BLOCKSIZE	0x00001000

#define IMX6Q_ECSPI1_BASEADDR	0x02008000
#define IMX6Q_ECSPI2_BASEADDR	0x0200c000
#define IMX6Q_ECSPI3_BASEADDR	0x02010000
#define IMX6Q_ECSPI4_BASEADDR	0x02018000

#undef  WAND_SPI
#define HARK_SPI

/* 実際に使用するSPIポート */
#define IMX6Q_SPI_BASEADDR      IMX6Q_ECSPI1_BASEADDR


#define	IMX6Q_CCM_BASEADDR	0x020c4000

#define	IMX6Q_EIM_BASEADDR	0x021b8000

#define	IMX6Q_IOMUXC_BASEADDR	0x020e0000


#define	writel(val,addr)	*((ulong *)(addr))=(ulong)(val)
#define	writew(val,addr)	*((ushort *)(addr))=(ushort)(val)
#define	writeb(val,addr)	*((u_char *)(addr))=(u_char)(val)

#define	readl(addr)		*((ulong *)(addr))
#define	readw(addr)		*((ushort *)(addr))
#define	readb(addr)		*((u_char *)(addr))

#if 0
/* 不要なコード */
typedef struct
{
	char		riff[4];	// = "RIFF"
	unsigned long	total_size;	// 全体サイズ
	char		fmt[8];		// "WAVEfmt "
	unsigned long	fmt_size;	// fmt チャンクサイズ
	unsigned short	format;		// フォーマットの種類
	unsigned short	channel;	// チャンネル
	unsigned long	rate;		// サンプリングレート
	unsigned long	avgbyte;	// rate * block
	unsigned short	block;		// channels * bit / 8
	unsigned short	bit;		// ビット数
	char		data[4];	// = "data"
	unsigned long	data_size;	// データサイズ
} WAVEFORMAT;
#endif

struct spi_regs {
	ulong	rxdata;		/* 0x00 */
	ulong	txdata;		/* 0x04 */
	ulong	conreg;		/* 0x08 */
	ulong	configreg;	/* 0x0c */
	ulong	intreg;		/* 0x10 */
	ulong	dmareg;		/* 0x14 */
	ulong	statreg;	/* 0x18 */
	ulong	periodreg;	/* 0x1c */
	ulong	testreg;	/* 0x20 */
	ulong	rev[7];
	ulong	msgdatareg;	/* 0x40 */
};

struct ccm_regs {
	ulong	ccr;		/* 0x00 */
	ulong	ccdr;		/* 0x04 */
	ulong	csr;		/* 0x08 */
	ulong	ccsr;		/* 0x0c */
	ulong	cacrr;		/* 0x10 */
	ulong	cbcdr;		/* 0x14 */
	ulong	cbcmr;		/* 0x18 */
	ulong	cbcmr1;		/* 0x1c */
	ulong	cbcmr2;		/* 0x20 */
	ulong	cscdr1;		/* 0x24 */
	ulong	cs1cdr;		/* 0x28 */
	ulong	cs2cdr;		/* 0x2c */
	ulong	cdcdr;		/* 0x30 */
	ulong	chsccdr;	/* 0x34 */
	ulong	cscdr2;		/* 0x38 */
	ulong	cscdr3;		/* 0x3c */
	ulong	rev1[2];
	ulong	cdhipr;		/* 0x48 */
	ulong	rev2[2];
	ulong	clpcr;		/* 0x54 */
	ulong	cisr;		/* 0x58 */
	ulong	cimr;		/* 0x5c */
	ulong	ccosr;		/* 0x60 */
	ulong	cgpr;		/* 0x64 */
	ulong	ccgr0;		/* 0x68 */
	ulong	ccgr1;		/* 0x6c */
	ulong	ccgr2;		/* 0x70 */
	ulong	ccgr3;		/* 0x74 */
	ulong	ccgr4;		/* 0x78 */
	ulong	ccgr5;		/* 0x7c */
	ulong	ccgr6;		/* 0x80 */
	ulong	rev3;
	ulong	cmeor;		/* 0x88 */
};

struct eim_regs {
	ulong	cs0gcr1;	/* 0x00 */
	ulong	cs0gcr2;	/* 0x04 */
	ulong	cs0rcr1;	/* 0x08 */
	ulong	cs0rcr2;	/* 0x0c */
	ulong	cs0wcr1;	/* 0x10 */
	ulong	cs0wcr2;	/* 0x14 */
	ulong	cs1gcr1;	/* 0x18 */
	ulong	cs1gcr2;	/* 0x1c */
	ulong	cs1rcr1;	/* 0x20 */
	ulong	cs1rcr2;	/* 0x24 */
	ulong	cs1wcr1;	/* 0x28 */
	ulong	cs1wcr2;	/* 0x2c */
	ulong	cs2gcr1;	/* 0x30 */
	ulong	cs2gcr2;	/* 0x34 */
	ulong	cs2rcr1;	/* 0x38 */
	ulong	cs2rcr2;	/* 0x3c */
	ulong	cs2wcr1;	/* 0x40 */
	ulong	cs2wcr2;	/* 0x44 */
	ulong	cs3gcr1;	/* 0x48 */
	ulong	cs3gcr2;	/* 0x4c */
	ulong	cs3rcr1;	/* 0x50 */
	ulong	cs3rcr2;	/* 0x54 */
	ulong	cs3wcr1;	/* 0x58 */
	ulong	cs3wcr2;	/* 0x5c */
	ulong	rev[12];
	ulong	wcr;		/* 0x90 */
	ulong	wiar;		/* 0x94 */
	ulong	ear;		/* 0x98 */
};

void sigcatch(int);
static int ctrlc = 0;

/* IMX6Q Wandboard SPI Test
 *
 * Syntax:
 *	fpga {bit filename}
 */
int main(int argc, char *argv[])
{
	int	rc = 0, fd = 0, i;
	ulong	addr, from, num, tmp, rdat, writeval;
	volatile u_char *iomap;
	volatile struct spi_regs *spi_base = NULL;
	volatile struct ccm_regs *ccm_base = NULL;
	volatile struct eim_regs *eim_base = NULL;
	volatile ulong *iomuxc_base = NULL;
	FILE	*fp = NULL;
	char	cfgdata[256];
	int	filesize = 0;

	if( argc < 2 ){
		printf("[usage] : fpga {bit filename}\n");
		rc = -1;
		goto FIN;
	}

	/* Open file */
	if ((fp = fopen(argv[1],"rb")) == NULL ) {
		printf("file open error!\n");
		rc = -2;
		goto FIN;
	}

	/* Set Signal Handler */
	if( SIG_ERR == signal( SIGINT, sigcatch )){
		printf("failed to set signal handler.\n");
		goto FIN;
	}

	/* Open Physical Memory Device */
	fd = open( DEVNAME, O_RDWR | O_SYNC );
	if( fd <= 0 ){
		perror( DEVNAME );
		goto FIN;
	}

	/* Do mmap with fd */
	addr = ( IMX6Q_SPI_BASEADDR );
	tmp = ( BLOCKSIZE - 1 ) & addr;
	from = addr - tmp;
	num = BLOCKSIZE;
	iomap = mmap( 0, num, PROT_READ | PROT_WRITE, MAP_SHARED, fd, from );
	if( iomap < 0 ){
		perror( DEVNAME );
		goto FIN;
	}
	spi_base = ( volatile struct spi_regs * )iomap;

	/* Do mmap with fd */
	addr = ( IMX6Q_CCM_BASEADDR );
	tmp = ( BLOCKSIZE - 1 ) & addr;
	from = addr - tmp;
	num = BLOCKSIZE;
	iomap = mmap( 0, num, PROT_READ | PROT_WRITE, MAP_SHARED, fd, from );
	if( iomap < 0 ){
		perror( DEVNAME );
		goto FIN;
	}
	ccm_base = ( volatile struct ccm_regs * )iomap;

	/* Do mmap with fd */
	addr = ( IMX6Q_EIM_BASEADDR );
	tmp = ( BLOCKSIZE - 1 ) & addr;
	from = addr - tmp;
	num = BLOCKSIZE;
	iomap = mmap( 0, num, PROT_READ | PROT_WRITE, MAP_SHARED, fd, from );
	if( iomap < 0 ){
		perror( DEVNAME );
		goto FIN;
	}
	eim_base = ( volatile struct eim_regs * )iomap;

	/* Do mmap with fd */
	addr = ( IMX6Q_IOMUXC_BASEADDR );
	tmp = ( BLOCKSIZE - 1 ) & addr;
	from = addr - tmp;
	num = BLOCKSIZE;
	iomap = mmap( 0, num, PROT_READ | PROT_WRITE, MAP_SHARED, fd, from );
	if( iomap < 0 ){
		perror( DEVNAME );
		goto FIN;
	}
	iomuxc_base = ( volatile ulong * )iomap;

	// Output BCLK 33MHz
	writel( 0x00ffffff, &ccm_base->ccgr6 );
	writel( 0x00000027, &eim_base->wcr );

	// STEP-1 : Clear the EN bit in ESCPI_CONREG to reset the block.
	rdat = readl( &spi_base->conreg );
	writel( rdat & ~0x00000001, &spi_base->conreg );

	// STEP-2 : Enable the clocks for ECSPI.
	rdat = readl( &ccm_base->ccgr1 );
	writel( rdat | 0x00000003, &ccm_base->ccgr1 );

	// STEP-3 : Set the EN bit in ESCPI_CONREG to put ECSPI out of reset.
	rdat = readl( &spi_base->conreg );
	writel( rdat | 0x00000001, &spi_base->conreg );

#ifdef HARK_SPI
	writel( 0x00000001, iomuxc_base + ( 0x098 / sizeof(ulong)));	/* EIM_D18 -> ECSPI_MOSI */
	writel( 0x00000001, iomuxc_base + ( 0x094 / sizeof(ulong)));	/* EIM_D17 -> ECSPI_MISO */
	writel( 0x00000001, iomuxc_base + ( 0x090 / sizeof(ulong)));	/* EIM_D16 -> ECSPI_SCLK */
	writel( 0x00000001, iomuxc_base + ( 0x09c / sizeof(ulong)));	/* EIM_D19 -> ECSPI_SS1  */
	writel( 0x00000000, iomuxc_base + ( 0x7f4 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_CLK_IN_SELECT_INPUT -> EIM_D16_ALT1 */
	writel( 0x00000000, iomuxc_base + ( 0x7f8 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_MISO_SELECT_INPUT -> EIM_D17_ALT1 */
	writel( 0x00000000, iomuxc_base + ( 0x7fc / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_MOSI_SELECT_INPUT -> EIM_D18_ALT1 */
	writel( 0x00000000, iomuxc_base + ( 0x804 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_SS1_SELECT_INPUT -> EIM_D19_ALT1 */
#else
	// STEP-4 : Configure corresponding IOMUX for ECSPI external signals.
//	writel( 0x00000001, iomuxc_base + ( 0x098 / sizeof(ulong)));	/* EIM_D18 -> ECSPI_MOSI */
//	writel( 0x00000001, iomuxc_base + ( 0x094 / sizeof(ulong)));	/* EIM_D17 -> ECSPI_MISO */
//	writel( 0x00000001, iomuxc_base + ( 0x090 / sizeof(ulong)));	/* EIM_D16 -> ECSPI_SCLK */
//	writel( 0x00000000, iomuxc_base + ( 0x208 / sizeof(ulong)));	/* KEY_COL2 -> ECSPI_SS1 */
	writel( 0x00000002, iomuxc_base + ( 0x1c4 / sizeof(ulong)));	/* DISP0_DAT21 -> ECSPI1_MOSI */
	writel( 0x00000002, iomuxc_base + ( 0x1c8 / sizeof(ulong)));	/* DISP0_DAT22 -> ECSPI1_MISO */
	writel( 0x00000002, iomuxc_base + ( 0x1c0 / sizeof(ulong)));	/* DISP0_DAT20 -> ECSPI1_SCLK */
	writel( 0x00000002, iomuxc_base + ( 0x1cc / sizeof(ulong)));	/* DISP0_DAT23 -> ECSPI1_SS0 */
	writel( 0x00000001, iomuxc_base + ( 0x7f4 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_CLK_IN_SELECT_INPUT -> DIPS0_DATA20_ALT2 */
	writel( 0x00000001, iomuxc_base + ( 0x7f8 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_MISO_SELECT_INPUT -> DIPS0_DATA22_ALT2 */
	writel( 0x00000001, iomuxc_base + ( 0x7fc / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_MOSI_SELECT_INPUT -> DIPS0_DATA21_ALT2 */
	writel( 0x00000001, iomuxc_base + ( 0x800 / sizeof(ulong)));	/* IOMUXC_ECSPI1_CSPI_SS0_SELECT_INPUT -> DIPS0_DATA23_ALT2 */
#endif
	// STEP-5 : Configure registers of ECSPI properly according to the specifications of the external SPI device.
	rdat = readl( &spi_base->conreg );
	rdat = ( rdat & ~0xfff00000 ) | 0x7ff00000;	/* BURST_LENGTH = 2048bit (256byte) */
#ifdef HARK_SPI
	rdat = ( rdat & ~0x000c0000 ) | 0x00040000;	/* CHANNEL_SELECT = SS1 */
#else
	rdat = ( rdat & ~0x000c0000 ) | 0x00000000;	/* CHANNEL_SELECT = SS0 */
#endif
	rdat = ( rdat & ~0x00030000 ) | 0x00000000;	/* DRCTL = SPI_RDY Don't Care */
//	rdat = ( rdat & ~0x0000f000 ) | 0x00001000;	/* PRE_DIVIDER = Divide by 2 */
	rdat = ( rdat & ~0x0000f000 ) | 0x00002000;	/* PRE_DIVIDER = Divide by 3 */
//	rdat = ( rdat & ~0x0000f000 ) | 0x00003000;	/* PRE_DIVIDER = Divide by 4 */
	rdat = ( rdat & ~0x00000f00 ) | 0x00000000;	/* POST_DIVIDER = Divide by 1 */
	rdat = ( rdat & ~0x000000f0 ) | 0x000000f0;	/* CHANNEL_MODE = All master mode */
//	rdat = ( rdat & ~0x00000008 ) | 0x00000008;	/* SMC = Immediatly starts */
	rdat = ( rdat & ~0x00000008 ) | 0x00000000;	/* SMC = SPI Exchange Bit (XCH) controls when a SPI burst can start */
	rdat = ( rdat & ~0x00000004 ) | 0x00000000;	/* XCH = Idle */
	rdat = ( rdat & ~0x00000002 ) | 0x00000000;	/* HT = Disable HT mode */
	rdat = ( rdat & ~0x00000001 ) | 0x00000001;	/* EN = Enable the block */
	writel( rdat, &spi_base->conreg );

	rdat = readl( &spi_base->configreg );
	rdat = ( rdat & ~0x00f00000 ) | 0x00000000;	/* SCLK_CTL = All stay low */
	rdat = ( rdat & ~0x000f0000 ) | 0x00000000;	/* DATA_CTL = All stay high */
	rdat = ( rdat & ~0x0000f000 ) | 0x00000000;	/* SS_POL = All active low */
	rdat = ( rdat & ~0x00000f00 ) | 0x00000f00;	/* SS_CTL = All negate SS between SPI bursts */
	rdat = ( rdat & ~0x000000f0 ) | 0x00000000;	/* SCLK_POL = All active high polarity */
	rdat = ( rdat & ~0x0000000f ) | 0x00000000;	/* SCLK_PHA = All phase 0 operation */
	writel( rdat, &spi_base->configreg );

	/* Config FPGA */
	while( !ctrlc ){
		num = fread( cfgdata, 1, sizeof(cfgdata), fp );
		if( num < 0 ) {
			printf("file read error!\n");
			goto FIN;
		}
		if( num == 0 ) break;
		filesize += num;
		for( i = 0 ; i < (sizeof(cfgdata)  / sizeof(ulong)) ; i++ ){
			writeval = ( cfgdata[i*4] << 24 ) |  ( cfgdata[i*4+1] << 16 ) | ( cfgdata[i*4+2] << 8 ) | ( cfgdata[i*4+3] << 0 );
			writel( writeval, &spi_base->txdata );
		}
		rdat = readl( &spi_base->conreg );
		rdat |= 0x00000004;
		writel( rdat, &spi_base->conreg );
		while( !ctrlc ){
//			rdat = readl( &spi_base->statreg );
			rdat = readl( &spi_base->conreg );
//			if( rdat & 0x00000080 ) break;
			if(( rdat & 0x00000004 ) == 0) break;
		}
//		writel( 0x00000080, &spi_base->statreg );
	}

	printf("configdata : %d [byte]\n", filesize);
FIN:
	/* Close mmap */
	num = BLOCKSIZE;
	if( spi_base ) munmap( (void *)spi_base, num );
	if( ccm_base ) munmap( (void *)ccm_base, num );
	if( eim_base ) munmap( (void *)eim_base, num );
	if( iomuxc_base ) munmap( (void *)iomuxc_base, num );

	/* Close device */
	if( fd ) close( fd );
	if( fp ) fclose( fp );

	return (rc);
}

void sigcatch( int sig )
{
	ctrlc++;
}

