#include "main.h"

#define 	W25Q128_FLASH_SIZE 			0x01000000
#define 	W25Q128_ADDR_BIT_NUM 		24

#define 	NOR_FLASH_SIZE 					W25Q128_FLASH_SIZE
#define 	NOR_FLASH_ADDR_BIT_NUM 	W25Q128_ADDR_BIT_NUM

#define 	FLASH_WP_H()			LL_GPIO_SetOutputPin(GPIOG,LL_GPIO_PIN_13)
#define 	FLASH_WP_L()			LL_GPIO_ResetOutputPin(GPIOG,LL_GPIO_PIN_13)	
#define 	FLASH_HOLD_H()		LL_GPIO_SetOutputPin(GPIOE,LL_GPIO_PIN_2)	
#define 	FLASH_HOLD_L()		LL_GPIO_ResetOutputPin(GPIOE,LL_GPIO_PIN_2)

#define 	xSPI_CS_HI() 			LL_GPIO_SetOutputPin(GPIOA,LL_GPIO_PIN_15)
#define 	xSPI_CS_LO() 			LL_GPIO_ResetOutputPin(GPIOA,LL_GPIO_PIN_15)

#define 	xSPI_CLK_HI() 		LL_GPIO_SetOutputPin(GPIOG,LL_GPIO_PIN_11)
#define 	xSPI_CLK_LO() 		LL_GPIO_ResetOutputPin(GPIOG,LL_GPIO_PIN_11)

#define 	xSPI_SET_MISO_INPUT() 	GPIOG->MODER&=~0x000C0000
#define 	xSPI_SET_MISO_OUTPUT() 	GPIOG->MODER|=0x00040000

#define 	xSPI_SET_MOSI_INPUT() 	GPIOD->MODER&=~0x0000C000
#define 	xSPI_SET_MOSI_OUTPUT() 	GPIOD->MODER|=0x00004000

#define 	xSPI_GET_MISO() 		LL_GPIO_IsInputPinSet(GPIOG,LL_GPIO_PIN_9)
#define 	xSPI_MOSI_HI() 			GPIOD->ODR|=(1<<7)
#define 	xSPI_MOSI_LO() 			GPIOD->ODR&=~(1<<7)

#define 	xSPI_SET_DATA_INPUT() 	(GPIOG->MODER&=~0x0C0C0000,GPIOE->MODER&=~0x00000030,GPIOD->MODER&=~0x0000C000)
#define 	xSPI_SET_DATA_OUTPUT() 	(GPIOG->MODER|=0x04040000,GPIOE->MODER|=0x00000010,GPIOD->MODER|=0x00004000)

#define 	xSPI_DIN()				(((GPIOE->IDR&0x0004)<<1)|((GPIOG->IDR&0x2000)>>11)|((GPIOG->IDR&0x0200)>>8)|((GPIOD->IDR&0x0080)>>7))

#define 	xSPI_D0_HI() 			LL_GPIO_SetOutputPin(GPIOD,LL_GPIO_PIN_7)
#define 	xSPI_D0_LO() 			LL_GPIO_ResetOutputPin(GPIOD,LL_GPIO_PIN_7)
#define 	xSPI_D1_HI() 			LL_GPIO_SetOutputPin(GPIOG,LL_GPIO_PIN_9)
#define 	xSPI_D1_LO() 			LL_GPIO_ResetOutputPin(GPIOG,LL_GPIO_PIN_9)
#define 	xSPI_D2_HI() 			LL_GPIO_SetOutputPin(GPIOG,LL_GPIO_PIN_13)
#define 	xSPI_D2_LO() 			LL_GPIO_ResetOutputPin(GPIOG,LL_GPIO_PIN_13)
#define 	xSPI_D3_HI() 			LL_GPIO_SetOutputPin(GPIOE,LL_GPIO_PIN_2)
#define 	xSPI_D3_LO() 			LL_GPIO_ResetOutputPin(GPIOE,LL_GPIO_PIN_2)

