/******************************************************************************
版权所有：	 
文件名称：	dspi.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-19
功能说明：	dspi驱动程序。
其它说明：
修改记录：
*/

/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
/*------------------------------- 宏定义 --------------------------------------
*/

//DSPI内部RAM地址							

//四个DSPI模块地址  
#define DSPI0_MODULE_ADDRESS    0xFC05C000
#define DSPI1_MODULE_ADDRESS    0xFC03C000
#define DSPI2_MODULE_ADDRESS    0xEC038000
#define DSPI3_MODULE_ADDRESS    0xEC03C000

#if (CFG_DSPI_INDEX == 0)
	#define DSPI_PM_REG		MCF_PMM_PPMCR0
	#define DSPI_PM_INDEX	23
#elif (CFG_DSPI_INDEX == 1)
	#define DSPI_PM_REG		MCF_PMM_PPMCR0
	#define DSPI_PM_INDEX	15
#endif

/*------------------------------ 全局变量 -------------------------------------
*/
//总线占用状态
int gh_dspi = 0; 

// ESAM spi 句柄
int g_spi_fd = -1;

/*------------------------------ 函数声明 -------------------------------------
*/


/*------------------------------ 外部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	dspi_init
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-19
函数说明：	qspi初始化。
参数说明：	无
返回值：  	成功返回0.
修改记录：	
*/
#define _MCF_DSPI_MCR(x) 	(*(vuint32*)(0xEC038000 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_TCR(x)  	(*(vuint32*)(0xEC038008 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_RSER(x) 	(*(vuint32*)(0xEC038030 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_CTAR1(x)	(*(vuint32*)(0xEC038010 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_SR(x)		(*(vuint32*)(0xEC03802C + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_PUSHR(x) 	(*(vuint32*)(0xEC038034 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))
#define _MCF_DSPI_POPR(x) 	(*(vuint32*)(0xEC038038 + ((x<2)? (0x10024000 + ((x)*0x4000) - ((x)*0x24000)) : ((x-2)*0x4000))))

