#include "w25q64.h"

static const uint8_t dummy = 0xFF;

//bit busy;

uint8_t* pTxBufferPtr;
uint16_t TxBufferCount;
uint16_t TxBufferSize;

uint8_t* pRxBufferPtr;
uint16_t RxBufferCount;
uint16_t RxBufferSize;
	
static void spi_transmit(uint8_t* pTxData, uint8_t* pRxData, uint16_t Size)
{
	uint8_t TxAllow = 1;
	uint16_t initial_TxBufferCount = Size;
	
	pTxBufferPtr =  pTxData;
	TxBufferCount = Size;
	TxBufferSize = Size;

	pRxBufferPtr =  pRxData;
	RxBufferCount = Size;
	RxBufferSize = Size;

	if(initial_TxBufferCount == 0x01)
	{
		SPDAT = *((const uint8_t*)pTxBufferPtr);
//		SPDAT = *pTxBufferPtr;
		pTxBufferPtr += sizeof(uint8_t);
		TxBufferCount--;
	}
	while((TxBufferCount > 0) || (RxBufferCount > 0))
	{
		while(!SPIF);
//		while(busy);
//		busy = 1;
		if((TxBufferCount > 0) && (TxAllow == 1))
		{
			SPIF = 1;/* 清除发送/接收完成标志位 */
			SPDAT = *((const uint8_t*)pTxBufferPtr);
			pTxBufferPtr++;
			TxBufferCount--;
			/* 转为接收模式，接收下一个数据 */
			TxAllow = 0;
		}
		
		if(RxBufferCount > 0)
		{
			SPIF = 1;
			*pRxBufferPtr = SPDAT;
			pRxBufferPtr++;
			RxBufferCount--;			
		}
	}
}

static uint8_t w25q64_write_read_byte(uint8_t* write_data)
{
	uint8_t read_data;
	spi_transmit(write_data, &read_data, 1);
	return read_data;
}

static uint8_t read_sr()
{
	uint8_t byte = 0;
	const uint8_t rs_cmd = W25X_ReadStatusReg;
	
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&rs_cmd);
	byte = w25q64_write_read_byte(&dummy);
	W25X_CS_SET = 1;
	
	return byte;
}

static void wait_idle()
{
	while((read_sr() & 0x01) == 0x01);
}

static void write_enale()
{
	const uint8_t we_cmd = W25X_WriteEnable;
	
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&we_cmd);
	W25X_CS_SET = 1;
}

void w25q64_init()
{	
	/* CLK和MOSI配置为强推挽输出 MISO配置为准双向口 */
    P1M0 = (P1M0 & ~0x40) | 0xa0; 
	P1M1 &= ~0xe0; 
	
	/* CS配置为强推挽输出 快速电平转换*/
    P0M0 |= 0x20; 
	P0M1 &= ~0x20; 
    P0SR &= ~0x20; 

	/* 使能SPI为主机模式 */
	SSIG = 0;
	SPEN = 1;
	DORD = 0;
	MSTR = 1;
	CPOL = 0;
	CPHA = 0;
	SPR1 = 0; 	/*40Mhz / 8 = 5Mhz*/
	SPR0 = 1;

	/* 清除中断标志位 */
	SPIF = 1;
	WCOL = 1;
	W25X_CS_SET = 1;
	
	/* new */
//	ESPI = 1;
//	EA = 1;
}

/* 4kb */
void w25q64_erase_sector(uint32_t addr)
{
	const uint8_t es_cmd = W25X_SectorErase;
	uint8_t H_addr, M_addr, L_addr;
	
	H_addr = (uint8_t)(addr >> 16);
	M_addr = (uint8_t)(addr >> 8);
	L_addr = (uint8_t)(addr);
	
	write_enale();
	wait_idle();
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&es_cmd);
	/* transimit 24 bit address data */
	w25q64_write_read_byte(&H_addr);
	w25q64_write_read_byte(&M_addr);
	w25q64_write_read_byte(&L_addr);
	W25X_CS_SET = 1;
	wait_idle();
}

void w25q64_read(uint32_t addr, uint8_t* buffer, uint32_t len)
{
	const uint8_t rd_cmd = W25X_ReadData;
	uint8_t H_addr, M_addr, L_addr, index;
	
	H_addr = (uint8_t)(addr >> 16);
	M_addr = (uint8_t)(addr >> 8);
	L_addr = (uint8_t)(addr);
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&rd_cmd);
	/* transimit 24 bit address data */
	w25q64_write_read_byte(&H_addr);
	w25q64_write_read_byte(&M_addr);
	w25q64_write_read_byte(&L_addr);	
	
	for(index = 0; index < len; index++)
	{
		buffer[index] = w25q64_write_read_byte(&dummy);
	}
	W25X_CS_SET = 1;
}

void w25q64_write(uint32_t addr, const uint8_t* buffer, uint32_t len)
{
	const uint8_t pp_cmd = W25X_PageProgram;
	uint8_t H_addr, M_addr, L_addr, index;
	
	H_addr = (uint8_t)(addr >> 16);
	M_addr = (uint8_t)(addr >> 8);
	L_addr = (uint8_t)(addr);
	
	write_enale();
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&pp_cmd);
	/* transimit 24 bit address data */
	w25q64_write_read_byte(&H_addr);
	w25q64_write_read_byte(&M_addr);
	w25q64_write_read_byte(&L_addr);	
	
	for(index = 0; index < len; index++)
	{
		w25q64_write_read_byte(&buffer[index]);
	}	
	W25X_CS_SET = 1;
	wait_idle();
}

void w25q64_read_unique_id(uint32_t* id_h, uint32_t* id_l)
{
	const uint8_t id_cmd = W25X_UniqueID;
	W25X_CS_SET = 0;
	w25q64_write_read_byte(&id_cmd);
	w25q64_write_read_byte(&dummy);
	w25q64_write_read_byte(&dummy);
	w25q64_write_read_byte(&dummy);
	w25q64_write_read_byte(&dummy);
	*id_h = w25q64_write_read_byte(&dummy);
	*id_h <<= 8;
	*id_h |= w25q64_write_read_byte(&dummy);
	*id_h <<= 8;
	*id_h |= w25q64_write_read_byte(&dummy);
	*id_h <<= 8;
	*id_h |= w25q64_write_read_byte(&dummy);
	
	*id_l = w25q64_write_read_byte(&dummy);
	*id_l <<= 8;
	*id_l |= w25q64_write_read_byte(&dummy);
	*id_l <<= 8;
	*id_l |= w25q64_write_read_byte(&dummy);
	*id_l <<= 8;
	*id_l |= w25q64_write_read_byte(&dummy);
	W25X_CS_SET = 1;
}

//void SPI_Isr() interrupt 9
//{
//	SPIF = 1;
//	busy = 0;
//}