static void flash_reset(void);
static void flash_write_enable(void);
static void flash_write_disable(void);
static void flash_quad_enable(void);
static void flash_quad_disable(void);
static uint8_t flash_read_sr1(void);
static uint8_t flash_read_sr2(void);

static void wait(void)
{
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
	__nop();
}

void w25q_IO_setup(void)
{
	static uint8_t val = 0;
	uint32_t i = 0;
	/*
	LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);
	LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOD);
	
	//PD6 PC10 12 13 14 15 
	GPIOC->MODER   &=~0xFF300000; 
	GPIOD->MODER   &=~0x00003000;
	GPIOC->MODER   |= 0x00100000;
	GPIOD->MODER   |= 0x00001000; //PD6,PC10 GPIO Output Mode
	
	GPIOC->OTYPER  &=~0x0000F400; //Output push-pull
	GPIOD->OTYPER  &=~0x00000040;
	GPIOC->OSPEEDR |= 0xFF300000; //Very High speed
	GPIOD->OSPEEDR |= 0x00003000;
	*/
	xSPI_CS_HI();
	xSPI_CLK_LO();
	
	xSPI_SET_DATA_OUTPUT();
	FLASH_WP_H();
	FLASH_HOLD_H();
	for(i=0;i<10000;i++);
	
	flash_write_enable();
	xSPI_SET_DATA_INPUT();
	
	flash_reset();
	
	xSPI_SET_DATA_OUTPUT();
	FLASH_WP_H();
	FLASH_HOLD_H();
	for(i=0;i<10000;i++);
	flash_write_enable();
	xSPI_SET_DATA_INPUT();
	
	do
	{
		val = flash_read_sr1();
	}while(val&0x01);
	
	val = flash_read_sr2();
	if(!(val&0x02))
	{
		do
		{
			flash_quad_enable();
			val = flash_read_sr2();
		}while(!(val&0x02));
	}
}

uint8_t QSPI_D0123_read(void)
{
	return(xSPI_DIN());
}

void QSPI_D0123_write(uint8_t x)
{
	if(x&0x01)
	{
		xSPI_D0_HI();
	}
	else
	{
		xSPI_D0_LO();
	}
	
	if(x&0x02)
	{
		xSPI_D1_HI();
	}
	else
	{
		xSPI_D1_LO();
	}
	
	if(x&0x04)
	{
		xSPI_D2_HI();
	}
	else
	{
		xSPI_D2_LO();
	}
	
	if(x&0x08)
	{
		xSPI_D3_HI();
	}
	else
	{
		xSPI_D3_LO();
	}
}

void xspi_write_byte(uint8_t data)
{
	uint32_t i;
	xSPI_SET_MOSI_OUTPUT();
	for(i=0;i<8;i++)
	{
		if(data & 0x80)xSPI_MOSI_HI();
		else xSPI_MOSI_LO();
		
		xSPI_CLK_HI();
		xSPI_CLK_LO();
		data<<=1;
	}
	xSPI_MOSI_LO();
	xSPI_SET_MOSI_INPUT();
}

uint8_t xspi_read_byte(void)
{
	uint32_t i;
	uint8_t data = 0;
	for(i=0;i<8;i++)
	{
		wait();
		data<<=1;
		if(xSPI_GET_MISO())data |= 0x01;
		xSPI_CLK_HI();
		wait();
		xSPI_CLK_LO();
	}
	return data;
}

void xspi_quad_write_byte(uint8_t data)
{
	xSPI_SET_DATA_OUTPUT();
	QSPI_D0123_write(data>>4);
	xSPI_CLK_HI();
	wait();
	xSPI_CLK_LO();
	QSPI_D0123_write(data);
	xSPI_CLK_HI();
	wait();
	xSPI_CLK_LO();
	xSPI_SET_DATA_INPUT();
	wait();
}

