#include "mx25.h"
#include <libLHP_SPI.h>
#include <libopencm3/stm32/gpio.h>
void mx25_id(uint32_t spi,uint8_t * buffer){
  spi_enable(spi);
  spi_send(spi, MX25_RDID);

  while((SPI_SR(spi) & SPI_SR_TXE) == 0);
  spi_send(spi, 0);


  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  spi_read(spi); // 0 of the command

  while((SPI_SR(spi) & SPI_SR_TXE) == 0);
  spi_send(spi, 0);

  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  buffer[0] = spi_read(spi);
  while((SPI_SR(spi) & SPI_SR_TXE) == 0);
  spi_send(spi, 0);
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  buffer[1]= spi_read(spi);

  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  buffer[2]= spi_read(spi);


  while((SPI_SR(spi) & SPI_SR_TXE) == 0);
  while((SPI_SR(spi) & SPI_SR_BSY));
  spi_disable(spi);
}

int mx25_writepage(uint32_t spi,
                 uint8_t * buffer,
                 uint32_t addr,
                 uint32_t len){
  int sent=0;
  mx25_write_enable(spi);
  if(mx25_getstatus(spi) & 2){
     spi_enable(spi);
     spi_send(spi, MX25_PP);
     while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     spi_send(spi, (addr & 0xff0000)>>16    );
     while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     spi_send(spi, (addr & 0x00ff00)>>8 );
     while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     spi_send(spi, (addr & 0xff));
     while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     while(sent<len){
       spi_send(spi, buffer[sent++]);
       while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     }
     while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
     spi_read(spi);
     while((SPI_SR(spi) & SPI_SR_TXE) == 0);
     while((SPI_SR(spi) & SPI_SR_BSY));
     for (int i = 0; i < 1000; i++)	/* Wait enough to be visible. */
       __asm__("nop");
     spi_disable(spi);

  }else{
    return -1;
  }



}

int mx25_sectorerase(uint32_t spi,uint32_t addr) {
  mx25_write_enable(spi);
  if(mx25_getstatus(spi) & 2){
    spi_enable(spi);
    spi_send(spi, MX25_SE);
    while((SPI_SR(spi) & SPI_SR_TXE) == 0);
    spi_send(spi, (addr & 0xff0000)>>16    );
    while((SPI_SR(spi) & SPI_SR_TXE) == 0);
    spi_send(spi, (addr & 0x00ff00)>>8 );
    while((SPI_SR(spi) & SPI_SR_TXE) == 0);
    spi_send(spi, (addr & 0xff));

    while((SPI_SR(spi) & SPI_SR_TXE) == 0);
    while((SPI_SR(spi) & SPI_SR_BSY));


    spi_disable(spi);
    return 0;
  }else{
    return -1;
  }

}

void mx25_readat(uint32_t spi,
                 uint8_t * buffer,
                 uint32_t addr,
                 uint32_t len){
  uint32_t read  =0;
  spi_enable(spi);
  spi_send(spi, MX25_READ);
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  SPI_DR(spi); // clear the received data


  spi_send(spi, (addr & 0xff0000)>>16);
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  SPI_DR(spi);
  spi_send(spi, (addr & 0x00ff00)>>8 );
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  SPI_DR(spi);
  spi_send(spi, (addr & 0x0000ff)    );
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  SPI_DR(spi);





  spi_send(spi, 0);
  while(read<len-1){
    if(SPI_SR(spi) & SPI_SR_RXNE){
      buffer[read] = SPI_DR(spi);
      read++;
      spi_send(spi, 0);
    }
  }
  while((SPI_SR(spi) & SPI_SR_RXNE) == 0);
  buffer[read] = SPI_DR(spi);
  while((SPI_SR(spi) & SPI_SR_TXE) == 0);
  while((SPI_SR(spi) & SPI_SR_BSY));
  spi_disable(spi);
}

int mx25_getstatus(uint32_t spi){
  int ret;

   spi_enable(spi);
   spi_send(spi, MX25_RDSR);
   while((SPI_SR(spi) & SPI_SR_TXE) == 0);
   spi_send(spi, 0);

   while((SPI_SR(spi) & SPI_SR_RXNE)==0);
   SPI_DR(spi); // 0 of the command
   while((SPI_SR(spi) & SPI_SR_RXNE)==0);
   SPI_DR(spi);
   while((SPI_SR(spi) & SPI_SR_TXE) == 0);
   while((SPI_SR(spi) & SPI_SR_BSY));
   ret=SPI_DR(spi);
   spi_disable(spi);
 for (int i = 0; i < 1000; i++)	/* Wait enough to be visible. */
     __asm__("nop");
   return (ret ); // return WEL bit

}

int mx25_write_enable(uint32_t spi){
   spi_enable(spi);
   spi_send(spi, MX25_WREN);
   while((SPI_SR(spi) & SPI_SR_TXE) == 0);
   while((SPI_SR(spi) & SPI_SR_BSY));
   SPI_DR(spi);
   spi_disable(spi);
    for (int i = 0; i < 1000; i++)	/* Wait enough to be visible. */
     __asm__("nop");
   return 1;
}
