#include "spi1_flash.h"

#if 1
#define TRACE(...)              printf(__VA_ARGS__)
#else
#define TRACE(...)
#endif


void spi1_cs_en(void);
void spi1_cs_dis(void);
void spi1_cs_init(void);

#define SPI1_CS_INIT()           spi1_cs_init()
#define SPI1_CS_EN()             spi1_cs_en()
#define SPI1_CS_DIS()            spi1_cs_dis()

#define SF_PROGRAM              0x02                //SPIFlash programming
#define SF_READ                 0x03                //SPIFlash Read
#define SF_READSSR              0x05                //SPIFlash Read Status Register
#define SF_WRITE_EN             0x06                //SPIFlash write enable
#define SF_ERASE                0x20                //SPIFlash erase//sector erase (4K erase)

#define SPIFLASH1_BAUD          (200000)            //200K

#define SPIFLASH_ID             0x40150000          //Determine whether FLASH is online by reading the ID, and change it to the ID corresponding to SPIFLASH

void spi1_cs_init(void)
{
    gpio_init_typedef gpio_init_structure;
    gpio_init_structure.gpio_pin = GPIO_PIN_6;          //spi1_cs
    gpio_init_structure.gpio_dir = GPIO_DIR_OUTPUT;
    gpio_init_structure.gpio_fen = GPIO_FEN_GPIO;
    gpio_init_structure.gpio_mode = GPIO_MODE_DIGITAL;
    gpio_init(GPIOE_REG, &gpio_init_structure);
    gpio_set_bits(GPIOE_REG, GPIO_PIN_6);
}

void spi1_cs_en(void)
{
    gpio_reset_bits(GPIOE_REG, GPIO_PIN_6);
}

void spi1_cs_dis(void)
{
    gpio_set_bits(GPIOE_REG, GPIO_PIN_6);
}

///SPIFlash Delay
static void spi1_delay(void)
{
    uint cnt = 20;
    while (cnt--) {
        asm("nop");
    }
}

///SPI interface obtains 1Byte data
static u8 spi1_getbyte(void)
{
    spi_dir_sel(SPI1, SPI_DIR_RX);
    spi_send_data(SPI1, 0xff);
    while (!spi_get_flag(SPI1, SPI_FLAG_PENDING));                   //Wait pending
    return SPI1BUF;
}

///SPI interface sends 1Byte data
static void spi1_sendbyte(u8 data)
{
    spi_dir_sel(SPI1, SPI_DIR_TX);
    spi_send_data(SPI1, data);
    while (!spi_get_flag(SPI1, SPI_FLAG_PENDING));                   //Wait pending
}

u32 spiflash1_id_read(void)
{
    u32 id = 0,i;
    SPI1_CS_EN();
    spi1_delay();
    spi1_sendbyte(0x9F);
    for (i = 0; i < 4; i++) {
        id |= spi1_getbyte();
        id <<= 8;
    }
    SPI1_CS_DIS();
    return id;
}
extern const u32 sys_clk_geehy[8];
void spi1_init(u8 mode,u32 baud)
{
    u32 sysclk;
    gpio_init_typedef gpio_init_structure;
    spi_init_typedef spi_init_structure;

    clock_gate1_cmd(CLOCK_GATE1_SPI1, ENABLE);

    if(mode==SPI_2WIRE_MODE)
    {
        gpio_init_structure.gpio_pin = GPIO_PIN_3;          //CLK
        gpio_init_structure.gpio_dir = GPIO_DIR_OUTPUT;
        gpio_init_structure.gpio_fen = GPIO_FEN_PER;
        gpio_init_structure.gpio_mode = GPIO_MODE_DIGITAL;
        gpio_init_structure.gpio_pupd = GPIO_PUPD_UP;
        gpio_init(GPIOA_REG, &gpio_init_structure);
        gpio_init_structure.gpio_pin = GPIO_PIN_4;          //DIO
        gpio_init_structure.gpio_dir = GPIO_DIR_INPUT;
        gpio_init(GPIOA_REG, &gpio_init_structure);
        gpio_func_mapping(SPI1_MAP_G1);
    }
    else if(mode==SPI_3WIRE_MODE)
    {
        gpio_init_structure.gpio_pin = GPIO_PIN_3 | GPIO_PIN_4;          //CLK
        gpio_init_structure.gpio_dir = GPIO_DIR_OUTPUT;
        gpio_init_structure.gpio_fen = GPIO_FEN_PER;
        gpio_init_structure.gpio_mode = GPIO_MODE_DIGITAL;
        gpio_init_structure.gpio_pupd = GPIO_PUPD_UP;
        gpio_init(GPIOA_REG, &gpio_init_structure);
        gpio_init_structure.gpio_pin = GPIO_PIN_5;                      //DIO
        gpio_init_structure.gpio_dir = GPIO_DIR_INPUT;
        gpio_init(GPIOA_REG, &gpio_init_structure);
        gpio_func_mapping(SPI1_MAP_G1);                             //改spi引脚
    }

    SPI1_CS_INIT();
    sysclk = get_cur_sysclk();
    printf("sysclk = %d MHz",sys_clk_geehy[sysclk]);
    spi_init_structure.baud = sys_clk_geehy[sysclk]*1000000/baud - 1;   //
    spi_init_structure.bus_mode = mode;
    spi_init_structure.spi_oss = 0;     //SPI sample data is at the same clock edge with output data.0:difference,1:same
    spi_init_structure.spi_sm = 0;      //Slave mode select bit.0: master mode
    spi_init_structure.spi_smps = 0;    //SPI output edge select bit.0: output data at the falling edge;
    spi_init(SPI1, &spi_init_structure);
    spi_cmd(SPI1, ENABLE);
}

