/*-----------------------------------------------------------------------------
 *      RL-ARM - FlashFS
 *-----------------------------------------------------------------------------
 *      Name:    SDIO_STM32F4xx.c
 *      Purpose: SD/SDIO MMC Interface Driver for ST STM32F4xx
 *      Rev.:    V4.60
 *-----------------------------------------------------------------------------
 *      This code is part of the RealView Run-Time Library.
 *      Copyright (c) 2004-2012 KEIL - An ARM Company. All rights reserved.
 *----------------------------------------------------------------------------*/

#include <File_Config.h>
#include "stm32f4xx.h"                  /* STM32F4xx Definitions              */
#include "SDIO_STM32F4xx.h"

#include "bsp_task.h"
#include "kit_time.h"

/*-----------------------------------------------------------------------------
  Memory Card FAT Driver instance definition
   mci0_drv: First SD/MMC drive [M0:]
   mci1_drv: Second SD/MMC drive [M1:]
 *----------------------------------------------------------------------------*/

#define __DRV_ID   mci0_drv
#define __SDIOCLK  48000000
#define __CPUCLK  168000000

/* MCI Driver Interface functions */
static BOOL Init (void);
static BOOL UnInit (void);
static void Delay (U32 us);
static BOOL BusMode (U32 mode);
static BOOL BusWidth (U32 width);
static BOOL BusSpeed (U32 kbaud);
static BOOL Command (U8 cmd, U32 arg, U32 resp_type, U32 *rp);
static BOOL ReadBlock (U32 bl, U8 *buf, U32 cnt);
static BOOL WriteBlock (U32 bl, U8 *buf, U32 cnt);
static BOOL SetDma (U32 mode, U8 *buf, U32 cnt);
static U32  CheckMedia (void);        /* Optional function for SD card check  */

/* MCI Device Driver Control Block */
MCI_DRV __DRV_ID = {
  Init,
  UnInit,
  Delay,
  BusMode,
  BusWidth,
  BusSpeed,
  Command,
  ReadBlock,
  WriteBlock,
  SetDma,
  CheckMedia                            /* Can be NULL if not existing        */
};


/* Wait time in for loop cycles */
#define DMA_TOUT  10000000

/*--------------------------- Init -------------------------------------------*/

static BOOL Init (void) {
  /* Initialize SDIO interface. */

  /* Enable GPIOH (card detect), GPIOD (command), GPIOC (data) clock */
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN |
                  RCC_AHB1ENR_GPIODEN |
                  RCC_AHB1ENR_GPIOCEN;

  RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; 
  /* Enable IO compensation cell */
//  SYSCFG->CMPCR |= SYSCFG_CMPCR_CMP_PD;
//  while (!(SYSCFG->CMPCR & SYSCFG_CMPCR_READY)); 

  /* PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */ 
  GPIOC->MODER   &= ~0x03FF0000;
  GPIOC->MODER   |=  0x02AA0000;        /* Pins to alternate function         */
  GPIOC->OTYPER  &= ~0x00001F00;        /* Configure as push-pull pins        */
  GPIOC->PUPDR   &= ~0x03FF0000;
  GPIOC->PUPDR   |=  0x01550000;        /* Pull-ups on pins                   */
  GPIOC->OSPEEDR |=  0x03FF0000;        /* Pins output speed to 100MHz        */

  GPIOC->AFR[1]  &= ~0x000FFFFF;
  GPIOC->AFR[1]  |=  0x000CCCCC;        /* Pins assigned to AF12 (SDIO)       */

  /* Configure PD.02 CMD line */
  GPIOD->MODER   &= ~0x0000030;
  GPIOD->MODER   |=  0x0000020;         /* Pins to alternate function         */
  GPIOD->OTYPER  &= ~0x0000004;         /* Configure as push-pull pin         */
  GPIOD->PUPDR   &= ~0x0000030;
  GPIOD->PUPDR   |=  0x0000010;         /* Pull-up on pin                     */       
  GPIOD->OSPEEDR |=  0x0000030;         /* Pins output speed to 100MHz        */

  GPIOD->AFR[0]  &= ~0x00000F00;
  GPIOD->AFR[0]  |=  0x00000C00;        /* Pin assigned to AF12 (SDIO)        */

//  /* Configure PH.15 Card Detect input */
//  GPIOH->MODER  &= ~0xC0000000;
//  GPIOH->PUPDR  &= ~0xC0000000;         /* Pin in Input mode                  */

  /* Enable SDIO clock */
  RCC->APB2ENR  |=  RCC_APB2ENR_SDIOEN;

  /* Reset/Dereset SDIO */
  RCC->APB2RSTR |=  RCC_APB2RSTR_SDIORST;
  RCC->APB2RSTR &= ~RCC_APB2RSTR_SDIORST;

  /* Power-on, the card is clocked */
  SDIO->POWER  = SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0;

    /* Enable DMA2 clock */
  RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

  /* Success, SDIO initialized. */
  return (__TRUE);
}