uint8_t xspi_quad_read_byte(void)
{
	uint8_t data;
	
	data = QSPI_D0123_read();
	xSPI_CLK_HI();
	xSPI_CLK_LO();
	data<<=4;
	data |= QSPI_D0123_read();
	xSPI_CLK_HI();
	xSPI_CLK_LO();
	return data;
}

static void flash_write_enable(void)
{
	uint32_t i = 0;
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x06);
	xSPI_CS_HI();
	for(i=0;i<10000;i++);
}

static void flash_write_disable(void)
{
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x04);
	xSPI_CS_HI();
}

static uint8_t flash_read_sr1(void)
{
	uint8_t sr1;
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x05);

	sr1 = xspi_read_byte();
	wait();
	xSPI_CS_HI();
	return sr1;
}

static uint8_t flash_read_sr2(void)
{
	uint8_t sr2;
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x35);

	sr2 = xspi_read_byte();
	wait();
	xSPI_CS_HI();
	return sr2;
}

void flash_write_sr(uint8_t sr1,uint8_t sr2)
{
	flash_write_enable();
	
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x01);	
	xspi_write_byte(sr1);	
	xspi_write_byte(sr2);	
	wait();
	xSPI_CS_HI();
	
	flash_write_disable();
}

static void flash_quad_enable(void)
{
	flash_write_enable();
	flash_write_sr(0x00,0x02);
	flash_write_disable();	
}

static void flash_quad_disable(void)
{
	flash_write_enable();
	flash_write_sr(0x00,0x00);
	flash_write_disable();
}

uint8_t get_flash_write_status(void)
{
	return(flash_read_sr1() & 0x01);
}

unsigned int flash_quad_read_data(unsigned int addr, unsigned char * p_data,unsigned int len)//p_data >=256byte
{
	unsigned int i = 0;
	
	if((addr + len) > NOR_FLASH_SIZE)return 1;
	
	xSPI_CLK_LO();
	xSPI_CS_LO();
	xspi_write_byte(0x6b);
	xspi_write_byte((addr>>16)&0xff);
	xspi_write_byte((addr>>8)&0xff);
	xspi_write_byte(addr&0xff);
	xspi_write_byte(0);

	for(i=0;i<len;i++)
	{	
		p_data[i] = xspi_quad_read_byte();
	}
	xSPI_CS_HI();
	
	return 0;
}

uint8_t* flash_quad_read_page(uint32_t addr)
{
	uint32_t i = 0;
	unsigned int addr_page = addr & 0xFFFFFF00;
	static uint8_t p_data[288];
	
	xSPI_CLK_LO();
	xSPI_CS_LO();
	xspi_write_byte(0x6b);
	xspi_write_byte((addr_page>>16)&0xff);
	xspi_write_byte((addr_page>>8)&0xff);
	xspi_write_byte(addr_page&0xff);
	xspi_write_byte(0);

	for(i=0;i<256;i++)
	{	
		p_data[i] = xspi_quad_read_byte();
	}
	xSPI_CS_HI();
	return &p_data[0];
}

void flash_quad_program_page(uint32_t addr,uint8_t* p_data)
{
	uint32_t i = 0;
	unsigned int addr_page = addr & 0xFFFFFF00;
	//flash_quad_enable();
	//do{
		//i = flash_read_sr1();
	//}while(i&0x01);

	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x32);
	xspi_write_byte((addr_page>>16)&0xff);
	xspi_write_byte((addr_page>>8)&0xff);
	xspi_write_byte(addr_page&0xff);
	
	for(i=0;i<256;i++)
	{
		xspi_quad_write_byte(*p_data);
		p_data++;
	}
	xSPI_CS_HI();
	flash_write_disable();
	
	do{
		LL_IWDG_ReloadCounter(IWDG1);
		i = flash_read_sr1();
	}while(i&0x01);
}