///SPIFlash write enable
static void spiflash1_write_enable(void)
{
    SPI1_CS_EN();
    spi1_sendbyte(SF_WRITE_EN);
    SPI1_CS_DIS();
    spi1_delay();
}

///SPIFlash Read Status Register
static uint spiflash1_readssr(void)
{
    uint ssr;
    SPI1_CS_EN();
    spi1_sendbyte(SF_READSSR);
    ssr = spi1_getbyte();
    SPI1_CS_DIS();
    spi1_delay();
    return ssr;
}

///SPIFlash waiting for BUSY
static void spiflash1_waitbusy(void)
{
    do {
        spi1_delay();
    } while (spiflash1_readssr() & 0x01);
}

///Address to send SPIFlash
static void spiflash1_sendaddr(u32 addr)
{
    spi1_sendbyte(addr >> 16);
    spi1_sendbyte(addr >> 8);
    spi1_sendbyte(addr);
}

///SPIFlash Read
void spiflash1_read(void *buf, u32 addr, u32 len)
{
    TRACE("[r:0x%X,%d]\n",addr, len);
    u32 i;
    u8 *read_buf = (u8*)buf;
    SPI1_CS_EN();
    spi1_sendbyte(SF_READ);
    spiflash1_sendaddr(addr);
    for (i = 0; i < len; i++) {
       read_buf[i] = spi1_getbyte();
    }
    SPI1_CS_DIS();
}

///SPIFlash programming
void spiflash1_write(void *buf, u32 addr, u32 len)
{
    TRACE("[w:0x%X,%d]\n",addr, len);

    u8 *write_buf = (u8*)buf;
    spiflash1_write_enable();
    SPI1_CS_EN();
    spi1_sendbyte(SF_PROGRAM);
    spiflash1_sendaddr(addr);

    spi_dir_sel(SPI1, SPI_DIR_TX);
    spi_set_dma_addr(SPI1, (u32)write_buf);
    spi_set_dma_cnt(SPI1, len);
    while (!spi_get_flag(SPI1, SPI_FLAG_PENDING));                   //Wait pending

    SPI1_CS_DIS();
    spiflash1_waitbusy();
}

///SPIFlash erase
void spiflash1_erase(u32 addr)
{
    TRACE("spi erase: %x\n", addr);
    spiflash1_write_enable();

    SPI1_CS_EN();
    spi1_sendbyte(SF_ERASE);
    spiflash1_sendaddr(addr);
    SPI1_CS_DIS();

    spiflash1_waitbusy();
}

#define SF_ERASE_BLOCK          0xD8              //SPIFlash abrasion  //block erase   //64K
void spiflash1_erase_block(u32 addr)
{
   TRACE("spi erase: %x\n", addr);
   spiflash1_write_enable();

   SPI1_CS_EN();
   spi1_sendbyte(SF_ERASE_BLOCK);
   spiflash1_sendaddr(addr);
   SPI1_CS_DIS();

   spiflash1_waitbusy();
}

bool is_exspiflash_online(void)
{
    u32 flash_id;
    u32 i = 5;
    while(i--) {
        WDT_CLR();
        flash_id = spiflash1_id_read();
        printf("flash id = 0x%X\n",flash_id);
        if (SPIFLASH_ID == flash_id) {
            return true;
        } else {
            printf("--->!!!Read Flash ID Err\n",flash_id);
            delay_ms(1);
        }
    }
    return false;
}

void spiflash_run(void)
{
    u8 write_buf[9] = "SPI TEST";
    u8 read_buf[9];
    u32 flash_id;

    flash_id = spiflash1_id_read();
    printf("\nFlash ID = 0x%X\n", flash_id);

    if( (((flash_id>>24) &0xff)!=0) && (((flash_id>>24) &0xff)!=0xff) )
    {
        spiflash1_erase_block(0);
        spiflash1_write(write_buf, 0, 9);
        spiflash1_read(read_buf, 0, 9);
        printf("read data:%s\n", read_buf);
    }

}