/*--------------------------- UnInit -----------------------------------------*/

static BOOL UnInit (void) {

  /* Reset PC.8, PC.9, PC.10, PC.11, PC.12 pins */
  GPIOC->MODER &= ~0x03FF0000;
  /* Reset PD.2 */
  GPIOD->MODER &= ~(3 <<  4);
  /* Reset PH.13 */ 
  GPIOH->MODER &= ~(3 << 26);

  /* Disable SDIO module clock */
  RCC->APB2ENR &= ~RCC_APB2ENR_SDIOEN;

  return (__TRUE);
}


/*--------------------------- Delay ------------------------------------------*/

static void Delay (U32 us) {
  /* Approximate delay in micro seconds. */
  U32 i;

  for (i = WAIT_CNT(__CPUCLK, us); i; i--);
}


/*--------------------------- BusMode ----------------------------------------*/

static BOOL BusMode (U32 mode) {
  /* Set SDIO Bus mode to Open Drain or Push Pull. */

  switch (mode) {
    case BUS_OPEN_DRAIN:
      GPIOD->OTYPER  |=  0x00000010;    /* Configure as open-drain            */
      return (__TRUE);

    case BUS_PUSH_PULL:
      GPIOD->OTYPER  &= ~0x00000010;    /* Configure as push-pull pin         */
      return (__TRUE);
    
    default:
      return (__FALSE);
  }
}


/*--------------------------- BusWidth ---------------------------------------*/

static BOOL BusWidth (U32 width) {
  /* Set SDIO Bus width. */

  switch (width) {
    case 1:
      SDIO->CLKCR &= ~SDIO_CLKCR_WIDBUS;
      return (__TRUE);

    case 4:
      SDIO->CLKCR |=  SDIO_CLKCR_WIDBUS_0;
      return (__TRUE);
    
    default:
      return (__FALSE);
  }
}


/*--------------------------- BusSpeed ---------------------------------------*/

static BOOL BusSpeed (U32 kbaud) {
  /* Set SDIO clock speed to desired value. */
  U32 div;

  /* baud = SDIOCLK / (div + 2) */
  div = (__SDIOCLK/1000 + kbaud - 1) / kbaud;
  if (div < 2)    div  = 0;
  else            div -= 2;
  if (div > 0xFF) div  = 0xFF;
  SDIO->CLKCR = (SDIO->CLKCR & ~0xFF) | SDIO_CLKCR_PWRSAV | SDIO_CLKCR_CLKEN | div;
  return (__TRUE);
}