int dspi_init(void)
{
	#define ESAM_DEV_NAME "/dev/spidev5.0"
	#define MAX_SPEED_HZ 5000000
    int ret = 0;
    __u32 mode = SPI_MODE_3;			// 模式3
    __u8 bits_per_word = 8; 			// 每字8位
    __u32 max_speed_hz = MAX_SPEED_HZ; 	// 5MHz
	
	
    // 1. 打开SPI设备文件
    g_spi_fd = open(ESAM_DEV_NAME, O_RDWR);
    if (g_spi_fd < 0) {
        printf("%s open %s failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        return -1;
    }

    // 2. 设置SPI模式
    ret = ioctl(g_spi_fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1) {
		printf("%s set %s mode failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        close(g_spi_fd);
		g_spi_fd = -1;
        return -2;
    }

    ret = ioctl(g_spi_fd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1) {
		printf("%s read %s mode failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        close(g_spi_fd);
		g_spi_fd = -1;
        return -3;
    }

	if(SPI_MODE_3 != mode){
    	printf("%s mode set err! set 0x%x,read 0x%x!!!\n", ESAM_DEV_NAME, SPI_MODE_3, mode);
        close(g_spi_fd);
		g_spi_fd = -1;
		return -4;
	}

    // 3. 设置每字的位数
    ret = ioctl(g_spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word);
    if (ret == -1) {
		printf("%s set %s bits_per_word failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        close(g_spi_fd);
		g_spi_fd = -1;
        return -5;
    }
    ret = ioctl(g_spi_fd, SPI_IOC_RD_BITS_PER_WORD, &bits_per_word);
    if (ret == -1) {
		printf("%s read %s bits_per_word failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        close(g_spi_fd);
		g_spi_fd = -1;
        return -6;
    }

	if(8 != bits_per_word){
    	printf("%s set bits_per_word err! set: 8, read: %d!!!\n", ESAM_DEV_NAME, bits_per_word);
		close(g_spi_fd);
		g_spi_fd = -1;
		return -7;
	}

    // 4. 设置最大时钟频率 (Hz)
    ret = ioctl(g_spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &max_speed_hz);
    if (ret == -1) {
		printf("%s set %s max_speed_hz failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
		close(g_spi_fd);
		g_spi_fd = -1;
        return -8;
    }
    ret = ioctl(g_spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &max_speed_hz);
    if (ret == -1) {
		printf("%s read %s max_speed_hz failed !!!\n", __FUNCTION__, ESAM_DEV_NAME);
        close(g_spi_fd);
		g_spi_fd = -1;
        return -9;
    }

	if(MAX_SPEED_HZ != max_speed_hz){
		printf("%s set max_speed_hz err! set: %d, read: %d!!!\n", ESAM_DEV_NAME, MAX_SPEED_HZ, max_speed_hz);
		close(g_spi_fd);
		g_spi_fd = -1;
        return -10;
	}
#if 0
// SPI总线初始化前，先打开ESAM模块的电源，解决esam模块有时工作不正常的问题。
	// 上电并延时1ms
	GPIO_ESAM_POWER_INIT();
	ustimer_delay(USTIMER_MS*10);
	
	//模块打开
	DSPI_PM_REG = DSPI_PM_INDEX;
	
	//使用GPIO控制CS
	GPIO_DSPI_CS_ESAM_INIT();

	// 设置为DSPI管脚
#if (CFG_DSPI_INDEX == 0)
	MCF_PAD_PAR_DSPIOWH = 0xFC;   
	MCF_PAD_PAR_DSPIOWL = 0;
	MCF_PAD_SRCR_DSPIOW = 0x01; //  调整管脚驱动能力,2太强，过冲太厉害，改为1

#elif (CFG_DSPI_INDEX == 1)
	MCF_PAD_PAR_SDHCH |= 0x01;   
	MCF_PAD_PAR_SDHCL |= 0X05;
	MCF_PAD_SRCR_SDHC = 0x1; //  调整管脚驱动能力,2太强，过冲太厉害，改为1

#endif
	
	//初始化TCR寄存器
	_MCF_DSPI_TCR(CFG_DSPI_INDEX) = 0;
	
	//初始化MCR寄存器  	
	_MCF_DSPI_MCR(CFG_DSPI_INDEX) = 0;
	
	//设置模式寄存器
	_MCF_DSPI_MCR(CFG_DSPI_INDEX) = MCF_DSPI_MCR_MSTR 
				  //| MCF_DSPI_MCR_SMPL_PT(x)
				  //| MCF_DSPI_MCR_SMPL_PT_0CLK
				  //| MCF_DSPI_MCR_SMPL_PT_1CLK
				  //| MCF_DSPI_MCR_SMPL_PT_2CLK
			  	  | MCF_DSPI_MCR_CLR_RXF
				  | MCF_DSPI_MCR_CLR_TXF
				  | MCF_DSPI_MCR_DIS_RXF // 禁止接收FIFO，打开后，接收数据前请FIFO会导致出错
				  | MCF_DSPI_MCR_DIS_TXF
				  //| MCF_DSPI_MCR_MDIS
				  //| MCF_DSPI_MCR_PCSIS0;
				 // | MCF_DSPI_MCR_ROOE
				  //| MCF_DSPI_MCR_MTFE
				  //| MCF_DSPI_MCR_FRZ
				  //| MCF_DSPI_MCR_DCONF(x)
				  //| MCF_DSPI_MCR_CONT_SCKE;
				//  | MCF_DSPI_MCR_HALT
				  | MCF_DSPI_MCR_PCSIS1;
				  //| MCF_DSPI_MCR_PCSIS2;
				  // MCF_DSPI_MCR_PCSIS3;
	
	//初始化DMA/中断请求选择和使能寄存器 
	_MCF_DSPI_RSER(CFG_DSPI_INDEX) = 0;//MCF_DSPI_RSER_EOQF_RE
					//| MCF_DSPI_RSER_TCF_RE;
					//| MCF_DSPI_RSER_TFFF_DIRS 
					//| MCF_DSPI_RSER_RFDF_DIRS				 
					//| MCF_DSPI_RSER_TFFF_RE
					//| MCF_DSPI_RSER_RFDF_RE;

	// 频率:125M/(4*7)=4.46M.字节与字节之间延时:125M/(1*128)=1.024us.
	_MCF_DSPI_CTAR1(CFG_DSPI_INDEX) = MCF_DSPI_CTAR_BR(1)
					 |MCF_DSPI_CTAR_PBR_7CLK	
//						 |MCF_DSPI_CTAR_PCSSCK(1)
//						 |MCF_DSPI_CTAR_CSSCK(1)
						 |MCF_DSPI_CTAR_PASC_1CLK
						 |MCF_DSPI_CTAR_ASC(3)
						 |MCF_DSPI_CTAR_PDT_1CLK
						 |MCF_DSPI_CTAR_DT(6)
					 //|MCF_DSPI_CTAR_LSBFE
					 	 |MCF_DSPI_CTAR_CPOL // 空闲时，时钟为高
					 //|MCF_DSPI_CTAR_DBR
					   |MCF_DSPI_CTAR_CPHA  // 时钟相位
					 |MCF_DSPI_CTAR_FMSZ(7);		
			
	_MCF_DSPI_SR(CFG_DSPI_INDEX) = MCF_DSPI_SR_EOQF
				 |MCF_DSPI_SR_TCF;

#endif	
	return 0;
}


/******************************************************************************
函数名称：	dspi_open
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-19
函数说明：	打开一个DSPI设备，所有的DSPI设备互斥，一次只能打开一个，在这个函数
			主要是根据设备的属性，设置DSPI的波特率、CLK极性、CLK相位，并使相应
			的片选有效。
			
			注意:此函数在资源竞争问题上,有一定的系统要求,如果要求不满足,用户应
			自己处理资源竞争问题.这个要求为:系统中每个任务(一个中断服务程序也算
			一个任务)都具有唯一优先级.目前UT800S的系统满足这个条件.
参数说明：	
	dspi_id: 指定需要打开DSPI设备的标识符(以DSPI_ID_开头)。
返回值：  
	>=0 : 打开设备的句柄
	<0	: 打开失败。
修改记录：	
*/
int dspi_open(int dspi_id)
{
	//如果QSPI总线空闲，占有它。
	if(gh_dspi == 0)
		gh_dspi = dspi_id;	
	else		
		return -1;
	
	//解决资源竞争问题，如果两个值不相等，说明在竞争中失败，退出。
	if(gh_dspi != dspi_id)
	{
		
		return -2;
	}
	
	switch(dspi_id)
	{
	case DSPI_ID_ESAM:	 
		// 片选有效  
		//GPIO_DSPI_CS_ESAM_LOW();
		//ustimer_delay(4*USTIMER_US);
		
		break;

 	default:
		gh_dspi = 0;
		return -3;
	}
	
	return dspi_id;
}


/******************************************************************************
函数名称：	dspi_close
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	关闭一个DSPI设备。
参数说明：	
	h_dspi：关闭设备的句柄。
返回值：  	 成功返回0.
修改记录：	
*/
int dspi_close(int h_dspi)
{
	if(h_dspi != gh_dspi)
	{
		
		return -1;
	}
	
	switch(h_dspi)
	{
	case DSPI_ID_ESAM:
    	//片选无效
		//GPIO_DSPI_CS_ESAM_HIGH();
		
		break;

	default:
		return -2;
	}
	
	gh_dspi = 0;
	
	return 0;
}



/******************************************************************************
函数名称：	dspi_read
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	从DSPI总线上读取数据。
参数说明：	
	h_dspi(in)：DSPI设备句柄。.
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度。
返回值：  	  成功返回0.
修改记录：	
*/
int dspi_read(int h_dspi,unsigned char *buffer, uint32_t length)
{	
	if(h_dspi != gh_dspi || buffer == 0)
	{	
		return -1;
	}
#if 0	
	// 清空接收buffer
	_MCF_DSPI_MCR(CFG_DSPI_INDEX) |=  MCF_DSPI_MCR_CLR_RXF;
	
	while(length)
	{
		//执行命令
		_MCF_DSPI_PUSHR(CFG_DSPI_INDEX) = MCF_DSPI_PUSHR_CTAS(1);		// 开始执行	
		while (!(_MCF_DSPI_SR(CFG_DSPI_INDEX) & MCF_DSPI_SR_TCF)) ; 	// 等待结束
		_MCF_DSPI_SR(CFG_DSPI_INDEX) = MCF_DSPI_SR_TCF;					// 清除标志
		
		*buffer++ = (unsigned char)_MCF_DSPI_POPR(CFG_DSPI_INDEX);

		//长度减放在这个地方，不放在while条件中，否则由于编译器的bug，
		//会导致程序流程执行不正确。
		length--;
	}
#endif	
	return 0;
}
/******************************************************************************
函数名称：	dspi_write
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	写数据到QSPI总线。
参数说明：	
	h_dspi(in)：DSPI设备句柄。.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度。
返回值：  	成功返回0.
修改记录：	
*/
int dspi_write(int h_dspi,unsigned char *buffer, uint32_t length)
{
#if 0
	if(h_dspi != gh_dspi || buffer == 0)
	{	
		return -1;
	}
	
	while(length)
	{	
		_MCF_DSPI_PUSHR(CFG_DSPI_INDEX) = MCF_DSPI_PUSHR_CTAS(1) | (unsigned char)(*buffer++);
		while (!(_MCF_DSPI_SR(CFG_DSPI_INDEX) & MCF_DSPI_SR_TCF)) ; 	// 等待结束
		_MCF_DSPI_SR(CFG_DSPI_INDEX) = MCF_DSPI_SR_TCF;
	    
		//长度减放在这个地方，不放在while条件中，否则由于编译器的bug，
		//会导致程序流程执行不正确。		
	    length--;
	}
#endif
	return 0;
}


/******************************************************************************
函数名称：	dspi_dummy_byte
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	往SPI总线上发空的CLOCK。
参数说明：	
	h_dspi(in)：DSPI设备句柄。.
	length(in):	需要发空CLOCK的长度，以BYTE为单位。
返回值：  	成功返回0.
修改记录：	
*/
int dspi_dummy_byte(int h_dspi,uint32_t length)
{
	if(h_dspi != gh_dspi)
	{
		
		return -1;
	}
#if 0	
	while(length)
	{
		//执行命令
		_MCF_DSPI_PUSHR(CFG_DSPI_INDEX) = MCF_DSPI_PUSHR_CTAS(1);		// 开始执行	
		while (!(_MCF_DSPI_SR(CFG_DSPI_INDEX) & MCF_DSPI_SR_TCF)) ; 	// 等待结束
		_MCF_DSPI_SR(CFG_DSPI_INDEX) = MCF_DSPI_SR_TCF;					// 清除标志
		
		
		//长度减放在这个地方，不放在while条件中，否则由于编译器的bug，
		//会导致程序流程执行不正确。
	    length--;
	}
#endif	
	return 0;
}

void dspi_esam_cs(int cs)
{
#if 0
	if(cs)
	{
		GPIO_DSPI_CS_ESAM_HIGH();
		ustimer_delay(11*USTIMER_US);
	}
	else
	{
		GPIO_DSPI_CS_ESAM_LOW();
		ustimer_delay(51*USTIMER_US);
	}
#endif
}

u16 dspi_esam_send(const u8 *pdat, u16 len)
{
	if(g_spi_fd < 0) {
		rt_printf("%s 设备未打开!\n",__func__);
		return 0;
	}

	return write(g_spi_fd,pdat,len);

}


int dspi_esam_recv(u8 *pdat, u16 len)
{
	u16 ret = 0;
	if(g_spi_fd < 0) {
		rt_printf("%s 设备未打开!\n",__func__);
		return -1;
	}
	ret = read(g_spi_fd,pdat,len);

	if(ret != len) {
		rt_printf("%s read data error!\n", __func__);
		return -1;
	} else {
		return 0;
	}

}



/*------------------------------ 内部函数 -------------------------------------
*/


/*------------------------------ 测试函数 -------------------------------------
*/


/*------------------------------ 文件结束 -------------------------------------
*/
  
