//-----------------------------------------------------------------------------
///
/// \brief  QSPI
///
///         Blah blah blub
///
/// \date   20220707
/// \author Maximilian Seesslen <mes@seesslen.net>
///
//-----------------------------------------------------------------------------


//---Includes------------------------------------------------------------------


//---Own------------------------------

#define LEPTO_LOG_DEBUG_ASSERT

#include <biwak/biwak.h>         // exception
#include <biwak/sd.hpp>
#include <biwak/handler.h>
#include <biwak/systick.h>       // usleep

#include <biwak/stm32/sd.hpp>
#include <biwak/rcc.h>
#include <biwak/exception.h>
#include <HALWrapper/stm32_rcc.h>
#include <lepto/log.h>

//---Implementation------------------------------------------------------------


CSd::CSd( const SSdConfig &_configData)
   //:eSpi(_eSpi)
   :configData(_configData)
   ,m_gpioCk(configData.eGpioPeripheralCk, configData.eGpioPinCk, gpioModeSD, configData.altFunc)
   ,m_gpioCmd(configData.eGpioPeripheralCmd, configData.eGpioPinCmd, gpioModeSDCmd, configData.altFunc)
   ,m_gpioD0(configData.eGpioPeripheralD0, configData.eGpioPinD0, gpioModeSD, configData.altFunc)
   ,m_gpioD1(configData.eGpioPeripheralD1, configData.eGpioPinD1, gpioModeSD, configData.altFunc)
   ,m_gpioD2(configData.eGpioPeripheralD2, configData.eGpioPinD2, gpioModeSD, configData.altFunc)
   ,m_gpioD3(configData.eGpioPeripheralD3, configData.eGpioPinD3, gpioModeSD, configData.altFunc)
{
   m_pHandler = new SD_HandleTypeDef;
   if( init() != 0 )
   {
      lCritical("Could not init SD");
   }
}

int CSd::init()
{
   int sta=0;
   initClock();
   
   HAL_SD_DeInit( m_pHandler );

   /* uSD device interface configuration */
   m_pHandler->Instance = SDMMC1;
   m_pHandler->Init.ClockEdge           = SDMMC_CLOCK_EDGE_RISING;
   m_pHandler->Init.ClockBypass         = SDMMC_CLOCK_BYPASS_DISABLE;
   m_pHandler->Init.ClockPowerSave      = SDMMC_CLOCK_POWER_SAVE_DISABLE;
   m_pHandler->Init.BusWide             = SDMMC_BUS_WIDE_1B; /* cant set to 4B*/
   m_pHandler->Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE;
   m_pHandler->Init.ClockDiv            = SDMMC_TRANSFER_CLK_DIV;


   /* HAL SD initialization */
   if (HAL_SD_Init( m_pHandler ) != HAL_OK)
   {
     sta = -1;
   }

   /* Configure SD Bus width */
   if ( sta == HAL_OK )
   {
     /* Enable wide operation */
     if ( HAL_SD_ConfigWideBusOperation( m_pHandler, SDMMC_BUS_WIDE_4B) != HAL_OK)
     {
       sta = -2;
     }
     else
     {
       sta = 0;
     }
   }

   m_initSta=sta;
   return(sta);
}

void CSd::initClock()
{
   RCC_PeriphCLKInitTypeDef  RCC_PeriphClkInit;

   HAL_RCCEx_GetPeriphCLKConfig(&RCC_PeriphClkInit);

   /* Configure the SDMMC1 clock source. The clock is derived from the PLLSAI1 */
   /* Hypothesis is that PLLSAI1 VCO input is 8Mhz */
   RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SDMMC1 | RCC_PERIPHCLK_SAI1;
   RCC_PeriphClkInit.PLLSAI1.PLLSAI1N = 24;
   RCC_PeriphClkInit.PLLSAI1.PLLSAI1Q = 4;
   RCC_PeriphClkInit.PLLSAI1.PLLSAI1P = 7;
   RCC_PeriphClkInit.PLLSAI1.PLLSAI1R = 2;
   RCC_PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK;
   //RCC_PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_PLLSAI1; // _HSI48; // RCC_SDMMC1CLKSOURCE_MSI;
   RCC_PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_HSI48; // RCC_SDMMC1CLKSOURCE_MSI;
   if ( HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK )
   {
      lFatal("Could not init clocks for SD");
   }

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvolatile"

   /* Enable SDMMC1 clock */
   __HAL_RCC_SDMMC1_CLK_ENABLE();

#pragma GCC diagnostic pop

}

void CSd::info()
{
   int sta=0;
   printf("Init-sta: %d\n", m_initSta);
   sta=HAL_SD_GetCardInfo  (m_pHandler, &m_pHandler->SdCard);
   printf("Status: %d\n", sta);
   printf("Card type: %lu\n", m_pHandler->SdCard.CardType);
   printf("Block size: %lu/%lu\n"
          , m_pHandler->SdCard.BlockSize, m_pHandler->SdCard.LogBlockSize);
   printf("Blocks: %lu/%lu\n"
          , m_pHandler->SdCard.BlockNbr, m_pHandler->SdCard.LogBlockNbr);

   return;
}

int CSd::read(void *data, int addr, size_t blocks) const
{
   while (HAL_SD_GetCardState(m_pHandler) != HAL_SD_CARD_TRANSFER)
   {};
   int sta=HAL_SD_ReadBlocks(m_pHandler, (uint8_t *)data, addr, blocks, 0x1000);
   lDebugAssert( sta == 0, "%d", sta );

   return( sta );
}

int CSd::write(const void *data, int addr, size_t blocks) const
{
   int cnt=0x800;
   int sta;
   
   while ( ( ( sta=HAL_SD_GetCardState(m_pHandler) ) != HAL_SD_CARD_TRANSFER) && cnt )
   {
      usleep(1000);
      cnt--;
   };
   if(!cnt)
   {
      lCritical("Sta: %d", sta);
      //biwakEventLoop();
      //exception();
      return(sta);
   }
   sta=HAL_SD_WriteBlocks(m_pHandler, (uint8_t *)data, addr, blocks, 0x1000);
   lDebugAssert( sta == 0, "%d", sta );

   return( sta );
}

int CSd::erase(int addr)
{
   while (HAL_SD_GetCardState(m_pHandler) != HAL_SD_CARD_TRANSFER)
   {};
   int sta=HAL_SD_Erase(m_pHandler, addr, addr);
   lDebugAssert( sta == 0, "%d", sta );

   return( sta );
}

int CSd::errorCode()
{
   return( HAL_SD_GetError(m_pHandler) );
}

void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
   lInfo("TxCplt");
}

void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
   lInfo("RxCplt");
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"

IRQ_Funcion_imp( SDMMC1_IRQHandler )
{
   return;
}

#pragma GCC diagnostic pop

//---fin-----------------------------------------------------------------------