/*--------------------------- Command ----------------------------------------*/
static BOOL Command (U8 cmd, U32 arg, U32 resp_type, U32 *rp) {
  /* Send a Command to Flash card and get a Response. */
  U32 cmdval,stat, cnt = 0;
  
  cmd   &= SDIO_CMD_CMDINDEX;
  cmdval = SDIO_CMD_CPSMEN | cmd;
  switch (resp_type) {
    case RESP_SHORT:
      cmdval |= SDIO_CMD_WAITRESP_0;
      break;
    case RESP_LONG:
      cmdval |= SDIO_CMD_WAITRESP_1 | SDIO_CMD_WAITRESP_0;
      break;
  }
  /* Send the command. */
  SDIO->ARG = arg;
  SDIO->CMD = cmdval;

  if (resp_type == RESP_NONE) {
    /* Wait until command finished. */
    while ((SDIO->STA & SDIO_STA_CMDACT) && (cnt++ < DMA_TOUT))
    {
        bsp_task_feed_wdog(1);
    }
    
    if(cnt >= DMA_TOUT)
    {
        return (__FALSE);
    }
	
    stat = SDIO->STA;
    SDIO->ICR = 0x00C007FF; 
    if (stat & SDIO_STA_CMDSENT) {
      return (__TRUE);
    }
    return (__FALSE);    
  }

  for (cnt = 0; cnt < DMA_TOUT; cnt++) {
    stat = SDIO->STA;
    if (stat & SDIO_STA_CTIMEOUT) {
      SDIO->ICR = stat & SDIO_STA_CLEAR_MASK;
      return (__FALSE);
    }
    if (stat & SDIO_STA_CCRCFAIL) {
      SDIO->ICR = stat & SDIO_STA_CLEAR_MASK;
      if ((cmd == SEND_OP_COND)      ||
          (cmd == SEND_APP_OP_COND)  ||
          (cmd == STOP_TRANS)) {
        SDIO->CMD = 0;
        break;
      }
      return (__FALSE);
    }
    if (stat & SDIO_STA_CMDREND) {
      SDIO->ICR = stat & SDIO_STA_CLEAR_MASK;
      break;
    }
    bsp_task_feed_wdog(1);
  }
  if ((SDIO->RESPCMD & 0x3F) != cmd) {
    if ((SDIO->RESPCMD & 0x3F) != 0x3F) {
      return (__FALSE);
    }
  }
  /* Read MCI response registers */
  rp[0] = SDIO->RESP1;
  if (resp_type == RESP_LONG) {
    rp[1] = SDIO->RESP2;
    rp[2] = SDIO->RESP3;
    rp[3] = SDIO->RESP4;
  }

  return (__TRUE);
}


/*--------------------------- ReadBlock --------------------------------------*/

static BOOL ReadBlock (U32 bl, U8 *buf, U32 cnt) {
  /* Read one or more 512 byte blocks from Flash Card. */
  U32 i;

  SDIO->DLEN    = cnt * 512;
  SDIO->DTIMER  = cnt * DATA_RD_TOUT_VALUE;

  SDIO->DCTRL   = SDIO_DCTRL_DBLOCKSIZE_3 | SDIO_DCTRL_DBLOCKSIZE_0 |
                  SDIO_DCTRL_DMAEN        | SDIO_DCTRL_DTDIR        |
                  SDIO_DCTRL_DTEN         ;

  for (i = DMA_TOUT; i; i--) {    
    if (DMA2->LISR & DMA_LISR_TEIF3) {
      break;
    }
    
    if (DMA2->LISR & DMA_LISR_TCIF3) {
      if ((SDIO->STA & (SDIO_STA_DBCKEND|SDIO_STA_DATAEND)) == (SDIO_STA_DBCKEND|SDIO_STA_DATAEND)) {
        /* Data transfer finished. */
        return (__TRUE);
      }
    }
    bsp_task_feed_wdog(1);
  }
  /* DMA Transfer timeout. */
  return (__FALSE);
}


/*--------------------------- WriteBlock -------------------------------------*/