void flash_quad_program_page_no_wait(uint32_t addr,uint8_t* p_data)
{
	uint32_t i = 0;
	unsigned int addr_page = addr & 0xFFFFFF00;

	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x32);
	xspi_write_byte((addr_page>>16)&0xff);
	xspi_write_byte((addr_page>>8)&0xff);
	xspi_write_byte(addr_page&0xff);
	
	for(i=0;i<256;i++)
	{
		xspi_quad_write_byte(*p_data);
		p_data++;
	}
	xSPI_CS_HI();
	flash_write_disable();
}

unsigned int flash_quad_program_no_wait(uint32_t addr,uint8_t* p_data,unsigned int len)
{
	uint32_t i = 0;
	
	if(((addr&0xFF) + len) > 0x100)return 1;

	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x32);
	xspi_write_byte((addr>>16)&0xff);
	xspi_write_byte((addr>>8)&0xff);
	xspi_write_byte(addr&0xff);
	
	for(i=0;i<len;i++)
	{
		xspi_quad_write_byte(*p_data);
		p_data++;
	}
	xSPI_CS_HI();
	flash_write_disable();
	return 0;
}

void flash_sector_erase(uint32_t addr)
{
	unsigned char val = 1;
	unsigned int i = 0;
	unsigned int addr_sector = addr & 0xFFFFF000;
	
	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x20);
	xspi_write_byte((addr_sector&0x00FF0000)>>16);
	xspi_write_byte((addr_sector&0x0000FF00)>>8);
	xspi_write_byte(addr_sector&0x000000FF);
	xSPI_CS_HI();
	flash_write_disable();
	
	while(val)
	{
		LL_IWDG_ReloadCounter(IWDG1);
		val = (flash_read_sr1() & 0x01);
	}
	
	for(i=0;i<10000;i++)__nop();
}

void flash_sector_erase_no_wait(uint32_t addr)
{
	unsigned char val = 1;
	unsigned int i = 0;
	unsigned int addr_sector = addr & 0xFFFFF000;
	
	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x20);
	xspi_write_byte((addr_sector&0x00FF0000)>>16);
	xspi_write_byte((addr_sector&0x0000FF00)>>8);
	xspi_write_byte(addr_sector&0x000000FF);
	xSPI_CS_HI();
	flash_write_disable();
	wait();
}

void flash_full_chip_erase(void)		// erase chip
{
	uint8_t val = 1;
	
	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x60);
	xSPI_CS_HI();
	while(val)
	{
		LL_IWDG_ReloadCounter(IWDG1);
		val = (flash_read_sr1() & 0x01);
	}
}

void flash_full_chip_erase_no_wait(void)		// erase chip
{
	uint8_t val = 1;
	
	flash_write_enable();
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x60);
	xSPI_CS_HI();
	wait();
}

static void flash_reset(void)
{
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x66);
	xSPI_CS_HI();
	
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x99);
	xSPI_CS_HI();
	
	HAL_Delay(5);
}

void flash_read_id(uint8_t *p_ddata)		
{
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	wait();
	xspi_write_byte(0x90);			// cmd
	xspi_write_byte(0);		// h
	xspi_write_byte(0);		// m
	xspi_write_byte(0);		// l

	*p_ddata = xspi_read_byte();
	p_ddata++;
	*p_ddata = xspi_read_byte();	
	//wait();
	xSPI_CS_HI();
}

void flash_quad_read_id(uint8_t *p_ddata)		
{
	xSPI_CLK_LO();
	xSPI_CS_LO();
	wait();
	xspi_write_byte(0x94);			// cmd
	xspi_quad_write_byte(0);		// h
	xspi_quad_write_byte(0);		// m
	xspi_quad_write_byte(0);		// l
	xspi_quad_write_byte(0xff);		// h
	xspi_quad_write_byte(0);		// m
	xspi_quad_write_byte(0);

	*p_ddata = xspi_quad_read_byte();
	p_ddata++;
	*p_ddata = xspi_quad_read_byte();
	wait();
	xSPI_CS_HI();
}