static BOOL WriteBlock (U32 bl, U8 *buf, U32 cnt) {
  /* Write a cnt number of 512 byte blocks to Flash Card. */
  U32 i;

  SDIO->DLEN    = cnt * 512;
  SDIO->DTIMER  = cnt * DATA_WR_TOUT_VALUE;
  SDIO->DCTRL   = SDIO_DCTRL_DBLOCKSIZE_3 | SDIO_DCTRL_DBLOCKSIZE_0 |
                  SDIO_DCTRL_DMAEN        | SDIO_DCTRL_DTEN         ;

  for (i = DMA_TOUT; i; i--) {
    if (DMA2->LISR & DMA_LISR_TEIF3) {
      break;
    }
    
    if (DMA2->LISR & DMA_LISR_TCIF3) {
      if ((SDIO->STA & (SDIO_STA_DBCKEND|SDIO_STA_DATAEND)) == (SDIO_STA_DBCKEND|SDIO_STA_DATAEND)) { 
        /* Data transfer finished. */
        return (__TRUE);
      }
    }
    bsp_task_feed_wdog(1);
  }
  /* DMA Transfer timeout. */
  return (__FALSE);
}


/*--------------------------- DmaStart ---------------------------------------*/
static BOOL SetDma (U32 mode, U8 *buf, U32 cnt) {
    U32 count = 0;
  /* Configure DMA Channel 4, Stream 3 for read or write */
  DMA2->LIFCR = 0x0F4 << 20;                   /* Clear all IRQs              */

  DMA2_Stream3->CR  &= ~DMA_SxCR_EN;           /* Disable stream 3            */
  while ((DMA2_Stream3->CR & DMA_SxCR_EN) && (count++ < DMA_TOUT))
  {
    bsp_task_feed_wdog(1);
  }      /* Wait until stream disabled  */
    
  if(count >= DMA_TOUT)
  {
     return __FALSE;
  }
  count = 0;
  
  DMA2_Stream3->FCR |=  DMA_SxFCR_DMDIS |      /* Disable direct mode         */
                        DMA_SxFCR_FTH   ;      /* FIFO threshold full         */

  DMA2_Stream3->PAR  = (U32)&(SDIO->FIFO);
  DMA2_Stream3->M0AR = (U32)buf;

  if (mode == DMA_READ) {
    /* Transfer from SDIO-FIFO to memory. */
    DMA2_Stream3->CR = (4 << 25) |             /* Use channel 4               */
                       (1 << 23) |             /* Memory burst 4 beats        */
                       (1 << 21) |             /* Peripheral burst 4 beats    */
                       (3 << 16) |             /* Priority level high         */
                       (2 << 13) |             /* Memory size 32-bits         */
                       (2 << 11) |             /* Peripheral size 32-bits     */
                       (1 << 10) |             /* Memory addr increment       */
                       (1 <<  5) ;             /* Peripheral flow control     */
  }
  else {
    /* Transfer from memory to SDIO-FIFO. */
    DMA2_Stream3->CR = (4 << 25) |             /* Use channel 4               */
                       (1 << 23) |             /* Memory burst 4 beats        */
                       (1 << 21) |             /* Peripheral burst 4 beats    */
                       (3 << 16) |             /* Priority level very high    */
                       (2 << 13) |             /* Memory size 32-bits         */
                       (2 << 11) |             /* Peripheral size 32-bits     */
                       (1 << 10) |             /* Memory addr increment       */
                       (1 <<  6) |             /* Memory-to-peripheral        */
                       (1 <<  5) ;             /* Peripheral flow control     */
  }

  /* Enable DMA channels, little endian */
  DMA2_Stream3->CR  |= DMA_SxCR_EN;            /* Enable stream 3             */
  return __TRUE;
}


/*--------------------------- CheckMedia -------------------------------------*/

static U32 CheckMedia (void) {
  /* Read CardDetect and WriteProtect SD card socket pins. */
  U32 stat = 0;

//#ifdef __XYNERGY
//  if (!(GPIOG->IDR & (1 << 8))) {
//#else
//  if (!(GPIOH->IDR & (1 << 15))) {
//#endif
//    /* Card is inserted (CD=0). */
//    stat |= M_INSERTED;
//  }

 #if 0 
  if (/* Write protect switch is active */) {
    stat |= M_PROTECTED;
  }
 #endif
    //BCE1C NO Card Detect
    stat |= M_INSERTED;
  return (stat);
}

/*-----------------------------------------------------------------------------
 * end of file
 *----------------------------------------------------------------------------*/
